---
module: devices.device-detail
title: Device Detail
tab: Claude
order: 40
audience: dev
---

Act-without-guessing reference for `devices.device-detail` (`AtmDetail.svelte`).

## Ownership

- **Owner:** `hiveops-devices` backend, port **8096**, DB **`hiveiq_devices`**, controller `AtmController` (`@RequestMapping({"/api/atms","/api/devices"})`).
- Page is a multi-service aggregator: tabs also call **incident**, **fleet**, **journal**.

## Endpoints — devices-backend (`AtmController` unless noted)

| Method + Path | Purpose | Auth |
|---------------|---------|------|
| `GET /api/atms/{id}` | Device detail | authed |
| `GET /api/atms/{id}/properties` | AtmProperties | authed |
| `PUT /api/atms/{id}` | Update / rename | `MSP_ADMIN`\|`BCOS_ADMIN` |
| `PUT /api/atms/{id}/properties` | Update properties | admin |
| `PATCH /api/atms/{id}/institution` | Move institution (upper-cases key) | admin |
| `PATCH /api/atms/{id}/status` | Set status | admin |
| `POST /api/atms/{id}/reset-state` | → IN_SERVICE, clear supervisor | admin |
| `PUT /api/atms/{id}/hardware-profile` | Set profile (does NOT queue task) | admin |
| `DELETE /api/atms/{id}` | Soft-delete if active, hard if INACTIVE | admin |
| `GET /api/atms/{id}/hardware/changes` | Hardware changes | authed |
| `POST /api/atms/{id}/hardware/changes/{changeId}/acknowledge` | Clear badge | admin |
| `GET /api/atms/{id}/config/changes` | Config changes | authed |
| `POST /api/atms/{id}/config/changes/{changeId}/acknowledge` | Clear badge | admin |
| `POST /api/atms/{id}/cassette-configuration/{configId}` | Apply template | admin |
| `DELETE /api/atms/{id}/cassette-configuration` | Clear template | admin |
| `GET /api/atms/{id}/latest-log` | Text log (from disk) | authed |
| `GET /api/atms/{id}/latest-log-collect` | Latest LOG_COLLECT zip | authed |
| `GET /api/atms/{id}/log-collects[/{collectId}/download]` | Bundle list / download | authed |
| `GET /api/atms/{id}/fleet-tasks` (`DeviceFleetTaskController`) | Task history | admin |

## Endpoints — other services (called from tabs)

| Method + Path | Service |
|---------------|---------|
| `GET /incident/journal-events/atm/{id}/paginated` | incident |
| `GET /incident/journal-events/atm/{id}/cassette-inventory` | incident |
| `GET /incident/incidents/atm/{id}` | incident |
| `POST /fleet/tasks` (CONFIG_UPDATE for hw profile) | fleet |
| `GET /fleet/captures?atmId=` · `/fleet/modules/atm/{id}` · `/fleet/tasks/history?atmId=` | fleet |
| `GET /journal/transactions?atmId=&type=REPLENISHMENT` | journal |

## Tables (`hiveiq_devices`)

- `atms`, `atm_properties`, `atm_hardware_changes`, `atm_config_changes`, `atm_log_collects`, `device_cassette_config`, `cassette_configurations`, `cassette_slots`.

## Kafka

- **Produces:** `hiveops.device.events` — key = `atmId`, `DeviceEventType ∈ {DEVICE_CREATED, DEVICE_UPDATED, DEVICE_DELETED}` (`DeviceEventPublisher`).
- Consumed by device_summary mirrors in incident/fleet/reports/recon (no reconciler).

## Gotchas

- Hardware profile = **two calls**: `PUT .../hardware-profile` THEN `POST /fleet/tasks` (`configPatch:{'atm.hardware.profile':<key>}`). Backend alone does not queue the task.
- `DELETE` on an active device is a **soft delete** (status→INACTIVE) yet still emits `DEVICE_DELETED`; call again to hard-delete.
- `moveInstitution` upper-cases + trims the key; blank is rejected.
- `latest-log*` are `FileSystemResource` reads → error/404 if no file uploaded yet (not a bug).
- Auto-refresh every **60s**, paused while `editing`.
- `server.error.include-message=never` → terse 5xx; get real errors from Loki (`localhost:3100`, services VM).
- Frontend base URL from `window.__APP_CONFIG__.apiUrl`; `gatewayUrl` = that minus trailing `/devices`.

## Do NOT

- Do NOT set a hardware profile without also queuing the CONFIG_UPDATE fleet task — the ATM won't get it.
- Do NOT add device-write endpoints that skip `DeviceEventPublisher` — mirrors silently desync.
- Do NOT expect journal/incident/transaction/capture tabs from devices — they belong to other services; debug the owner.
- Do NOT write incidents/fleet-tasks/journal data into `hiveiq_devices` — respect [platform.service-ownership].
- Do NOT assume reads are admin-gated (they aren't); do NOT assume writes are open (all are `MSP_ADMIN`/`BCOS_ADMIN`).
- Do NOT call it "AI" — the assistant is **Adoons**; brand is **HiveIQ**.
