---
title: "Edit Speaker"
method: PUT
path: "/speakers/{speaker_id}"
tags: ["speakers"]
---

# Edit Speaker

`PUT /speakers/{speaker_id}`

## Request

- Base URL: `https://api.verdun.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.verdun.ai/speakers/{speaker_id}`
- Auth: HTTP bearer

## Path parameters

- `speaker_id` string, required

## Request body

- EditSpeakerRequest: Full replacement of a speaker's configuration, with keep-on-omit fields. `password` is the first: OMITTING it means "unchanged", the same contract as EditNvrRequest, so a label fix never requires re-sending a device password. THE THREE SIP FIELDS FOLLOW THAT SAME CONTRACT, and they have to. This is a full-replace body, and the shipped console round-trips only the fields it knows about — so a `False` default on `sip_enabled` would mean that every volume change saved from the Speakers tab silently disarms the AI voice announcement for that speaker, with no error and no log, until the console learns the field. A client that never mentions them cannot change them. Clearing rather than keeping `sip_account_id` is spelled `""` (see its description); `null`/omitted is always "keep". `transport` and `port` also stay unchanged when omitted or null. An unrelated edit must preserve HTTP and custom router-forwarded ports.
  - `label` string, required: Display name for this speaker.
  - `brand` 'axis'
  - `model` 'axis_c1310_e' | 'axis_c1210_e' | 'axis_c1310_e_mk2'
  - `host` string, required: IP address of the speaker, reachable from api-backend. IP LITERALS ONLY (IPv4 or IPv6) — a hostname is rejected, because a name can only be validated against what DNS answers at that moment while delivery re-resolves later; hostname support arrives with pinned resolution. Stored canonically (lowercased, IPv6 compressed), so one device has one spelling.
  - `port` string, nullable: Device web port: the HTTPS port for direct_https (typically 443), the HTTP port for direct_http (typically 80). Omit or send null to leave the stored port unchanged, including custom forwarded ports.
  - `username` string, required: Device account username.
  - `password` string, nullable: New device password. Omit to leave the stored one unchanged. Never returned.
  - `transport` 'direct_https' | 'direct_http' | 'nvr_proxy'
  - `clip_id` string, nullable: Id of the pre-loaded deterrent clip on the device.
  - `clip_params` object, nullable: Playback parameters (volume, repeat).
  - `enabled` boolean: Kill switch: a disabled speaker is skipped by delivery and by the health poll.
  - `sip_enabled` boolean, nullable: Whether the AI voice agent may place a SIP call to this speaker. OMIT OR SEND null TO LEAVE IT UNCHANGED — this is not a full-replace field, because a client that does not know about it must never be able to disarm the AI announcement by saving an unrelated edit. Send true/false to change it. Arming it is a real safety decision: it stays false until the device has been configured for SIP (a peer-to-peer account and STUN), because there is no state we can infer that from. The agent's speaker resolution reads it; the operator's Talk Down and live talk do not.
  - `sip_account_id` string, nullable: Which of the device's SIP accounts to place the call from. OMIT OR SEND null TO LEAVE IT UNCHANGED. To CLEAR it — i.e. go back to the device default — send an empty string "". Null and empty string mean different things on this field precisely because it is unchanged-by-default.
  - `mic_enabled` boolean, nullable: Whether this unit's microphone is live. OMIT OR SEND null TO LEAVE IT UNCHANGED; send true/false to change it. Informational: it tells the agent whether to pause and listen for a reply, rather than leaving ten seconds of silence at a deaf horn.
  - `map_x` number, nullable: Normalized floor-plan x.
  - `map_y` number, nullable: Normalized floor-plan y.
  - `map_lat` number, nullable: Geographic latitude of the speaker pin.
  - `map_lng` number, nullable: Geographic longitude of the speaker pin.
  - `map_location_label` string, nullable: Operator-facing location prose.

## Response `200`

Successful Response

