---
module: claims.claim-detail
title: Claim Detail
tab: Architect
order: 30
audience: dev
---

How the **Claim Detail** panel is built end-to-end. See [platform.service-ownership] — this feature is owned entirely by **hiveops-claims**; no other HiveOps service is called for claim, message, note, attachment, or payment data. The only external dependency is the device mirror (below).

## Services involved

| Piece | What it is | Role for this feature |
|-------|-----------|-----------------------|
| `hiveiq-claims-frontend` | Svelte SPA (port 5179, `claims.bcos.cloud`) | Renders `ClaimPanel.svelte`; one component, five tabs |
| `hiveiq-claims` | Spring Boot backend (port 8092, DB `hiveiq_claims`) | Serves all panel data + mutations |
| SMTP (`JavaMailSender`) | outbound mail | Vendor notifications on message send / attachment upload |
| devices service (upstream) | publishes `hiveops.device.events` | Feeds `device_summary` read-model (terminal/location/ATM list) |

There is **no** executor→Kafka→record-store pattern here (that's the fleet-task shape). Claims is a straight synchronous REST + JPA CRUD service; the only Kafka involvement is the inbound device mirror. See [platform.kafka].

## Component → controller wiring

`ClaimPanel.svelte` takes a `claimUuid` prop and:

1. **On mount** — `Promise.all([ claimsAPI.get(uuid), vendorAPI.list() ])`
   - `GET /api/claims/{uuid}` → `ClaimController.getClaim` → `ClaimService.getClaim` → returns `ClaimSummaryResponse` with `payments` **embedded** (so the Payment tab needs no extra call).
   - `GET /api/claims/vendors` → `VendorController` (used for Edit dropdowns + Messages recipient list).
2. **Tabs lazy-load on first open** (via `ClaimActivityController`, base `/api/claims/{claimUuid}`):
   - Messages → `GET /messages?vendorUuid=` (optional filter)
   - Notes → `GET /notes`
   - Files → `GET /attachments`

## Data flow — mutations

All mutations are synchronous REST → `ClaimService` / `ClaimActivityService` → JPA repositories.

- **Overview → Save** — `PUT /api/claims/{uuid}` body `{status, claimType, citVendorUuid|clearCitVendor, spVendorUuid|clearSpVendor}` → `ClaimService.updateClaim`. Sets status/type; assigning/clearing a vendor adds or deletes the matching `claim_payments` row (no re-split).
- **Messages → Send** — `POST /messages` `{messageText, vendorUuid?, additionalEmail?}` → `ClaimActivityService.sendMessage`: persists `claim_messages` row then calls `EmailService.sendMessageNotification` (recipient = vendor `contactEmail`, else `additionalEmail`; silently skipped if neither resolves or SMTP unconfigured). Sender party is derived server-side; UI renders `fromParty == INSTITUTION` on the right.
- **Notes → Add** — `POST /notes` `{noteText}` → `claim_notes`. Institution-only, no email, never shown to vendors.
- **Files → Upload** — `POST /attachments` multipart `file` → writes bytes to `UPLOADS_DIR` (default `/tmp/claims-uploads`), persists `claim_attachments` row, then `EmailService.sendAttachmentNotification` to assigned CIT/SP vendors. **Download** streams back via `GET /attachments/{attachmentUuid}/download`.
- **Payment → Save card** — `PUT /payments/{party}` `{amountOwed, amountPaid}` (`party` ∈ `INSTITUTION|CIT|SP`) → updates one `claim_payments` row.

## DB tables (read/written by this feature) — DB `hiveiq_claims`

| Table | Read | Written | By |
|-------|------|---------|-----|
| `claims` | ✔ | ✔ (PUT) | get/update claim |
| `claim_payments` | ✔ (embedded) | ✔ | payment edit, vendor assign/clear |
| `claim_messages` | ✔ | ✔ | Messages tab |
| `claim_notes` | ✔ | ✔ | Notes tab |
| `claim_attachments` | ✔ | ✔ | Files tab (+ bytes on local disk) |
| `vendors` | ✔ | — | Edit + Messages dropdowns |
| `email_templates` | ✔ | — | resolves vendor-notification subject/body |
| `device_summary` | ✔ | — (Kafka-fed) | ATM/terminal metadata |

See [platform.data-architecture]. `dev` profile uses H2 (`create-drop`, Flyway off); `prod` uses PostgreSQL with Flyway `validate`.

## Kafka topics + consumers

- **Consumed:** `hiveops.device.events` — `DeviceEventConsumer` (`@KafkaListener`, group `hiveops-claims-device-sync`, constants in `DeviceEventKafkaConfig`). Upserts/deletes `device_summary`; `DEVICE_DELETED` removes the row; missing `institutionKey` defaults to `"BCOS"`. This is what backs the terminal/location shown on the panel.
- **Produced:** **none.** Claim state changes do not emit events. Downstream services do not learn about claim/payment updates.

Claims is one of the `device_summary` mirror consumers (incident/fleet/reports/recon/claims); there is **no reconciler**, so a missed publish leaves the mirror stale silently. See [platform.kafka].

## Security model (this feature)

- JWT-authentication-only; `ClaimsPrincipal(userId, email, role, institutionKey)`. No role checks.
- **List** scopes by `institutionKey`; **detail + activity endpoints resolve by UUID only** (no institution scoping) — see the Internal tab and uncertainties for the resulting cross-institution exposure.

For the terse endpoint/table/topic map, see [claims.claim-detail] Claude tab.
