---
title: "Cancel Speaker Sip Test"
method: DELETE
path: "/speakers/{speaker_id}/sip-test"
tags: ["speakers"]
---

# Cancel Speaker Sip Test

`DELETE /speakers/{speaker_id}/sip-test`

## 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}/sip-test`
- Auth: HTTP bearer

Hang the test call up NOW.

THE RELEASE FOR A HORN NOBODY CAN GET OFF. A test whose dial went
indeterminate holds its speaker until the watchdog gives up, and while it
does, Talk Down, the clip Test and live Talk all refuse — with somebody
standing at the device. `DELETE /events/{id}/voice-agent-call` cannot help:
it is addressed by EVENT and joins `agent_actions`, which a test row has
none of.

Ops-lead only and tenant-scoped exactly like the POST, through the same
`_load_speaker`. Idempotent: a test that has already ended is returned
unchanged, and 404 means this speaker has never been tested.

It asks the DEVICE to hang up and closes the row only if that is allowed —
an unconfirmed hang-up leaves the row open with its reason recorded and the
speaker held, which is the shared rule for releasing a speaker and not a
second opinion about it.

## Path parameters

- `speaker_id` string, required

## Response `200`

Successful Response

- SpeakerSipTestResponse: The result of one SIP test call. A 200 MEANS "THE HORN IS ON A CALL", NOT "THE TEST IS OVER": the request returns at connect and deliberately leaves the call up so the owner hears it. Poll `GET /speakers/{id}/sip-test` to watch it end. A `DELETE` on the same path hangs the call up early — the owner's release for a horn that is stuck, and the only one a test row has (the event-scoped hang-up cannot address it). `passed` is the two-signal check, minus the reservation: the device reported the call `Active` AND the voice platform reported the same call live. Either alone proves half of it — the platform connecting proves an agent is speaking into a call, the device being Active proves the horn is on a call, and it takes both to prove the agent is speaking OUT OF the horn.
  - `call_id` string, uuid, required: The call record for this test.
  - `speaker_id` string, uuid, nullable: The speaker that was tested.
  - `speaker_label` string, required: The speaker's display name.
  - `status` string, required: placing | connected | ended | failed.
  - `passed` boolean, required: True only when the device and the voice platform both reported this call live.
  - `summary` string, required: One sentence a person can act on.
  - `device_call_id` string, nullable: The device's own CallId, once it answered the dial.
  - `device_states` SipTestDeviceState[]: Every state the device reported, in order.
    - `state` string, required: The state the device reported, verbatim.
    - `at` string, required: When we observed it, ISO-8601 UTC.
    - `reason` string, nullable: What the device said about the transition, when it said anything.
  - `provider_call_id` string, nullable: The voice platform's call id.
  - `provider_status` string, nullable: The platform's own call status, verbatim.
  - `provider_disconnection_reason` string, nullable: Why the platform says the call ended.
  - `duration_ms` integer, nullable: The platform's measured call duration.
  - `provider_unreadable` boolean: True when the voice platform could not be read at all. NOT the same as `passed: false` with a status: it means we do not know what the platform saw, which is a different thing to do about it than a call the platform never received.
  - `signals_agreed_at` string, date-time, nullable: When the device and the platform first agreed the call was live. A HIGH-WATER MARK: once set it stays, because a call that has since ended still passed.
  - `auth_mode` string, nullable: Which auth mode the device accepted.
  - `playback_note` string, nullable: Set when the device could not confirm it had finished a clip and the owner went ahead anyway.
  - `device_error` SipTestDeviceError: The device's own refusal, quoted. THE SINGLE MOST USEFUL SENTENCE IN AN INSTALL. AXIS answers a refused `axcall:Call` with an HTTP 400 whose body names the fault — `Invalid SIPAccountId 'Gate horn'` is literally the mistake that cost a full day on site — and until this shipped the body was dropped and the console showed nothing but a failure. Bounded at 200 characters; headers are never quoted, and the body cannot carry device credentials (they exist only in the auth header).
    - `http_status` integer, nullable: The HTTP status the device answered with.
    - `body` string, nullable: A bounded excerpt of the device's error body.
  - `sip_account_id` string, nullable: Which SIP account the call was placed from; null means the device default.
  - `started_at` string, date-time, nullable: When the call was placed.
  - `ended_at` string, date-time, nullable: When the call ended, if it has.
  - `end_reason` string, nullable: Why the call ended.
  - `max_seconds` integer, required: The test call's duration cap — the watchdog hangs it up at this.
  - `result_version` integer: Shape version of the stored result this was rendered from. 0 means a row written before the stamp existed.

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