---
module: msp.institution-keys
title: Institution Keys
tab: Internal
order: 20
audience: internal
---

> Ops/support view. Grounded in `hiveops-devices` backend + `hiveops-msp` frontend, 2026-07-01.

## What this screen actually does
Maps a short **key** (2–10 upper-case chars, e.g. `AICU`) → the full **institution name** stored on devices (`atms.institution`) → the **device types** allowed to register. The key is embedded in the agent JWT / `X-Institution-Key` and used to scope every device query for CUSTOMER and MSP_ADMIN users.

Owning service is **hiveops-devices** (NOT hiveops-msp — the MSP SPA has no backend; it calls devices via NGINX `/devices/...`). Table lives in the **hiveiq_devices** DB.

## Roles required
- **Read** (list/search): any valid JWT. `GET` endpoints have **no `@PreAuthorize`**; results are scoped by `InstitutionContext.resolveScope()`.
  - BCOS_ADMIN / ADMIN / QDS_ADMIN / USER → unrestricted (sees all).
  - CUSTOMER / MSP_ADMIN → only their JWT-granted keys.
- **Create / Edit / Delete**: `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` only.
  - Note: a plain `ADMIN` (or QDS_ADMIN/USER) can *read all* but **cannot mutate** — the write `@PreAuthorize` does not include `ADMIN`. If a "why can't I add a key?" ticket comes in, check the caller's role first.

## First things to check when it misbehaves
1. **New device won't come online / not visible.** The device's `deviceType` must be in `supported_device_types` for its key, and the key must exist. Registration (agent-proxy → devices `POST /api/internal/atms/register`) returns **403 `{linked:false, error:...}`** when the device type isn't supported. Add the missing type on the Edit panel.
2. **Device registers but disappears from a customer's view.** The key had **no mapping** at registration time → `atms.institution` was left blank, so scoped users can't see it. Create the mapping, then the device links its institution on next registration (only fills when `atms.institution` is still blank — it never overwrites an existing value).
3. **Renamed an institution and old devices vanished.** Editing the **Institution Name** changes only the `institution_keys` row. Already-registered devices keep the OLD name string in `atms.institution`; scope resolution translates key→*current* name, so the old-named devices stop matching. Fix the device institution names in the Devices app (or re-register) after a rename.
4. **Duplicate-name error on save.** `institution_name` has a **UNIQUE** constraint. Two keys cannot share a name; the save fails at the DB layer (surfaces as a 5xx/error toast, not a clean validation message).
5. **Stale list.** The single-key lookup is cached (`institutionKeys` cache); create/update/delete evict it. `getAll` (the list) is uncached, so the table is always fresh — a stale row usually means the frontend's 60s auto-refresh is off, not a cache issue.

## Common failure modes → fix
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| 403 on Add/Edit/Delete | caller lacks MSP_ADMIN/BCOS_ADMIN | use a correctly-roled account |
| Agent reg rejected (403 linked:false) | device type not in supported list | add ATM/TCR/SRV on Edit panel |
| Device has no institution | key unmapped at first contact | create key, wait for next heartbeat/register |
| Save fails, no clear message | duplicate `institution_name` (UNIQUE) | pick a distinct name |
| Deleted a key, agent config gone | FK cascade wiped `institution_agent_properties` | recreate key + re-enter Agent Properties |

## Deleting a key — what it takes with it
`institution_agent_properties.institution_key` has `ON DELETE CASCADE` → deleting a key **also deletes that institution's stored agent/EJ config**. Users whose JWT carries that key see no devices until a new mapping exists. Only delete a key you are sure is unused.

## Gotcha worth knowing
An unknown/blank `deviceType` on registration is silently parsed as **`ATM`** (`AtmService.parseDeviceType`). A device sending a garbage type still passes if the institution allows ATM — so "rejected" tickets are always a real type mismatch, never a typo falling through.

See also: [msp.institution-keys] Overview (user-facing steps), Architect tab (data flow), Claude tab (exact paths).