- SpeakerResponse: A speaker record. Deliberately carries NO credential material: not the password (which never leaves Secrets Manager) and not the credentials_arn (an internal handle nothing client-side has a use for) — the same shape as NvrResponse.
  - `id` string, uuid, required: The speaker record ID.
  - `site_id` string, uuid, required: The site this speaker belongs to.
  - `parent_id` string, uuid, required: The client this speaker belongs to.
  - `label` string, required: Display name.
  - `brand` 'axis', required
  - `model` 'axis_c1310_e' | 'axis_c1210_e' | 'axis_c1310_e_mk2'
  - `host` string, required: IP address of the speaker (IPv4 or IPv6 literal), in canonical form. Hostnames are not supported in v1.
  - `port` string, required: HTTPS port.
  - `username` string, required: Device account username (the password is never returned).
  - `transport` 'direct_https' | 'direct_http' | 'nvr_proxy', required
  - `clip_id` string, nullable: Id of the pre-loaded deterrent clip.
  - `clip_params` object, nullable: Playback parameters.
  - `enabled` boolean, required: Whether delivery and the health poll consider this speaker.
  - `sip_enabled` boolean: Whether the AI voice agent may place a SIP call to this speaker.
  - `sip_account_id` string, nullable: Which of the device's SIP accounts the agent's call is placed from; null means the device default.
  - `mic_enabled` boolean: Whether this unit's microphone is live, which decides whether the agent waits for a reply.
  - `onboarding_checklist` object, nullable: The device-setup checklist: the owner's own attestations under `items`, each stamped server-side. An absent key means not confirmed. The one item the device answers for itself is NOT stored here — read it live from `GET /speakers/{id}/sip-accounts`.
  - `agent_armed_camera_count` integer, nullable: How many of this speaker's camera mappings are ARMED for the AI agent. Zero means nothing will ever announce here, however the device is configured.
  - `last_sip_test` LastSipTest: The headline of the newest SIP test on a speaker. `passed` is the two-signal check — the device reported the call Active AND the voice platform reported the same call live. The full timeline lives on `GET /speakers/{id}/sip-test`; this is what a list row needs to say whether a horn is armed.
    - `at` string, date-time, nullable: When the test was placed.
    - `passed` boolean, required: True only when both signals agreed.
    - `call_id` string, uuid, required: The call record, for the detail view.
    - `summary` string, required: One sentence a person can act on.
  - `map_x` number, nullable: Normalized floor-plan x.
  - `map_y` number, nullable: Normalized floor-plan y.
  - `map_lat` number, nullable: Geographic latitude of the speaker pin.
  - `map_lng` number, nullable: Geographic longitude of the speaker pin.
  - `map_location_label` string, nullable: Operator-facing location prose.
  - `map_heading` number, nullable: Direction a horn faces, degrees clockwise from map-up; null = not aimed (always null for an omnidirectional ceiling unit).
  - `healthy` boolean, nullable: Last health-poll result.
  - `last_healthcheck_at` string, date-time, nullable: When the speaker was last polled.
  - `health_error` string, nullable: Why the last health poll failed: unreachable (no HTTP answer), auth_rejected (the device answered 401 to the stored credentials), not_a_speaker (something answered, but not an Axis device API - e.g. a router page), clip_missing (the configured clip id is not on the device), or error. Null when healthy or never checked.
  - `device_serial` string, nullable: Serial number of the device last seen at this host:port, read without credentials. Compare it across speakers to tell two units behind one IP apart. Last known value; a failed poll keeps it.
  - `device_product` string, nullable: Product name of the device last seen at this host:port (ProdFullName, else ProdNbr). Last known value.
  - `device_firmware` string, nullable: Firmware version of the device last seen at this host:port. Last known value.
  - `device_identity_at` string, date-time, nullable: When the device identity above was last read successfully.
  - `created_at` string, date-time, nullable: When the speaker was registered.
  - `updated_at` string, date-time, nullable: When the speaker was last edited.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/verdun/apis/verdun-api.md) - [All operations](https://skmtc.dev/verdun/apis/verdun-api/llms.txt) - [OpenAPI document](https://skmtc.dev/verdun/apis/verdun-api/revisions/35a2a6ebedda?raw)
