---
module: analytics.incidents
title: Incident Trends
tab: Architect
order: 30
audience: dev
---

> How the **Incident Trends** tab is built. Grounded in `hiveops-analytics` + `hiveops-incident` source, 2026-07-01. Cross-link [platform.data-architecture], [platform.kafka], [platform.service-ownership].

## Services involved

| Service | Role for this feature |
|---------|-----------------------|
| `hiveops-analytics` (port 8089) | Owns the tab endpoint; aggregates/shapes the 4 charts in-memory. No DB write for this feature. |
| `hiveops-incident` (port 8080) | Owns the data (`journal_events`) and applies institution scoping. |
| Frontend (`hiveops-analytics/frontend`) | Svelte SPA renders `IncidentTrendsTab.svelte` from one JSON payload. |

Ownership boundary (see [platform.service-ownership]): analytics **computes**, incident **owns the journal-event data**. Analytics holds no incident/journal tables of its own for this tab.

## Data flow (synchronous, per request)

```
IncidentTrendsTab.svelte
  └─ analyticsAPI.getIncidentTrends()
       └─ GET {apiBase}/incident-trends          → IncidentTrendsController (analytics)
            └─ IncidentTrendsService.buildIncidentTrends(token)
                 └─ incidentWebClient.get()
                      GET /api/journal-events/activity?hoursBack=24   → JournalEventController (incident)
                           └─ JournalEventService.getActivityEvents(24, InstitutionContext.resolveScope())
                                └─ JournalEventRepository.findActivityEvents / findActivityEventsByInstitutionIn
                                     └─ SELECT from journal_events (joined to atms)
```

- **Auth propagation:** the caller's `Authorization: Bearer <jwt>` is extracted in the controller and re-sent on the WebClient call. No service account; downstream scoping keys off that token.
- **`incidentWebClient`** is the `@Qualifier("incidentWebClient")` bean; base URL from `INCIDENT_SERVICE_URL` (default `http://hiveops-incident-backend:8080`).

## No Kafka / no snapshot store on this path

This is the key architectural fact: **Incident Trends bypasses the analytics executor→Kafka→record-store pattern entirely.** It does *not* read `analytics_snapshots` or `ai_insights`, and it does *not* rely on the `IncidentEventConsumer` (`hiveops.incidents.created/updated/resolved`) or `JournalFileParsedConsumer` (`journal.file-parsed`) that feed the snapshot scheduler. Those exist in the service for the dashboard **Overview** snapshots, but the trends tab is a fresh live pull each time. Cross-link [platform.kafka] for the topic catalog used elsewhere in analytics.

## DB tables

| Table | Service | Access |
|-------|---------|--------|
| `journal_events` | hiveops-incident (`hiveiq_incident`) | **read** — `findActivityEvents(ACTIVITY_TYPES, since, institution)` filtered to a curated `ACTIVITY_TYPES` set and `event_time >= now-24h` |
| `atms` | hiveops-incident | **read** — joined for `atmName`/`location`, and drives institution scoping |

Analytics writes nothing for this tab. (`analytics_snapshots`, `ai_insights` in `hiveiq_analytics` are unused here.) See [platform.data-architecture].

## In-memory aggregation (analytics)

From the single `List<JournalEventDTO>`:
- **dailyCounts** — bucketed by date; seeded with **today only** (single bucket).
- **byCategory** — `FleetHealthService.classifyEventType(eventType)` → `HARDWARE | CASH | NETWORK | POWER | AUTH | SYSTEM | OTHER`.
- **recurringFailures** — group by `atmId|eventType` over last 24h, keep `count ≥ 3`, sort desc, top 20.
- **peakHours** — `int[7][24]`, Mon=row 0, by `eventTime` day-of-week × hour (UTC).

## Key API endpoints

| Method | Path | Service | Auth |
|--------|------|---------|------|
| GET | `/api/analytics/incident-trends` | analytics | authenticated JWT (no role gate) |
| GET | `/api/journal-events/activity?hoursBack=24` | incident | authenticated JWT; institution-scoped |

## Config knobs

- `INCIDENT_SERVICE_URL` (analytics) — downstream base URL.
- `JWT_SECRET` — must match across services for the forwarded token to validate.
- `hoursBack` is **hard-coded to 24** in `IncidentTrendsService`; not env-configurable.

See the Internal tab for failure modes and the Overview tab for the customer-facing description.
