Legs
Last updated August 14, 2026
Voice call legs (SIP or WebRTC)
List all active legs
Responses
Originate an outbound leg
Originate a new outbound leg. The type field selects the transport: sip originates a SIP INVITE; whatsapp originates a WhatsApp call through Meta; websocket dials a remote WebSocket endpoint (audio is PCM in either binary or json_base64 framing, with bidirectional text and caller-supplied X-/P- headers).
Request Body
| Field | Type | Description | |
|---|---|---|---|
type | enum | required | Leg type Values: sip, whatsapp, websocket, livekit_room |
to | string | optional | Destination. For sip legs, a SIP URI (e.g. "sip:[email protected]"). For whatsapp legs, an E.164 phone number (with or without '+'). |
uri | string | optional | Deprecated alias for `to` (sip legs only). Prefer `to`. |
from | string | optional | Caller ID. A bare user-part (e.g. "+15551234567", "alice") sets the user of the SIP From header. A full SIP URI (e.g. "sip:[email protected]") sets both the user and the host; otherwise the host comes from the matched trunk's AOR realm, falling back to SIP_DOMAIN. |
privacy | string | optional | SIP Privacy header value (e.g. "id", "none") |
ring_timeout | integer | optional | Seconds to wait for answer; 0 = no timeout 0 |
max_duration | integer | optional | Maximum call duration in seconds after connect. Automatically hung up when reached. 0 or omitted = no limit. 0 |
codecs | array[enum] | optional | Codec preference order (sip legs only) |
headers | object | optional | Custom headers to include in the outbound INVITE (sip/whatsapp) or the WebSocket upgrade request (websocket) |
room_id | string | optional | Room ID to auto-add the leg to once media is ready (early_media or connected). If the room does not exist, it is automatically created. |
auth | any | optional | Digest auth credentials. Required for whatsapp legs (Meta-issued password; username defaults to `from` with '+' stripped). Optional for sip legs (sipgo retries on 401/407 challenge). |
webhook_url | string(uri) | optional | Route all events for this leg exclusively to this URL instead of global webhooks. |
webhook_secret | string | optional | HMAC-SHA256 signing secret for the per-leg webhook. |
amd | any | optional | Enable Answering Machine Detection on outbound calls. Include the object (even empty) to enable with defaults; omit to disable. |
accept_dtmf | boolean | optional | If false, this leg will not receive DTMF digits broadcast from other legs in the same room. Defaults to true. true |
app_id | string | optional | Application identifier. Carried through to all events for this leg. Use to filter the WebSocket event stream by app. |
speech_detection | boolean | optional | If true, emit speaking.started and speaking.stopped events for this leg. If false, suppress them. Omit to use the server default (SPEECH_DETECTION_ENABLED env var, default false). |
rtt | boolean | optional | For sip legs: offer Real-Time Text (ITU-T T.140 over RTP per RFC 4103) alongside audio. For websocket legs: enable the bidirectional text-message channel. Default: false. false |
streams | array[object] | optional | SIP outbound only. Extra m=audio sections to offer alongside the call's primary bidirectional audio, so a multi-stream call is established by the first INVITE instead of a follow-up re-INVITE. Each entry binds its own RTP port and may be mixed into its own room. To add a stream to a call that is already up, use POST /v1/legs/{id}/streams instead. |
url | string(uri) | optional | WebSocket target URL (ws:// or wss://) for outbound websocket legs. Required when type=websocket. |
sample_rate | enum | optional | PCM sample rate for websocket legs. The room's mixer automatically resamples between this and the room rate. 16000Values: 8000, 16000, 24000, 48000 |
wire_format | enum | optional | Audio framing for websocket legs. `binary` ships raw PCM as WebSocket binary frames; `json_base64` wraps PCM as `{"type":"audio","audio":"<base64>"}` text frames (browser-friendly). "binary"Values: binary, json_base64 |
sample_format | enum | optional | On-the-wire PCM sample encoding for websocket legs. v1 only supports `s16le`. "s16le"Values: s16le |
livekit | any | optional | LiveKit room join parameters (only used when type=livekit_room). |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
id | string | required | Unique leg identifier (UUID) |
type | enum | required | Leg type Values: sip_inbound, sip_outbound, webrtc, whatsapp_in, whatsapp_out, websocket_in, websocket_out, moq_in, livekit_publish, livekit_participant |
state | enum | required | Leg state Values: ringing, early_media, connected, held, hung_up |
room_id | string | optional | Room ID if the leg is in a room, empty otherwise |
muted | boolean | required | Whether the leg is muted (cannot be heard by others) |
deaf | boolean | required | Whether the leg is deaf (cannot hear others) |
accept_dtmf | boolean | required | Whether the leg receives DTMF digits broadcast from other legs in the same room. Defaults to true. |
held | boolean | required | Whether the call is on hold (SIP legs only) |
role | string | optional | Routing role used by the room's audio routing matrix (e.g. "customer", "agent", "supervisor"). Empty string means unroled (full mesh). |
app_id | string | optional | Application identifier for event stream filtering. |
sip_headers | object | optional | Deprecated: X-* headers from the inbound INVITE. Only present on sip_inbound legs. Use `headers` for new code; it carries the same map plus surfaces handshake headers for websocket legs. |
headers | object | optional | Custom protocol headers exposed by the leg's transport — X-/P- headers from a SIP INVITE, the WebSocket upgrade request, or supplied at outbound dial time. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Connect a WebSocket as a leg (HTTP upgrade)
Upgrades the HTTP request to a WebSocket and creates a websocket_in leg. Query parameters: sample_rate (8000/16000/24000/48000; default 16000); wire_format (binary default, or json_base64); sample_format (s16le only in v1); room_id to auto-add the leg to a room; app_id for event filtering; rtt=true to enable the bidirectional text channel; webhook_url/webhook_secret for per-leg event routing. X-* and P-* request headers (plus Authorization) are captured into the leg's headers map and surfaced on leg.ringing and in LegView. The leg goes straight to connected (no ringing/answer flow). Audio frames carry PCM16-LE mono at sample_rate; with wire_format=binary each WebSocket binary frame is exactly one 20ms PCM frame, with json_base64 the same payload is wrapped as {"type":"audio","audio":"<base64>"}. Text and control messages always use JSON text frames: {"type":"text","text":...}, {"type":"ping","event_id":N}/{"type":"pong","event_id":N}, and {"type":"hangup"} to terminate from the peer side.
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Connect a MoQ (Media over QUIC) leg (WebTransport extended-CONNECT, experimental)
**Actual HTTP method: CONNECT** (HTTP/3 extended-CONNECT for WebTransport). OpenAPI 3.1 does not define connect as a path-item method, so this operation is documented under post with an x-actual-method: CONNECT extension. Standard HTTP clients (e.g. curl -X POST) will receive 405 Method Not Allowed — use a WebTransport-capable HTTP/3 client.
**Experimental / PoC.** Upgrades an HTTP/3 extended-CONNECT request to a WebTransport session and creates an inbound MoQ leg. Reachable only over HTTP/3 on the MoQ listener (not on the regular HTTP/1.1 chi listener). Requires MOQ_ENABLED=true plus MOQ_TLS_CERT_FILE and MOQ_TLS_KEY_FILE. Speaks IETF draft-11 of moq-transport (via mengelbart/moqtransport); browser interop with draft-16 clients (moqtail, moq.dev) is not expected to work. Media is bidirectional within a single MoQ session per leg: the server publishes namespace mix/track audio (downlink, room mix) and subscribes to namespace mic/track audio (uplink, leg mic). Audio is Opus framed one frame per MoQ Object (LOC-style) at 48 kHz mono, 20 ms frames. Query parameters: sample_rate (only 48000 is accepted — the encoder/decoder are hard-wired to 48 kHz; default 48000); room_id to auto-add the leg to a room; app_id for event filtering; webhook_url/webhook_secret for per-leg event routing. X-* and P-* request headers (plus Authorization) are captured into the leg's headers map and surfaced on LegView. The leg goes straight to connected (no ringing/answer flow); no DTMF, no RTT, and event parity is limited to leg.connected / leg.disconnected.
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Get a single leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
id | string | required | Unique leg identifier (UUID) |
type | enum | required | Leg type Values: sip_inbound, sip_outbound, webrtc, whatsapp_in, whatsapp_out, websocket_in, websocket_out, moq_in, livekit_publish, livekit_participant |
state | enum | required | Leg state Values: ringing, early_media, connected, held, hung_up |
room_id | string | optional | Room ID if the leg is in a room, empty otherwise |
muted | boolean | required | Whether the leg is muted (cannot be heard by others) |
deaf | boolean | required | Whether the leg is deaf (cannot hear others) |
accept_dtmf | boolean | required | Whether the leg receives DTMF digits broadcast from other legs in the same room. Defaults to true. |
held | boolean | required | Whether the call is on hold (SIP legs only) |
role | string | optional | Routing role used by the room's audio routing matrix (e.g. "customer", "agent", "supervisor"). Empty string means unroled (full mesh). |
app_id | string | optional | Application identifier for event stream filtering. |
sip_headers | object | optional | Deprecated: X-* headers from the inbound INVITE. Only present on sip_inbound legs. Use `headers` for new code; it carries the same map plus surfaces handshake headers for websocket legs. |
headers | object | optional | Custom protocol headers exposed by the leg's transport — X-/P- headers from a SIP INVITE, the WebSocket upgrade request, or supplied at outbound dial time. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Hang up a leg (asynchronous)
Validates the leg exists and queues a hangup. The HTTP call returns 202 as soon as the leg is found; the SIP work and cleanup run in the background, and the eventual disconnection is observed via the leg.disconnected event.
Without a request body the legacy behavior is preserved: SIP BYE on connected legs (cdr.reason: "api_hangup"), or dialog cancel on unanswered inbound legs (cdr.reason: "caller_cancel").
With {"reason": "<value>"} and an unanswered SIP inbound leg (state ringing or early_media), VoiceBlender sends a final non-2xx response instead of BYE/cancel: busy→486, declined/rejected→603, unavailable→480, not_found→404, forbidden→403, server_error→500. The reason value is passed through to leg.disconnected's cdr.reason.
For connected legs the request body is ignored.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
reason | enum | optional | Disconnect reason. Only honored for unanswered SIP inbound legs (state `ringing` or `early_media`); on connected legs the body is ignored and the leg is hung up with the legacy `api_hangup` reason. The value flows through to `leg.disconnected`'s `cdr.reason` and selects the SIP final response: `busy`→486, `declined`/`rejected`→603, `unavailable`→480, `not_found`→404, `forbidden`→403, `server_error`→500. Values: busy, declined, rejected, unavailable, not_found, forbidden, server_error |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Answer a ringing or early-media inbound SIP leg (asynchronous)
Signals the inbound-call goroutine to send 200 OK. The HTTP call returns 202 immediately; the actual SIP 200 OK is sent in the background, and the leg's transition is observed via leg.connected. Pre-condition failures (wrong state, unknown codec) still return 4xx synchronously. When the caller offers several m=audio sections they are all accepted; streams optionally routes each accepted stream beyond the primary into its own room, applied once the answer is negotiated.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
speech_detection | boolean | optional | If true, emit speaking.started and speaking.stopped events for this leg. If false, suppress them. Omit to use the server default (SPEECH_DETECTION_ENABLED env var, default false). |
codec | enum | optional | Explicit codec for the answer SDP. Must appear in the remote offer's offered_codecs list. Omit to use the server's default preference order. Values: PCMU, PCMA, G722, opus, AMR-WB, AMR-NB |
streams | array[object] | optional | Rooms for the caller's additional audio streams, applied once the answer is negotiated. Positional: entry i addresses the i-th accepted stream beyond the primary, in m-line order — the caller's offer decides how many exist, so an entry with no matching stream is ignored. Use POST /v1/legs/{id}/streams/{streamId}/room to re-route a stream later. |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Send 180 Ringing on a ringing inbound SIP leg (asynchronous)
Queues a SIP 180 Ringing provisional response with no SDP. Use when SIP_AUTO_RINGING=false (the default) and you want to indicate alerting before deciding to early-media or answer. Idempotent: each call emits another 180 — receivers tolerate re-sends. The HTTP call returns 202 as soon as the request is validated; SIP-level send failures surface as leg.command_failed with command="ring".
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Challenge a ringing inbound SIP leg with a 401 digest auth request
Sends a SIP 401 Unauthorized carrying a WWW-Authenticate digest challenge on an unanswered inbound INVITE. The current leg is torn down (a leg.disconnected with reason="challenged" is published); the UAC's credentialed re-INVITE arrives as a new inbound call surfaced via leg.ringing with authenticated=true once VoiceBlender verifies the response against the supplied credential. Provide either password or ha1. An invalid retry is answered with 403 Forbidden and never surfaced.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
realm | string | required | |
username | string | optional | |
password | string | optional | |
ha1 | string | optional | |
algorithm | string | optional | |
qop | array[string] | optional | |
max_expires | integer | optional |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Enable early media on a ringing inbound SIP leg (asynchronous)
Queues a SIP 183 Session Progress with SDP and the RTP/codec setup. The HTTP call returns 202 as soon as the request is validated; the leg transitions to early_media state asynchronously, observable via leg.early_media. Setup failures surface as leg.command_failed with command="early_media".
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
codec | enum | optional | Explicit codec for the 183 Session Progress SDP. Must appear in the remote offer's offered_codecs list. Omit to use the server's default preference order. Values: PCMU, PCMA, G722, opus, AMR-WB, AMR-NB |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Mute a leg
A muted leg's audio is excluded from the room mix and speaking events are suppressed. Taps (recording/STT) still receive the muted leg's own audio.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Unmute a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Put a SIP call on hold (asynchronous)
Queues a re-INVITE with sendonly SDP direction. The HTTP call returns 202 as soon as the leg is validated; the re-INVITE is sent in the background and success surfaces as leg.hold. Failures surface as leg.command_failed with command="hold". The RTP timeout is paused while held, and a 2-hour auto-hangup timer starts.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Resume a held SIP call (asynchronous)
Queues a re-INVITE with sendrecv SDP direction. The HTTP call returns 202; success surfaces as leg.unhold, failures as leg.command_failed with command="unhold".
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Transfer a SIP leg via REFER (asynchronous)
Asynchronously transfers a SIP leg. The HTTP call returns 202 as soon as the request is validated; the REFER is sent in the background and its outcome is surfaced through leg.transfer_initiated / leg.transfer_progress / leg.transfer_completed / leg.transfer_failed events. Blind transfer when replaces_leg_id is omitted; attended transfer when present (the named leg's dialog identity is embedded as a Replaces parameter per RFC 3891). On terminal 2xx the leg (and the replaces leg, if any) is hung up automatically.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
target | string | required | SIP URI to transfer the call to (e.g. "sip:[email protected]"). |
replaces_leg_id | string | optional | ID of an existing connected SIP leg whose dialog should be replaced (attended transfer). Omit for blind transfer. |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Accept a parked inbound REFER
Accepts an inbound transfer surfaced via leg.transfer_requested (default app-driven model, SIP_REFER_AUTO_DIAL=false). Sends 202 Accepted to the referrer and a NOTIFY sipfrag 100 Trying, keeping the refer subscription open. {id} is the referrer leg (the leg that received the REFER). After accepting, re-bridge as needed and report progress via .../transfer/progress and the outcome via .../transfer/complete.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Report interim transfer progress
Sends an interim sipfrag NOTIFY (e.g. 180 Ringing) on an accepted inbound transfer so the referrer's UA reflects real progress. The subscription stays active.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
status_code | integer | required | |
reason | string | optional |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Complete an accepted inbound transfer
Terminates an accepted inbound transfer with a final sipfrag NOTIFY and emits leg.transfer_completed (on success) or leg.transfer_failed. success:true sends 200 OK; otherwise status_code/reason (default 500) carry the failure. The referrer leg is left for the app to hang up.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
success | boolean | required | |
status_code | integer | optional | |
reason | string | optional |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Decline a parked inbound REFER
Rejects a parked (not-yet-accepted) inbound transfer, replying to the referrer with a non-2xx (603 Decline by default; override via code/reason) and emitting leg.transfer_failed.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
code | integer | optional | |
reason | string | optional |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Send DTMF digits on a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
digits | string | required | DTMF digits to send (0-9, *, #) |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Enable DTMF reception on a leg
Allow this leg to receive DTMF digits broadcast from other legs in the same room. This is the default state for new legs.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Disable DTMF reception on a leg
Block this leg from receiving DTMF digits broadcast from other legs in the same room. DTMF received from this leg's own far end is still emitted as a leg.dtmf event.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Send Real-Time Text (T.140) on a SIP leg
Sends UTF-8 text on the leg's RTT (T.140 / RFC 4103) media stream. Requires that the SDP offer/answer agreed on an m=text section with the remote UA. Enable RTT on the server with RTT_ENABLED=true.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
text | string | required | UTF-8 text to send. May be one or more characters and may include T.140 control codes (e.g. backspace U+0008, CR/LF). |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Enable RTT reception on a leg
Allow this leg to receive RTT text broadcast from other legs in the same room and to emit rtt.received events for incoming text. Default state for new legs.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Disable RTT reception on a leg
Block this leg from receiving RTT text broadcast from other legs in the same room and suppress rtt.received events for this leg.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Start audio playback to a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
url | string(uri) | required | URL of the audio file (mutually exclusive with tone) |
tone | string | required | Built-in telephone tone name. Format: {country}_{type} or bare {type} (defaults to US). Types: ringback, busy, dial, congestion. Countries: us, gb, de, fr, au, jp, it, in, br, pl, ru. Examples: us_ringback, gb_busy, dial. |
mime_type | string | required | MIME type (e.g. audio/wav). Required when using url. |
repeat | integer | required | Number of times to repeat playback (url only) 0 |
volume | integer | required | Volume adjustment in dB (-8 to 8) 0 |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Change the volume of an active leg playback
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
playbackID | path | string | required | Playback ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
volume | integer | required | Volume adjustment (-8 to 8, ~3dB per step, 0 = unchanged) |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Stop audio playback on a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
playbackID | path | string | required | Playback ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Synthesize speech and play it on a leg
Synthesizes the provided text using the configured TTS provider and plays the audio on the leg. When TTS_CACHE_ENABLED=true, identical requests (same text, voice, model, language, and prompt) are stored on disk in TTS_CACHE_DIR and persist across restarts, without calling the external provider.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
text | string | required | Text to synthesize |
voice | string | required | Provider-specific voice identifier. ElevenLabs: voice name or ID. AWS Polly: voice ID (e.g. Joanna, Matthew). Google Cloud: voice name — either full format (e.g. en-US-Neural2-F) or short name for Gemini models (e.g. Achernar, Kore). Deepgram: model name (e.g. aura-2-asteria-en). |
model_id | string | required | Provider-specific model/engine. ElevenLabs: model ID. AWS Polly: engine (standard, neural, long-form, generative; default neural). Google Cloud: model name (e.g. gemini-2.5-pro-tts, chirp3-hd). |
language | string | optional | Language code (e.g. "en-US", "pl-pl"). Required for Google Gemini TTS voices that use short names (e.g. Achernar). Auto-extracted from full voice names like en-US-Neural2-F. |
prompt | string | optional | Style/tone instruction for promptable voice models (Google Gemini TTS only). E.g. "Read aloud in a warm, welcoming tone." |
volume | integer | required | Volume adjustment in dB (-8 to 8) 0 |
provider | enum | optional | TTS provider: "elevenlabs" (default), "aws", "google", or "deepgram" Values: elevenlabs, aws, google, deepgram |
api_key | string | optional | ElevenLabs: API key override (falls back to ELEVENLABS_API_KEY env var). AWS: optional ACCESS_KEY:SECRET_KEY override (falls back to default AWS credential chain). Google Cloud: optional API key override (falls back to Application Default Credentials). Deepgram: API key override (falls back to DEEPGRAM_API_KEY env var). |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Synthesize speech and hold it for a later commit
Stages a speculative reply. The audio is synthesized and buffered in memory but not played, so that committing it starts playback with no synthesis delay. Intended for the turn-taking loop of a voice agent: start a draft reply on the eager_end_of_turn stt.turn event, then commit it on end_of_turn or discard it on turn_resumed. A tts.staged event reports when the audio is ready; synthesis failures are reported on tts.error. Staged utterances are dropped after TTS_PREFLIGHT_TTL or when the leg ends, and at most TTS_PREFLIGHT_MAX_PER_LEG may be staged on one leg at a time. Preflight is leg-scoped; use POST /rooms/{id}/tts for room announcements.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
text | string | required | Text to synthesize |
voice | string | required | Provider-specific voice identifier. ElevenLabs: voice name or ID. AWS Polly: voice ID (e.g. Joanna, Matthew). Google Cloud: voice name — either full format (e.g. en-US-Neural2-F) or short name for Gemini models (e.g. Achernar, Kore). Deepgram: model name (e.g. aura-2-asteria-en). |
model_id | string | required | Provider-specific model/engine. ElevenLabs: model ID. AWS Polly: engine (standard, neural, long-form, generative; default neural). Google Cloud: model name (e.g. gemini-2.5-pro-tts, chirp3-hd). |
language | string | optional | Language code (e.g. "en-US", "pl-pl"). Required for Google Gemini TTS voices that use short names (e.g. Achernar). Auto-extracted from full voice names like en-US-Neural2-F. |
prompt | string | optional | Style/tone instruction for promptable voice models (Google Gemini TTS only). E.g. "Read aloud in a warm, welcoming tone." |
volume | integer | required | Volume adjustment in dB (-8 to 8) 0 |
provider | enum | optional | TTS provider: "elevenlabs" (default), "aws", "google", or "deepgram" Values: elevenlabs, aws, google, deepgram |
api_key | string | optional | ElevenLabs: API key override (falls back to ELEVENLABS_API_KEY env var). AWS: optional ACCESS_KEY:SECRET_KEY override (falls back to default AWS credential chain). Google Cloud: optional API key override (falls back to Application Default Credentials). Deepgram: API key override (falls back to DEEPGRAM_API_KEY env var). |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Play a staged TTS utterance
Starts playback of an utterance staged by the preflight endpoint. Returns immediately, before synthesis has necessarily finished; failure is reported asynchronously on tts.error, exactly as for POST /legs/{id}/tts. Once committed, stop playback with DELETE /legs/{id}/play/{playbackID} using the same tts_id.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
ttsID | path | string | required |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Drop a staged TTS utterance without playing it
Discards an utterance staged by the preflight endpoint, aborting synthesis if it is still in flight. Already-committed utterances are stopped with DELETE /legs/{id}/play/{playbackID} instead.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
ttsID | path | string | required |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Start recording a leg to a WAV file
For SIP legs, recording is stereo (left=incoming, right=outgoing). For legs in a room, stereo at 16kHz (left=participant audio, right=mixed-minus-self).
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
storage | enum | required | "file" (default) — local disk, "s3" — upload to S3 after recording stops, "gcs" — upload to Google Cloud Storage via the native GCS API (Application Default Credentials / Workload Identity) Values: file, s3, gcs |
multi_channel | boolean | required | When true, record each participant to a separate mono WAV file in addition to the full mix. Only applies to room recordings. false |
s3_bucket | string | required | S3 bucket name. Overrides S3_BUCKET env var. Required if env var is not set. |
s3_region | string | required | AWS region. Overrides S3_REGION env var. Default us-east-1. |
s3_endpoint | string | required | Custom S3 endpoint (MinIO, etc.). Overrides S3_ENDPOINT env var. |
s3_prefix | string | required | Key prefix (e.g. recordings/). Overrides S3_PREFIX env var. |
s3_access_key | string | required | AWS access key ID. Overrides default credential chain. |
s3_secret_key | string | required | AWS secret access key. Must be set together with s3_access_key. |
gcs_bucket | string | required | GCS bucket name. Overrides GCS_BUCKET env var. Required if env var is not set when storage=gcs. |
gcs_object_name_prefix | string | required | Object name prefix (e.g. recordings or recordings/). Overrides GCS_OBJECT_NAME_PREFIX env var. A trailing slash is added automatically when missing. |
filename | string | required | Optional output basename for the WAV file. A .wav suffix is added when missing. Must be a single path segment (no directories). Dots inside the name are preserved (only a trailing .wav is treated as the extension). Rejected with 409 if the file already exists or another recording is using the same name. When omitted, a timestamped name is generated. |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Stop recording a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Pause a leg recording
Replaces incoming audio with silence on the active recording until /record/resume is called. The WAV's timeline is preserved (silent gap where audio was paused), so reviewers can see exactly when sensitive data was excluded. Idempotent: calling while already paused returns status: already_paused.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Resume a paused leg recording
Resumes writing real audio after a prior /record/pause. Idempotent: calling while not paused returns status: not_paused.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Start real-time speech-to-text on a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
language | string | required | Language code (e.g. "en", "es") |
partial | boolean | required | Emit partial (non-final) transcripts false |
provider | enum | optional | STT provider: "elevenlabs" (default), "deepgram" (/v1/listen), "deepgram_flux" (/v2/listen, conversational turn detection) or "azure" Values: elevenlabs, deepgram, deepgram_flux, azure |
api_key | string | optional | API key override (falls back to ELEVENLABS_API_KEY, DEEPGRAM_API_KEY or AZURE_SPEECH_KEY env var depending on provider) |
model | string | optional | Provider-specific model. Deepgram: default "nova-3". Deepgram Flux: "flux-general-en" (default) or "flux-general-multi". |
keyterms | array[string] | optional | Terms to boost recognition of (Deepgram and Deepgram Flux). |
endpointing | integer | optional | Deepgram only: milliseconds of silence before a segment is finalized. 0 disables endpointing. |
utterance_end_ms | integer | optional | Deepgram only: milliseconds of silence after which an stt.turn event with event=utterance_end is emitted. Deepgram requires interim results for this, which are requested automatically and still suppressed unless partial is true. |
eager_eot_threshold | number | optional | Deepgram Flux only: end-of-turn confidence that fires an eager_end_of_turn stt.turn event, enabling speculative generation. Must be between 0.3 and 0.9. When unset, no eager_end_of_turn or turn_resumed events are emitted at all. |
eot_threshold | number | optional | Deepgram Flux only: end-of-turn confidence required to close a turn. Deepgram default 0.7. |
eot_timeout_ms | integer | optional | Deepgram Flux only: milliseconds of silence after which a turn is closed regardless of confidence. Deepgram default 5000. |
language_hints | array[string] | optional | Deepgram Flux only: candidate language codes for the "flux-general-multi" model. |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Stop speech-to-text on a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Flush the STT buffer on a leg without stopping STT
Forces the provider to emit a final transcript for the audio buffered so far while the session keeps running, so a caller that knows the speaker has finished does not have to wait for the provider's own endpointing. Only the deepgram provider supports this; deepgram_flux, azure and elevenlabs answer 501 — /v2/listen has no flush message, and Flux reports turn ends itself on stt.turn. The flushed transcript arrives on the usual stt.text event with is_final true — a segment containing no speech produces no event at all, so do not block on one.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Attach an ElevenLabs ConvAI agent to a leg
Bridges audio bidirectionally with an ElevenLabs conversational AI agent. Standalone legs use direct audio; legs in a room use mixer taps.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
agent_id | string | required | ElevenLabs agent ID |
first_message | string | optional | Override the agent's first message |
language | string | optional | Language code (e.g. "en", "es") |
dynamic_variables | object | optional | Key-value pairs passed to the agent as dynamic variables |
api_key | string | optional | API key override (falls back to ELEVENLABS_API_KEY env var) |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Attach a VAPI agent to a leg
Bridges audio bidirectionally with a VAPI conversational AI agent. Standalone legs use direct audio; legs in a room use mixer taps.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
assistant_id | string | required | VAPI assistant ID |
first_message | string | optional | Override the agent's first message |
variable_values | object | optional | Key-value pairs passed as VAPI variable values (assistantOverrides.variableValues) |
api_key | string | optional | API key override (falls back to VAPI_API_KEY env var) |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Attach a Pipecat bot to a leg
Bridges audio bidirectionally with a self-hosted Pipecat bot via WebSocket. Standalone legs use direct audio; legs in a room use mixer taps.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
websocket_url | string(uri) | required | WebSocket URL of the Pipecat bot (e.g. ws://my-bot:8765) |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Attach a Deepgram Voice Agent to a leg
Bridges audio bidirectionally with a Deepgram Voice Agent. Standalone legs use direct audio; legs in a room use mixer taps.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
settings | object | optional | Full Deepgram agent settings object (agent.listen, agent.think, agent.speak, etc.). When omitted, sensible defaults are used (nova-3 STT, gpt-4o-mini LLM, aura-2-asteria-en TTS). |
greeting | string | optional | Agent greeting message |
language | string | optional | Language code (e.g. "en", "es") |
api_key | string | optional | API key override (falls back to DEEPGRAM_API_KEY env var) |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Inject a message into a running agent session on a leg
Sends a context message or instruction to the running agent. Supported by Deepgram (InjectAgentMessage), Pipecat (TextFrame), and VAPI (control URL). Returns 501 for ElevenLabs.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
message | string | required | Context or instruction to inject into the running agent session |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Detach the agent from a leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Start answering machine detection on a connected leg
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
initial_silence_timeout | integer | optional | Max milliseconds of silence before declaring no_speech 2500 |
greeting_duration | integer | optional | Speech duration threshold (ms) above which answerer is classified as machine 1500 |
after_greeting_silence | integer | optional | Silence duration (ms) after initial speech to declare human 800 |
total_analysis_time | integer | optional | Max analysis window in milliseconds. A threshold longer than this window suppresses that verdict; a window shorter than all of initial_silence_timeout, greeting_duration and after_greeting_silence is rejected, since the call could only end not_sure. 5000 |
minimum_word_length | integer | optional | Minimum speech burst duration (ms) to count as a word 100 |
beep_timeout | integer | optional | Max time (ms) to wait for the voicemail beep after machine detection. 0 or omitted = disabled. 0 |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
status | string | required |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Get a SIPREC recording session
Returns the RFC 7865 recording metadata of an inbound SIPREC session (leg type siprec_in): every recorded participant, every negotiated media stream, and the binding between them. A stream's a=label is what ties the m= section to a participant, so the streams entries carry both the leg stream ID and the participant identity. The raw metadata document is returned verbatim in metadata.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
leg_id | string | required | Leg carrying the recording session. |
session_id | string | optional | Communication session being recorded, from the metadata's sessionrecordingassoc. |
data_mode | enum | optional | Data mode of the most recently applied metadata document (RFC 7865 §6.1). Values: complete, partial |
room_id | string | optional | Room this session's streams were attached to, when SIPREC_ROOM_MODE placed them in one. |
participants | array[object] | required | Every party currently recorded by this session. |
streams | array[object] | required | Every negotiated media stream, joined to the participant it carries. |
metadata | string | optional | The raw rs-metadata XML document as most recently received. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Fork a single call to an external SIPREC recording server
Originates a SIPREC recording session (RFC 7866) carrying one call as two sendonly sections: what the far end says, and what this server sends them. No room is involved. leg_ids is ignored here — the two sections are fixed. Returns the resulting siprec_out leg; delete it to end the session. Requires SIPREC_SRC_ENABLED=true.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
srs_uri | string | required | SIP URI of the session recording server, e.g. "sip:[email protected]:5060". A recording session carries the metadata document alongside the SDP and exceeds the UDP message limit, so the target should accept TCP. |
leg_ids | array[string] | optional | Which participants to record. Each entry is either a leg ID (that leg's own audio) or "<legID>#<streamID>" for one of a leg's secondary audio streams mixed into the room. Empty or absent records every participant. An entry that is not in the room is a 404. |
session_id | string | optional | Communication session identifier put in the recording metadata. Defaults to the room ID. |
app_id | string | optional | Application identifier tagged onto the resulting leg and its events. |
auth_username | string | optional | SIP digest username, when the recording server challenges the INVITE. |
auth_password | string | optional | SIP digest password, when the recording server challenges the INVITE. |
headers | object | optional | Extra SIP headers to include in the INVITE. Require: siprec is always sent. |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
id | string | required | Unique leg identifier (UUID) |
type | enum | required | Leg type Values: sip_inbound, sip_outbound, webrtc, whatsapp_in, whatsapp_out, websocket_in, websocket_out, moq_in, livekit_publish, livekit_participant |
state | enum | required | Leg state Values: ringing, early_media, connected, held, hung_up |
room_id | string | optional | Room ID if the leg is in a room, empty otherwise |
muted | boolean | required | Whether the leg is muted (cannot be heard by others) |
deaf | boolean | required | Whether the leg is deaf (cannot hear others) |
accept_dtmf | boolean | required | Whether the leg receives DTMF digits broadcast from other legs in the same room. Defaults to true. |
held | boolean | required | Whether the call is on hold (SIP legs only) |
role | string | optional | Routing role used by the room's audio routing matrix (e.g. "customer", "agent", "supervisor"). Empty string means unroled (full mesh). |
app_id | string | optional | Application identifier for event stream filtering. |
sip_headers | object | optional | Deprecated: X-* headers from the inbound INVITE. Only present on sip_inbound legs. Use `headers` for new code; it carries the same map plus surfaces handshake headers for websocket legs. |
headers | object | optional | Custom protocol headers exposed by the leg's transport — X-/P- headers from a SIP INVITE, the WebSocket upgrade request, or supplied at outbound dial time. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
List a leg's audio streams
Returns every negotiated m=audio section on the leg, in m-line order. A single-stream call has exactly one entry, the primary.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Add an audio stream to a live call
Negotiates an additional m=audio section with a re-INVITE (RFC 3264 §8.1). The new section is appended below the existing ones and binds its own RTP port. Use it to carry a second independent audio stream — a translated feed, for example — alongside the original. Set content to "alt" and lang to the feed's language so the peer can tell them apart. A peer that answers the new section with port 0 leaves the call untouched and this returns 409.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
direction | enum | optional | Media direction for the new stream, from this server's point of view. Defaults to sendrecv. "sendrecv"Values: sendrecv, sendonly, recvonly, inactive |
lang | string | optional | BCP 47 language tag advertised as a=lang (RFC 8866), e.g. "es-ES" for a Spanish translation feed. |
content | enum | optional | Value advertised as a=content (RFC 4796). Use "main" for original audio and "alt" for an alternative feed such as a translation. Values: main, alt, speaker, slides, sl |
label | string | optional | Value advertised as a=label (RFC 4574), for correlating the stream with external metadata. |
room_id | string | optional | If set, attach the new stream to this room once it is negotiated. |
role | string | optional | Routing role to apply when room_id is set. |
Responses
| Field | Type | Description | |
|---|---|---|---|
id | string | required | Stream identifier, stable for the life of the dialog. The primary stream is always "0". |
mid | string | optional | The stream's SDP a=mid token (RFC 5888), used to correlate it across offer/answer. |
index | integer | required | Position of the stream's m= line in the SDP. Fixed for the life of the dialog (RFC 3264 §8). |
primary | boolean | required | True for the call's main bidirectional audio stream, which cannot be removed or attached to a room independently. |
state | enum | required | Negotiation state. Values: pending, active, removed |
direction | enum | required | Negotiated media direction from this server's point of view. Values: sendrecv, sendonly, recvonly, inactive |
desired_direction | enum | optional | Direction requested by the application. Survives hold/unhold, unlike the negotiated direction. Values: sendrecv, sendonly, recvonly, inactive |
codec | string | optional | Codec negotiated for this stream. Streams on one leg may use different codecs. |
sample_rate | integer | optional | Native sample rate of the stream's codec, in Hz. |
local_port | integer | optional | Local RTP port. Each stream binds its own port; a shared transport is undefined without BUNDLE (RFC 9143). |
remote_addr | string | optional | Remote RTP address media is currently sent to. |
label | string | optional | The stream's a=label value (RFC 4574), for correlating it with external metadata. |
content | string | optional | The stream's a=content value (RFC 4796), e.g. "main" for original audio and "alt" for a translated feed. |
lang | string | optional | The stream's a=lang value (RFC 8866): a BCP 47 language tag such as "en" or "es-ES". |
room_id | string | optional | Room this stream's audio is mixed into. A secondary stream may sit in a different room than its leg. |
role | string | optional | Routing role of this stream within its room. Streams carry their own role, independent of their leg's. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Get one of a leg's audio streams
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
streamId | path | string | required |
Responses
| Field | Type | Description | |
|---|---|---|---|
id | string | required | Stream identifier, stable for the life of the dialog. The primary stream is always "0". |
mid | string | optional | The stream's SDP a=mid token (RFC 5888), used to correlate it across offer/answer. |
index | integer | required | Position of the stream's m= line in the SDP. Fixed for the life of the dialog (RFC 3264 §8). |
primary | boolean | required | True for the call's main bidirectional audio stream, which cannot be removed or attached to a room independently. |
state | enum | required | Negotiation state. Values: pending, active, removed |
direction | enum | required | Negotiated media direction from this server's point of view. Values: sendrecv, sendonly, recvonly, inactive |
desired_direction | enum | optional | Direction requested by the application. Survives hold/unhold, unlike the negotiated direction. Values: sendrecv, sendonly, recvonly, inactive |
codec | string | optional | Codec negotiated for this stream. Streams on one leg may use different codecs. |
sample_rate | integer | optional | Native sample rate of the stream's codec, in Hz. |
local_port | integer | optional | Local RTP port. Each stream binds its own port; a shared transport is undefined without BUNDLE (RFC 9143). |
remote_addr | string | optional | Remote RTP address media is currently sent to. |
label | string | optional | The stream's a=label value (RFC 4574), for correlating it with external metadata. |
content | string | optional | The stream's a=content value (RFC 4796), e.g. "main" for original audio and "alt" for a translated feed. |
lang | string | optional | The stream's a=lang value (RFC 8866): a BCP 47 language tag such as "en" or "es-ES". |
room_id | string | optional | Room this stream's audio is mixed into. A secondary stream may sit in a different room than its leg. |
role | string | optional | Routing role of this stream within its room. Streams carry their own role, independent of their leg's. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Change an audio stream's routing role
Updates the stream's role in place and, when the stream is mixed into a room, recomputes that room's matrix-derived allow-sets atomically (single mixer-mutex acquisition), so no audio bleeds through mid-change. Emits leg.stream_role_changed and room.routing_changed with reason: leg_stream_role_changed.
Only the role is mutable here. The SDP-level attributes — direction, lang, content, label — are fixed when the stream is negotiated; change them by removing the stream and adding a new one.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
streamId | path | string | required |
Request Body
| Field | Type | Description | |
|---|---|---|---|
role | string | optional | New routing role for this stream inside its room. The room's routing matrix decides who hears it. Pass an empty string to clear the role (full mesh). Omit to leave it untouched. Applied atomically — the room's allow-sets are recomputed in a single mixer-mutex acquisition, so no audio bleeds through mid-change. |
Responses
| Field | Type | Description | |
|---|---|---|---|
id | string | required | Stream identifier, stable for the life of the dialog. The primary stream is always "0". |
mid | string | optional | The stream's SDP a=mid token (RFC 5888), used to correlate it across offer/answer. |
index | integer | required | Position of the stream's m= line in the SDP. Fixed for the life of the dialog (RFC 3264 §8). |
primary | boolean | required | True for the call's main bidirectional audio stream, which cannot be removed or attached to a room independently. |
state | enum | required | Negotiation state. Values: pending, active, removed |
direction | enum | required | Negotiated media direction from this server's point of view. Values: sendrecv, sendonly, recvonly, inactive |
desired_direction | enum | optional | Direction requested by the application. Survives hold/unhold, unlike the negotiated direction. Values: sendrecv, sendonly, recvonly, inactive |
codec | string | optional | Codec negotiated for this stream. Streams on one leg may use different codecs. |
sample_rate | integer | optional | Native sample rate of the stream's codec, in Hz. |
local_port | integer | optional | Local RTP port. Each stream binds its own port; a shared transport is undefined without BUNDLE (RFC 9143). |
remote_addr | string | optional | Remote RTP address media is currently sent to. |
label | string | optional | The stream's a=label value (RFC 4574), for correlating it with external metadata. |
content | string | optional | The stream's a=content value (RFC 4796), e.g. "main" for original audio and "alt" for a translated feed. |
lang | string | optional | The stream's a=lang value (RFC 8866): a BCP 47 language tag such as "en" or "es-ES". |
room_id | string | optional | Room this stream's audio is mixed into. A secondary stream may sit in a different room than its leg. |
role | string | optional | Routing role of this stream within its room. Streams carry their own role, independent of their leg's. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Remove an audio stream from a live call
Disables the stream with a re-INVITE carrying port 0 for its section (RFC 3264 §8.2) and releases its RTP port. The m-line slot survives as a tombstone — the m-line count never decreases for the life of a dialog — so a later added stream takes a new position. The primary stream carries the call and cannot be removed.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
streamId | path | string | required |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Mix an audio stream into a room
Attaches one of the leg's secondary streams to a room, which may differ from the leg's own room — that is what lets an original audio stream and a translated one be mixed separately. A leg never hears its own other streams, whatever the routing matrix says.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
streamId | path | string | required |
Request Body
| Field | Type | Description | |
|---|---|---|---|
room_id | string | required | Room to mix this stream into. May differ from the leg's own room. |
role | string | optional | Routing role for the stream inside that room. The room's routing matrix decides who hears it. |
Responses
| Field | Type | Description | |
|---|---|---|---|
id | string | required | Stream identifier, stable for the life of the dialog. The primary stream is always "0". |
mid | string | optional | The stream's SDP a=mid token (RFC 5888), used to correlate it across offer/answer. |
index | integer | required | Position of the stream's m= line in the SDP. Fixed for the life of the dialog (RFC 3264 §8). |
primary | boolean | required | True for the call's main bidirectional audio stream, which cannot be removed or attached to a room independently. |
state | enum | required | Negotiation state. Values: pending, active, removed |
direction | enum | required | Negotiated media direction from this server's point of view. Values: sendrecv, sendonly, recvonly, inactive |
desired_direction | enum | optional | Direction requested by the application. Survives hold/unhold, unlike the negotiated direction. Values: sendrecv, sendonly, recvonly, inactive |
codec | string | optional | Codec negotiated for this stream. Streams on one leg may use different codecs. |
sample_rate | integer | optional | Native sample rate of the stream's codec, in Hz. |
local_port | integer | optional | Local RTP port. Each stream binds its own port; a shared transport is undefined without BUNDLE (RFC 9143). |
remote_addr | string | optional | Remote RTP address media is currently sent to. |
label | string | optional | The stream's a=label value (RFC 4574), for correlating it with external metadata. |
content | string | optional | The stream's a=content value (RFC 4796), e.g. "main" for original audio and "alt" for a translated feed. |
lang | string | optional | The stream's a=lang value (RFC 8866): a BCP 47 language tag such as "en" or "es-ES". |
room_id | string | optional | Room this stream's audio is mixed into. A secondary stream may sit in a different room than its leg. |
role | string | optional | Routing role of this stream within its room. Streams carry their own role, independent of their leg's. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Remove an audio stream from its room
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
streamId | path | string | required |
Responses
| Field | Type | Description | |
|---|---|---|---|
id | string | required | Stream identifier, stable for the life of the dialog. The primary stream is always "0". |
mid | string | optional | The stream's SDP a=mid token (RFC 5888), used to correlate it across offer/answer. |
index | integer | required | Position of the stream's m= line in the SDP. Fixed for the life of the dialog (RFC 3264 §8). |
primary | boolean | required | True for the call's main bidirectional audio stream, which cannot be removed or attached to a room independently. |
state | enum | required | Negotiation state. Values: pending, active, removed |
direction | enum | required | Negotiated media direction from this server's point of view. Values: sendrecv, sendonly, recvonly, inactive |
desired_direction | enum | optional | Direction requested by the application. Survives hold/unhold, unlike the negotiated direction. Values: sendrecv, sendonly, recvonly, inactive |
codec | string | optional | Codec negotiated for this stream. Streams on one leg may use different codecs. |
sample_rate | integer | optional | Native sample rate of the stream's codec, in Hz. |
local_port | integer | optional | Local RTP port. Each stream binds its own port; a shared transport is undefined without BUNDLE (RFC 9143). |
remote_addr | string | optional | Remote RTP address media is currently sent to. |
label | string | optional | The stream's a=label value (RFC 4574), for correlating it with external metadata. |
content | string | optional | The stream's a=content value (RFC 4796), e.g. "main" for original audio and "alt" for a translated feed. |
lang | string | optional | The stream's a=lang value (RFC 8866): a BCP 47 language tag such as "en" or "es-ES". |
room_id | string | optional | Room this stream's audio is mixed into. A secondary stream may sit in a different room than its leg. |
role | string | optional | Routing role of this stream within its room. Streams carry their own role, independent of their leg's. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
Change a leg's routing role
Updates the leg's routing role and, if the leg is currently in a room, recomputes the room's matrix-derived allow-sets atomically (single mixer-mutex acquisition). The next mix tick (≤ 20 ms) reflects the change.
Parameters
| Name | In | Type | Description | |
|---|---|---|---|---|
id | path | string | required | Leg ID |
Request Body
| Field | Type | Description | |
|---|---|---|---|
role | string | required | New routing role for the leg. The room's routing matrix decides which other legs this leg hears and is heard by based on roles. Pass an empty string to clear the role (full mesh). |
Responses
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
id | string | required | Unique leg identifier (UUID) |
type | enum | required | Leg type Values: sip_inbound, sip_outbound, webrtc, whatsapp_in, whatsapp_out, websocket_in, websocket_out, moq_in, livekit_publish, livekit_participant |
state | enum | required | Leg state Values: ringing, early_media, connected, held, hung_up |
room_id | string | optional | Room ID if the leg is in a room, empty otherwise |
muted | boolean | required | Whether the leg is muted (cannot be heard by others) |
deaf | boolean | required | Whether the leg is deaf (cannot hear others) |
accept_dtmf | boolean | required | Whether the leg receives DTMF digits broadcast from other legs in the same room. Defaults to true. |
held | boolean | required | Whether the call is on hold (SIP legs only) |
role | string | optional | Routing role used by the room's audio routing matrix (e.g. "customer", "agent", "supervisor"). Empty string means unroled (full mesh). |
app_id | string | optional | Application identifier for event stream filtering. |
sip_headers | object | optional | Deprecated: X-* headers from the inbound INVITE. Only present on sip_inbound legs. Use `headers` for new code; it carries the same map plus surfaces handshake headers for websocket legs. |
headers | object | optional | Custom protocol headers exposed by the leg's transport — X-/P- headers from a SIP INVITE, the WebSocket upgrade request, or supplied at outbound dial time. |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |
| Field | Type | Description | |
|---|---|---|---|
instance_id | string | optional | Instance identifier |
error | string | required | Error message |