Quick start
/access-token endpoint. See Authenticating Client Apps for details.
Connection
Connect to the WebSocket endpoint:Protocol Overview
The WebSocket connection uses JSON messages for control events and base64-encoded audio for media. New server event types may be added over time. To keep your integration forward-compatible, ignore any event you do not recognize instead of treating it as an error. A session moves through four phases:- Start. Send a
startevent with your audio config. - Ready. The server responds with
ackonce the agent pipeline is ready. - Converse. Stream user audio as
media_inputand play agent audio frommedia_output. In parallel, the server pushes conversation events (turn_started,turn_output_text_delta,turn_ended) that describe the dialogue as it happens. - End. Either side can close the connection. The server’s close reason tells you why the call ended (see Connection Management).
call_id from the ack.
Client events
Starting the Session (start)
Initializes the audio stream configuration.
configoverrides your agent’s default input audio settingsstream_idis optional. If not provided, the server generates one and returns it in theackevent
stream_id(optional): Stream identifier. If not provided, server generates oneconfig.input_format: Audio format for client audio input (mulaw_8000,pcm_16000,pcm_24000,pcm_44100)config.output_audio_delivery(optional): How the server delivers agent audio.speaking_pace(default) paces audio to playback speed;as_availablesends it as fast as the model produces it, so a client that buffers locally can play it back on its own clock for lower latency. Not supported when the agent uses background audio.config.voice_id(optional): Override the agent’s default TTS voiceagent(optional): Allows configuring individual agent calls via API and previewing changes in introduction or prompt without publishing to productionmetadata(optional): Custom metadata object. These will be passed through to the agent code, but there are some special fields you can use as well:to(optional): Destination identifier for call routing (defaults to agent ID)from(optional): Source identifier for the call (defaults to “websocket”)
Streaming User Audio (media_input)
Audio data sent from the client to the server. payload audio data should be base64 encoded.
stream_id: Unique identifier for the Stream from the ack responsemedia.payload: Base64-encoded audio data in the format specified in the start event
Sending DTMF Tones (dtmf)
Sends DTMF (dual-tone multi-frequency) tones.
stream_id: Stream identifierdtmf: DTMF digit (0-9, *, #)
Sending Custom Metadata (custom)
Sends custom metadata to the agent.
stream_id: Stream identifiermetadata: Object containing key-value pairs of custom data
Server events
Session Ready (ack)
Sent in response to start once the agent pipeline is ready to receive audio. Confirms the stream configuration and returns the server-generated stream_id if one wasn’t provided in the start event.
call_id: Identifier of the call record created for this session. Use it with the calls API to fetch the recording and transcript after the call
Receiving Agent Audio (media_output)
The agent’s speech. payload is base64-encoded audio in the format set by config.input_format in the start event.
Conversation Events
Three events describe the conversation itself:turn_started, turn_output_text_delta, and turn_ended. Every turn gets an id from a single counter shared by both roles, starting at 1 and strictly increasing over the call, so id alone identifies a turn.
The events for a single turn always arrive in order: turn_started, then any turn_output_text_delta events, then turn_ended. The server pushes them as the conversation happens, independent of audio delivery timing. Use these events to drive transcripts and speaking indicators, and see Build a live transcript for a code example.
When a Turn Begins (turn_started)
Sent when the user starts speaking or the agent starts responding.
stream_id: Stream identifierturn_started.id: Turn index, unique across the callturn_started.role:userorassistantturn_started.start_timestamp: Seconds since the start of the call (approximately when the client receivedack)
Streaming Agent Text (turn_output_text_delta)
Sent for each word the agent speaks, as it is spoken. Use it to render the agent’s side of the transcript, word by word.
stream_id: Stream identifierturn_output_text_delta.id: Matches theidof the turn the text belongs toturn_output_text_delta.role: Alwaysassistantturn_output_text_delta.text: The exact substring to append. Separator spaces are included, so build the running text withbuffer[id] += text
turn_ended event.
When a Turn Ends (turn_ended)
Sent when a turn finishes. It carries the complete, final text for the turn, which is the version you should store and display once the turn is over.
stream_id: Stream identifierturn_ended.id: Matches theidof the correspondingturn_startedeventturn_ended.role:userorassistantturn_ended.text: The finalized text of the turn. For a user turn, this is the transcribed speech. For an assistant turn, it is the text the agent actually spoketurn_ended.was_interrupted: For assistant turns,truewhen the user interrupted the turn. For user turns,trueonly when the call ended mid-turnturn_ended.start_timestamp,turn_ended.end_timestamp: Seconds since the start of the callturn_ended.tool_calls: Tool calls made during the turn. Each entry hasname,arguments, and optionallyresultandid
turn_ended. Treat the connection close as ending any open turn.
Handling Interruptions (clear)
Indicates the agent wants to clear/interrupt the current audio stream.
Call Transfers (transfer_call)
Indicates the agent wants to transfer the call to a phone number. The client is responsible for initiating the transfer on its telephony side.
stream_id: Stream identifiertransfer.target_phone_number: E.164 phone number to transfer the call to
Build a live transcript
A live transcript shows the conversation on a UI as it happens, with the agent’s words appearing one at a time like live captions.turn_ended arrives. For an agent turn, show each word as its delta arrives so the response builds up at speaking pace.
Connection Management
Inactivity Timeout
The server closes idle connections after 180 seconds. Any client message resets the timer:- Application messages (media_input, dtmf, custom events)
- Standard WebSocket ping frames
- Any other valid WebSocket message
- Code: 1000 (Normal Closure)
- Reason:
"connection idle timeout"
Ping/Pong Keepalive
To prevent inactivity timeouts during periods of silence, use standard WebSocket ping frames for periodic keepalive:Connection Close
The connection can be closed by either the client or server using WebSocket close frames. Client-initiated close:- Code: 1000 (Normal Closure)
- Reason:
"call ended by agent"or"call ended by agent, reason: {specific_reason}"if additional context is available
Best Practices
- Send
startfirst — The connection closes if any other event is sent beforestart. - Choose the right audio format — Match the format to your source:
mulaw_8000for telephony,pcm_44100for web clients. - Handle closes cleanly — Always capture close codes and reasons for debugging and recovery.
- Keep the connection alive — Send WebSocket ping frames every 60–90 seconds to avoid the 180-second inactivity timeout.
- Manage stream IDs — Provide your own
stream_idvalues to improve observability across systems. - Recover from idle timeouts — On
1000 / connection idle timeout, reconnect and resend astartevent.