Skip to main content
POST /api/v1/ai-sessions/{call_id}/control is the single endpoint your voice AI uses to drive a live call. The JSON body’s event field is the discriminator — there are 25 verbs across three groups: call-state primitives (13), conferencing + supervisor (9), and lead / context (3).

Authentication

The callback token auto-rotates at minute 25 of the call via a token_refresh text frame on the WS. Agents that ignore the refresh lose control-verb access at the 30-min mark; the call continues without interruption.
ai_sessions:control_admin is opt-in. Tenant API keys do not carry control-verb permission by default. Add the scope to a key explicitly when you want to drive calls from a dashboard or test harness.

Request envelope

  • Tenant fence. call_id must belong to the token’s tenant. Cross-tenant access returns 403.
  • Rate limit. 10 req/sec per call_id.
  • Idempotency. The Yotel SDKs auto-generate an Idempotency-Key for state-changing verbs. Replays of the same (call_id, key) pair return the cached response. See Idempotency.

Response envelope

result carries verb-specific data (e.g. {"conference_id": "..."}) for verbs that have a return; empty {} otherwise.

Group 1 — Call-state primitives (13)

Operate on the AI’s own call leg.

transfer

Bridge the caller to an agent queue, an E.164 number, or a SIP URI. The AI’s leg drops; outcome ← transferred_*. Fires ai_session.transferred + ai_session.ended.
Python

hangup

End the call. outcome ← hangup; fires ai_session.ended.
TypeScript

log

Append a structured entry to ai_sessions.metadata.logs[]. No state effect, no webhook — useful for breadcrumbs in the audit log.
Python

mute / unmute

Mute one party for the rest of the call. target is "caller" (default) or "agent" (the AI’s leg).
Python

hold / unhold

Place the caller on hold; optional moh_url for music. unhold clears the state.
TypeScript

send_dtmf

Inject DTMF digits into the call (e.g. when navigating an external IVR after a transfer attempt).
Python

play_audio

Play either a pre-recorded URL or TTS text — exactly one of audio_url / tts_text must be set. See the TTS limitation below.
Python

set_disposition

Save a disposition code on the call. source is recorded as "ai".
Python

recording_pause / recording_resume

Stop/start recording mid-call (e.g. PCI compliance during card capture). Optional reason is stored on the audit log.
Python

get_call_state

Read-only snapshot of current state. No idempotency key needed.
Returns:

Group 2 — Conferencing + supervisor (9)

Add other participants to the call, monitor or barge into another call leg, or escalate to a human supervisor.

conference_start

Promote the AI’s two-leg call into a conference. The AI stays in. Fires ai_session.conference_changed.
Python

conference_add

Dial a participant into the conference. participant_type is one of e164 | sip_uri | agent | supervisor.
Python

conference_remove

Kick a participant by member_id. member_id comes from the participants[] array on ai_session.conference_changed.

conference_leave

The AI’s leg drops; the rest of the conference continues.

request_supervisor

Enqueue a supervisor escalation. UI alert + WebSocket push to on-duty supervisors. Fires ai_session.escalated twice: once on invocation (supervisor_id null), once on claim.
Python

whisper

One-way audio injection into another call leg (typically an agent’s ear during a coaching session). Requires target_call_id and an existing monitor or conference relationship — bare invocation on an unrelated call returns 403.
Python

barge

Promote from monitor-only to a full participant in target_call_id. Both sides hear the AI.

monitor_start / monitor_stop

Open or close a one-way audio fork from a target call into the AI’s WS. mode is listen (default) or listen_and_whisper.
Python

Group 3 — Lead / context (3)

Persist data back to the lead row or schedule follow-up work.

set_lead_field

Patch one custom field on the lead’s custom_fields JSONB.
Python

set_lead_status

Override the lead status. Recorded as status_set_by='ai'.
Python

schedule_callback

Insert a callbacks row. The campaign engine picks it up at scheduled_at. Optional voice_agent_id to route the callback to a different agent (e.g. escalate Tier-1 → Tier-2).
Python

Idempotency

Idempotency-Key is honored on every state-changing verb (read-only log and get_call_state ignore it). The Python and TypeScript SDKs auto-generate a UUID v4 per call so retries are safe by default; override with idempotency_key=... (Python) / idempotencyKey: ... (TS) when you want cross-process dedup. Replay window: 24h on (call_id, idempotency_key).

TTS not yet supported

play_audio and whisper accept a tts_text field in the v1 protocol, but the dispatcher currently returns 422 when that shape is sent — TTS rendering isn’t wired in v1. Use audio_url with a pre-rendered WAV/MP3 for now. TTS support is tracked for v1.1.

Error reference

See also