---
module: devices.agent-profile
title: Agent Profile
tab: Internal
order: 20
audience: internal
---

Ops/support view for the **Agent Profile** screen (Devices › Settings). Profiles are hardware groupings (`profileKey` + label) that decide which agent modules an ATM receives. This tab covers who can change them, what breaks, and how to fix it fast.

## Who can do what

| Action | Endpoint | Role required |
|--------|----------|---------------|
| List profiles | GET `/api/agent-profiles` | any authenticated user (no `@PreAuthorize`) |
| View modules manifest | GET `/api/agent-profiles/modules-manifest` | any authenticated user |
| Add profile | POST `/api/agent-profiles` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` |
| Edit profile | PUT `/api/agent-profiles/{key}` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` |
| Delete profile | DELETE `/api/agent-profiles/{key}` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` |

All served by `hiveops-devices` backend (port 8096). The read endpoints are open to any valid JWT; only mutations are admin-gated.

## First things to check when it misbehaves

1. **Modules column shows "—" for every profile.** The CDN modules manifest failed to load. The frontend shows the banner *"Could not load modules manifest from CDN."* The backend proxies `https://cdn.bcos.cloud/downloads/agent/modules/modules.json` and returns HTTP **502** with `{"modules":[]}` when the CDN is unreachable or non-200. Check: is the CDN up, and is `agent.cdn.token` / `CDN_TOKEN` set correctly on the devices backend? The token is sent as the `X-CDN-Token` header; if the CDN now requires it and it's missing/stale, the fetch returns non-200 and the column blanks out. Manifest is cached 60s, so allow up to a minute after a fix.
2. **Module counts look wrong / an "exclusive" module isn't showing.** Module-to-profile mapping lives entirely in the CDN `modules.json` (each module's optional `profiles: []` array), NOT in the devices DB. A module with no `profiles` (or an empty array) installs on **every** ATM; a module listing a profileKey installs **only** on ATMs with that profile (the ★ exclusive badge). If a module shows on the wrong profile, fix `modules.json` on the CDN, not the profile record.
3. **Profile save fails with 400.** Backend rejects blank `label` (create + update) and blank `profileKey` (create) with `400 Bad Request`. The form also enforces this client-side.
4. **"Profile key already exists" (409).** Create is refused when the key is already present — keys are the primary key of `agent_profiles`. Edit the existing one instead.
5. **Delete fails with 409 "assigned to one or more devices".** A profile can't be deleted while any ATM has it. See below.

## Common failure modes + fixes

- **Can't delete a profile.** `DELETE` returns **409 Conflict** if `SELECT COUNT(*) FROM atms WHERE hardware_profile = :key > 0`. Reassign or clear those devices' profile first (device detail page, or group bulk-assign), then retry. To find the blockers: query `atms` for `hardware_profile = '<key>'`.
- **ATM not getting its modules.** Two independent gates: (a) the profile row must be **enabled** (Active badge) — a disabled profile pauses provisioning; (b) the ATM's `atm.hardware.profile` agent property must exactly match a `profileKey` (lowercase, no spaces). A mismatch means the ATM falls back to only the universal (non-exclusive) modules and never gets the exclusive ones.
- **Key case/format issues.** Backend lowercases and trims `profileKey` on create (`applyFields`), but the ATM-side `atm.hardware.profile` value is matched as-is against `modules.json` and the `atms` table. Keep everything lowercase.
- **Edit doesn't change the key.** The key field is disabled on edit — `profileKey` is immutable (it's the PK). To "rename" a key you must create a new profile, reassign devices, and delete the old one.

## Data location

- **DB:** `hiveiq_devices` › table `agent_profiles` (columns: `profile_key` PK, `label`, `description`, `enabled`, `sort_order`, `created_at`, `updated_at`). Created by Flyway `V14__agent_profiles_and_hardware_profile.sql`.
- **Device assignment:** `atms.hardware_profile` (varchar 40).
- **Module manifest:** CDN file, not DB — `https://cdn.bcos.cloud/downloads/agent/modules/modules.json`.

No Kafka is involved in editing a profile. Profile writes do **not** publish `hiveops.device.events` (that topic tracks device rows, not profile rows).
