---
title: "Update External Talkdown"
method: PATCH
path: "/events/{event_id}/external-talkdowns/{talkdown_id}"
tags: ["external-talkdowns"]
---

# Update External Talkdown

`PATCH /events/{event_id}/external-talkdowns/{talkdown_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/events/{event_id}/external-talkdowns/{talkdown_id}`
- Auth: HTTP bearer

Report what the extension observed: `connected`, `ended` or `failed`.

`requested -> connected -> ended`, `requested -> failed`,
`connected -> failed`. Anything else is a 409, EXCEPT an exact repeat of the
row's current terminal status with the same `fail_reason`, which is an
idempotent 200 no-op so a lost response does not make the extension's retry
look like an error. Timestamps and `duration_ms` are server-side.

## Path parameters

- `event_id` string, required
- `talkdown_id` string, required

## Request body

- UpdateExternalTalkdownRequest: Move one talk-down row along its state machine. `requested` IS NOT AN ACCEPTED TARGET, and that is a 422 rather than a 409: it is not a transition anyone could legitimately attempt, it is a malformed request. The three that are accepted are the only ones the extension can ever observe. THERE IS NO `connected_at`, `ended_at` OR `duration_ms` FIELD. All three are written by the service from its own clock; see the service's state machine for which transition writes which. Unknown keys are rejected rather than ignored (`extra="forbid"`), so a client that sends one of those names learns it was not accepted instead of believing the value landed.
  - `status` 'connected' | 'ended' | 'failed', required: The state the extension observed. `connected` = the vendor console reported the call up (the horn is live). `ended` and `failed` are terminal.
  - `fail_reason` 'timeout' | 'not_logged_in' | 'phone_not_found' | 'busy' | 'extension_error'
  - `detail` string, nullable: Short attempt trail — extension version, last observed console state. At most 200 characters, letters/digits/spaces and `. , ; : _ ( ) / -` only, no unbroken run over 32 characters, and never a URL. Anything token-shaped is refused rather than stored.

## Response `200`

Successful Response

- ExternalTalkdownResponse: One talk-down attempt through a vendor console. THERE IS NO `announced_at` AND THERE MUST NEVER BE ONE. A vendor console reports that the CALL came up, never that audio was delivered; `agent_actions.announced_at` means positively-verified playback and is what a quiet auto-dismissal rests on. `status = connected` is the strongest honest fact this path has.
  - `id` string, uuid, required: The talk-down record id.
  - `event_metadata_id` string, uuid, required: The alarm this was about.
  - `camera_id` string, uuid, required: The camera whose vendor console was driven.
  - `parent_id` string, uuid, required: The client, denormalised so a tenant-scoped read needs no join.
  - `site_id` string, uuid, required: The site, denormalised for the same reason.
  - `operator_id` string, uuid, nullable: Who talked. Null once that account has been deleted — the record outlives it.
  - `provider` 'verkada_command', required
  - `mode` 'mic' | 'clip', required
  - `status` 'requested' | 'connected' | 'ended' | 'failed', required
  - `fail_reason` 'timeout' | 'not_logged_in' | 'phone_not_found' | 'busy' | 'extension_error'
  - `client_ref` string, required: The extension's per-attempt id; the idempotency key this row was created under.
  - `requested_at` string, date-time, required: When the operator pressed the button, on the SERVER's clock.
  - `connected_at` string, date-time, nullable: When the vendor console reported the call up. Null if it never did.
  - `ended_at` string, date-time, nullable: When the attempt reached a terminal state.
  - `duration_ms` integer, nullable: connected_at to ended_at in milliseconds. Null when the call never connected — which is the honest answer, not zero.
  - `detail` string, nullable: Free-text attempt trail. Never a URL or a token.
  - `created_at` string, date-time, nullable: Row creation timestamp.
  - `updated_at` string, date-time, nullable: Row update timestamp.

## 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)
