---
module: incident.incident-management
title: Incident Management
tab: Claude
order: 40
audience: dev
---

## Ownership
- **Service:** `hiveops-incident` (Spring Boot, port **8080**, NGINX `/api/incident/`).
- **DB:** `hiveiq_incident` (Postgres). Env var **`DB_URL`** = single full JDBC URL (NOT split host/port/name).
- **Frontend view:** `hiveops-incident/frontend/src/components/Incident/IncidentList/IncidentList.svelte`; API client `lib/api.ts` (`incidentAPI`).

## Endpoints (METHOD path) — all under `/api`
| Method | Path | Role |
|--------|------|------|
| GET | `/api/incidents/paginated` | any auth |
| GET | `/api/incidents` · `/api/incidents/{id}` | any auth |
| GET | `/api/incidents/status/open` · `/api/incidents/severity/critical` | any auth |
| GET | `/api/incidents/atm/{atmId}` · `/api/incidents/device/{atmId}` (aliases) | any auth |
| PUT | `/api/incidents/{id}` | any auth |
| POST | `/api/incident-management` (create) | MSP_ADMIN/BCOS_ADMIN |
| POST | `/api/incident-management/{id}/assign` · `/reassign` · `/unassign` | MSP_ADMIN/BCOS_ADMIN |
| POST | `/api/incident-management/{id}/transition` | MSP_ADMIN/BCOS_ADMIN |
| POST | `/api/incident-management/{id}/notes` | MSP_ADMIN/BCOS_ADMIN |
| POST | `/api/incident-management/bulk/update` | MSP_ADMIN/BCOS_ADMIN |
| POST | `/api/incident-management/{id}/link` · `/group` | MSP_ADMIN/BCOS_ADMIN |
| DELETE | `/api/incident-management/{id}/link/{linkedId}` | MSP_ADMIN/BCOS_ADMIN |
| GET | `/api/incident-management/{id}/links` · `/audit-trail` · `/stats/summary` | any auth |
| GET | `/api/incident-management/technicians/available` | any auth |

Bulk close body: `{ "incidentIds": ["1","2"], "newStatus": "CLOSED" }` (ids are strings).

## Roles
- Every mutation: `@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")`. Reads: authenticated only.

## Tables (`hiveiq_incident`)
- `incidents`, `incident_events`, `incident_notes`, `workflow_transitions`, `atms`, `technicians`, `helpdesk_persons`, `device_summary` (Kafka mirror), `incident_embeddings`.

## Kafka topics
- **Produced:** `hiveops.incidents.created`, `hiveops.incidents.updated`, `hiveops.incidents.resolved` (`IncidentLifecycleProducer` / `IncidentLifecycleKafkaConfig`).
- **Consumed (auto-create):** `hiveops.device.events` (group `hiveops-incident-device-sync`), `hiveops.aria.threats` (group `hiveops-incident-aria`), `adoons.rule-suggestion.ready` (group `hiveops-incident-adoons`).
- WebSocket (STOMP): `/topic/incidents/status-changed`.

## Statuses
- `OPEN, ASSIGNED, IN_PROGRESS, RESOLVED, CLOSED, PARKED`.
- A device with any `PARKED` incident suppresses ALL auto-creation for that device (#219).

## Gotchas
- Transition requires a valid `workflow_transitions` edge; if `requiresAssignment`, assignee must be set first (else 400).
- Closing blocked by `assertNoUnfixedChildren()` if linked children unfixed.
- **Possible Fraud note-before-close is FRONTEND-only** (`IncidentList.svelte` fraud-close modal) — NOT enforced by the API.
- Agents never call this service; they go via **hiveops-agent-proxy** → `/api/internal/**` (`X-Internal-Secret`).
- `device_summary` is a read-only Kafka mirror from hiveops-devices; no reconciler → stale = fix upstream.
- `DB_URL` default points at a stale `hiveops_incident` DB — prod overrides to `hiveiq_incident`.

## Do NOT
- Do NOT add device/institution writes here — device data is owned by hiveops-devices.
- Do NOT assume the fraud-note rule protects direct API closes — it doesn't.
- Do NOT expect UPDATE/transition without an admin JWT — reads succeed, writes 403.
- Do NOT split `DB_URL` into host/port/name.
- Do NOT create incidents for a parked device expecting auto-flow — parking silences it.
- Do NOT invent transition endpoints per-status (start/resolve/close); all go through one `POST /{id}/transition` with `newStatus`.
