---
module: analytics.agents
title: Agents
tab: Claude
order: 40
audience: dev
---

> **Internal · AI agent quick-ref.** Confirmed against source 2026-07-01. Act on confirmed facts only.

## Ownership
- **Tab served by:** `hiveops-analytics` (port 8089), `AgentInsightController` + `AgentInsightService`.
- **Data owned by:** `hiveops-devices` (port 8096) — source of truth for ATM/agent fields.
- **DB behind the tab:** `hiveiq_devices` only. `hiveiq_analytics` is **not** used by this tab.

## Endpoints (METHOD path)
| Method | Path | Service | Auth |
|--------|------|---------|------|
| GET | `/api/analytics/agents` | hiveops-analytics | authenticated JWT, **no role** |
| GET | `/api/atms/paginated?size=500&page=0` | hiveops-devices | authenticated JWT, **no role**; caller JWT forwarded by analytics |

## Response DTO (`AgentInsightResponse`)
- `versionDistribution: Map<version,count>`
- `latestVersion: String` — highest `x.y.z` present in fleet, else `"unknown"`
- `connectedCount` / `disconnectedCount` / `neverConnectedCount: int`
- `outdatedAtms: [{ atmId, agentVersion, lastHeartbeat, connectionStatus }]`

## Tables
- `atms`, `atm_properties` (in `hiveiq_devices`) — `atm_properties` holds `agentVersion`, `lastHeartbeat`.

## Kafka topics
- **None** produced or consumed by this tab. (Analytics-wide topics `hiveops.incidents.*`, `journal.file-parsed`, `analytics.snapshot-ready` are unrelated to Agents. Devices emits `hiveops.device.events` but this tab reads devices via REST, not that topic.)

## Connection status rule (computed in devices, not stored)
- `lastHeartbeat == null` → `NEVER_CONNECTED`
- heartbeat ≤ **15 min** → `CONNECTED`
- else → `DISCONNECTED`

## Wiring gotcha
- Analytics calls devices through a bean named `incidentWebClient`, base URL `services.incident.url` / `INCIDENT_SERVICE_URL` (default `http://hiveops-incident-backend:8080`). `/api/atms/paginated` is a **devices** endpoint (moved out of incident ~2026-06-18). That env var must resolve to the backend serving `/api/atms/paginated`, or the tab goes empty.

## Gotchas
- Empty tab = upstream failure. `fetchAtmsPaginated` **catches all exceptions → returns `[]`**; logs `AgentInsight: failed to fetch ATMs paginated` (Loki, service `hiveiq-analytics`).
- Only **first 500 ATMs** (`size=500,page=0`) are aggregated. Larger fleets under-count.
- `latestVersion` = max semver **present in fleet**, NOT the CDN release. Regex is strict `\d+\.\d+\.\d+`; suffixed/prefixed versions (`v4.4.3`, `4.4.3-patch`) → `"unknown"` → every ATM flagged outdated.
- `lastHeartbeat` serialized from `LocalDateTime` → ISO string; frontend renders relative ("2h ago").

## Do NOT
- Do NOT tell users the Agents tab pushes/schedules updates — it is **read-only**.
- Do NOT look for an analytics DB table for this tab — there isn't one; it's a live read-through.
- Do NOT claim "latest" reflects the newest released agent — it's fleet-relative.
- Do NOT expect a Kafka topic for this feature.
- Do NOT trust totals on fleets > 500 devices.
- Do NOT require an admin role to view — no `@PreAuthorize` on either endpoint.
- Do NOT hit incident for this data — devices owns `/api/atms/paginated`.
