---
module: technical.incident
title: hiveops-incident — ATM incident management & journal-event store
tab: Overview
order: 10
audience: dev
---

> **DRAFT · internal.** Written 2026-07-01 from hiveops-incident source. Verify against code before relying on specifics.

## Responsibility

`hiveops-incident` owns the **incidents domain** and the **journal-event store**: creating/assigning/transitioning incidents, storing ATM inventory (`atms`), and ingesting per-event journal data from ATMs. Incidents are auto-created from device events, ARIA threat events, and journal activity. See [platform.service-ownership].

Agents never call this service directly — all agent traffic goes through **hiveops-agent-proxy**, which calls incident's `/api/internal/**` endpoints (secured by `X-Internal-Secret` → `ROLE_INTERNAL` via `InternalSecretFilter`). Fleet-task endpoints have moved out to **hiveops-fleet** (`InternalFleetController` no longer here).

## Data

- **Database:** `hiveiq_incident` (Postgres). Configured via env var **`DB_URL`** (full JDBC URL) — the code default is `jdbc:postgresql://192.168.200.103:5432/hiveops_incident`, overridden in prod. Schema managed by Flyway (`ddl-auto=validate`). See [platform.data-stores].
- **Key tables/entities:**
  - `atms` — ATM inventory (`Atm`: location, status, model, `atmId`)
  - `incidents` — issues (type, severity, status, assignment); plus `incident_events`, `incident_notes`, `workflow_transitions`
  - `journal_events` — individual ATM journal events (`JournalEvent`)
  - `technicians`, `helpdesk_persons` — assignment targets
  - `device_summary` — Kafka-fed mirror of device data from hiveops-devices
  - `event_type`, `incident_type`, `incident_severity`, `incident_status` — lookup tables
  - `ticket_processing_rules`, `incident_closing_rules`, `rule_suggestions`, `rule_drafts` — auto-processing / Adoons rule suggestions
  - `institution_keys`, `institution_agent_properties`, `institution_config_audit` — per-institution config
  - `incident_embeddings` — AI (Adoons) incident indexing

## Kafka

Bootstrap via `KAFKA_BOOTSTRAP_SERVERS`. See [platform.kafka].

**Produced:**

| Topic | Producer class | When |
|-------|---------------|------|
| `hiveops.incidents.created` | `IncidentLifecycleProducer.publishCreated` | incident created |
| `hiveops.incidents.updated` | `IncidentLifecycleProducer.publishUpdated` | incident updated (non-resolved) |
| `hiveops.incidents.resolved` | `IncidentLifecycleProducer.publishUpdated` | status RESOLVED/CLOSED |
| `adoons.rule-suggestion.requested` | `RuleSuggestionProducer` | request Adoons/AI rule suggestion |

**Consumed:**

| Topic | Listener class | Purpose |
|-------|---------------|---------|
| `hiveops.device.events` | `DeviceEventConsumer` (group `hiveops-incident-device-sync`) | sync `device_summary`; auto-create incidents via `IncidentAutoCreationService` |
| `hiveops.aria.threats` | `AriaThreatEventConsumer` (group `hiveops-incident-aria`) | create incidents from ARIA threat events |
| `adoons.rule-suggestion.ready` | `RuleSuggestionResultConsumer` (group `hiveops-incident-adoons`) | ingest completed AI rule suggestions |

## API surface

Base paths: `/api/incidents`, `/api/incident-management`, `/api/journal-events`, `/api/atms`, `/api/internal/**`.

| Method + path | Purpose |
|---------------|---------|
| `POST /api/incidents` | Create incident |
| `GET /api/incidents` | List incidents |
| `GET /api/incidents/paginated` | Paginated list |
| `GET /api/incidents/status/open` | Open incidents |
| `GET /api/incidents/severity/critical` | Critical incidents |
| `GET /api/incidents/{atm,device}/{atmId}` | Incidents for a device (both aliases) |
| `PUT /api/incidents/{id}` | Update incident |
| `POST /api/incident-management/{incidentId}/assign` | Assign incident (admin) |
| `POST /api/incident-management/{incidentId}/transition` | Workflow transition (admin) |
| `POST /api/incident-management/bulk/update` | Bulk update (admin) |
| `POST /api/internal/journal-events` | **Journal-event ingest — internal only** (agent-proxy) |
| `POST /api/internal/agent/{country}/{atm}/heartbeat` | Agent heartbeat (internal) |
| `POST /api/internal/agent/{country}/{atm}/auto-update` | Agent auto-update check (internal) |
| `GET /api/journal-events/{atm,device}/{atmId}/recent` | Recent events for a device |
| `GET /api/atms/dashboard/stats` | Dashboard statistics |

Not exhaustive — the service has ~26 controllers (ATM groups, cassette config, event types/exceptions, settings, technicians, geocoding, Adoons suggestions, auth-audit proxy, etc.).

## Gotchas

- **`DB_URL` is a single full JDBC URL**, not split `DB_HOST`/`DB_PORT`/`DB_NAME`. The hardcoded default points at a stale `hiveops_incident` DB on `192.168.200.103`; production overrides to `hiveiq_incident`.
- **Admin mutations require `@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")`** — `/api/incident-management` assign/unassign/transition/notes/reassign/bulk all gate on this.
- **Agents cannot reach this service directly.** `AgentApiController` was removed; agent-facing routes live in hiveops-agent-proxy, which authenticates to `/api/internal/**` via `X-Internal-Secret` (`INTERNAL_SERVICE_SECRET`). Fleet-task internal endpoints moved to hiveops-fleet (port 8097).
- **`/atm/...` vs `/device/...` dual mappings** exist on incident and journal-event controllers (Phase 2 device-rename aliases) — both resolve to the same handler.
- `JWT_SECRET` is shared with hiveops-auth and hiveops-mgmt; `mgmt.service.url` is used for agent token validation.
- Server port is **8080** (NGINX path `/api/incident/`); multipart uploads allow up to 500MB (agent log files).
