---
module: devices.map
title: Map
tab: Claude
order: 40
audience: dev
---

> Terse agent reference. Grounded in `AtmMap.svelte`, `lib/api.ts`, `lib/stores.ts`, `AtmController`, `AtmGroupController`, `IncidentController` (2026-07-01). Read-only view — no writes, no dedicated backend.

## Ownership

- **View:** `hiveops-devices` frontend, `frontend/src/components/AtmMap/AtmMap.svelte` (Leaflet, port 5177).
- **Pin/group data owner:** `hiveops-devices` backend (8096).
- **Incident data owner:** `hiveops-incident` backend (8080).
- No `devices.map` controller / table / Kafka topic exists.

## Endpoints (all GET, all read-only)

| Method + Path | Service | Auth | Notes |
|---|---|---|---|
| `GET /api/atms` | devices | any JWT (no `@PreAuthorize`) | pins; frontend `atmAPI.getAll()` |
| `GET /api/atms/paginated?size=9999&groupId=<gid>&sort=atmId,asc` | devices | any JWT | used instead when `localStorage.hiveops_defaultGroupId` set |
| `GET /api/atm-groups` | devices | any JWT | Group filter (`atmGroupAPI.getAll()`) |
| `GET /api/incidents` | incident | any JWT, institution-scoped | open-incident colour + type filter; frontend calls gateway path `…/incident/incidents` |
| `PUT /api/atms/{id}` | devices | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` | ONLY way to set lat/long (unmapped fix); done in ATM Detail, not the map |

- Frontend base URLs: `devicesApi` = `window.__APP_CONFIG__.apiUrl` (`https://api.bcos.cloud/devices`); `incidentApi` = that with `/devices` stripped + `/incident`. nginx injects `/api` for the incident call.
- Devices controller also answers Phase-2 aliases `/api/devices` and `/api/device-groups`.

## Tables (read-only)

- `atms` (`hiveiq_devices`) — cols used: `latitude`, `longitude`, `status`, `in_supervisor`, `agent_connection_status`.
- `atm_groups` (`hiveiq_devices`).
- `incidents` (`hiveiq_incident`).

## Topics

- None consumed/produced by this view. (Platform context only: devices publishes `hiveops.device.events`; the map does NOT read those mirrors — it queries devices + incident live.)

## Client-only state (localStorage keys)

- `hiveops_map_filters_v2` — current filters + saved presets.
- `devices_map_autoRefresh` — auto-refresh toggle.
- `hiveops_defaultGroupId` — forces group-scoped `loadAtms()`.

## Enums

- `status`: `IN_SERVICE`, `DOWN`, `MAINTENANCE`, `INACTIVE`, `OUT_OF_CASH`.
- `agentConnectionStatus`: `CONNECTED`, `DISCONNECTED`, `NEVER_CONNECTED`.
- incident: only `status === 'OPEN'` colours a pin; `severity`: `CRITICAL`/`HIGH`/other.

## Gotchas

- Pins need **non-null lat AND long**; others dropped + counted in amber banner.
- Clustering keys on **exact** `"lat,lon"` string — near-but-not-equal coords do not merge.
- Two-service view: incident outage ⇒ pins still render, but no orange/incident-type filter.
- Default group active ⇒ map capped at 9999 rows and scoped to that group.
- Presets/filters are per-browser only; not persisted server-side.
- Reads have no role gate; only lat/long edits (`PUT /api/atms/{id}`) need `MSP_ADMIN`/`BCOS_ADMIN`.

## Do NOT

- Do NOT invent a `devices.map` endpoint/table/topic — none exists; it aggregates existing device + incident reads.
- Do NOT expect an incident endpoint at `/incident/api/incidents`; frontend uses `/incident/incidents` (nginx adds `/api`).
- Do NOT tell users presets are recoverable after clearing browser data — they are `localStorage`-only.
- Do NOT attribute stale pins to Kafka `device_summary` mirrors — this view bypasses them.
- Do NOT add write actions to the map; corrections belong in ATM Detail (devices) / Incident app.
