How a conversation flows
- Open a WebSocket connection, optionally setting the audio format through query parameters.
- Optionally send a
session.updateframe as your first message to pass dynamic variables for the conversation. - Telnyx authenticates the connection, starts a conversation with the requested assistant, and sends a
session.createdframe confirming the negotiated input and output audio formats. - Stream microphone audio as
input_audio_buffer.appendframes. - Telnyx runs server-side voice-activity detection (VAD) and sends
input_audio_buffer.speech_startedandinput_audio_buffer.speech_stoppedon speech edges, followed by the user’s transcript. - The assistant responds with a
response.createdframe, a stream of audio and transcript deltas, thenresponse.output_audio.doneandresponse.done. - Speaking while the assistant is talking triggers barge-in: Telnyx stops the response and starts a new turn.
Connect and authenticate
Authorization header:
Connect from a server-side WebSocket client that supports the
Authorization header. Keep the API key on your server. The examples in this guide use Node.js and Python.type field. Audio travels inside JSON frames as base64 — never as binary WebSocket frames.
Session lifecycle
The first session frame issession.created. It confirms the conversation has started and reports the negotiated audio formats — read the output rate from it before playing PCM audio:
server_vad — there is no manual turn mode to configure.
Pass dynamic variables
If the assistant’s instructions, greeting, or tools use dynamic variables, supply the values for this conversation with asession.update frame. Send it as your first frame, immediately on open — before any audio, and without waiting for session.created:
session.update starts the conversation with no client-supplied variables after a short timeout.
Client-supplied values override same-named assistant defaults or values returned by the configured dynamic-variable webhook. Unspecified variables retain those defaults or webhook values; an empty object does not clear them.
Only type is required. A bare { "type": "session.update" } — or one whose session object carries no dynamic_variables — is valid: it skips that timeout and starts the conversation with no client-supplied variables.
session.update configures nothing but dynamic variables, and only before the conversation starts. Sending it after session.created is rejected with a session_update_after_start error, and a dynamic_variables value that is not an object is rejected with invalid_session_update. The telnyx_ namespace is reserved for system variables, so telnyx_conversation_channel is set by Telnyx and cannot be overridden.Handling audio
Stream microphone audio
Send audio asinput_audio_buffer.append frames. The audio field is base64-encoded, raw little-endian PCM16 (16-bit signed, mono) at the sample rate you chose with input_sample_rate:
- Stream at a real-time pace, in small chunks (for example 20–100 ms of audio per frame). The server enforces an ingress budget; sending far faster than real time fails with an
ingress_budget_exceedederror. - Keep frames under 1 MiB. Larger frames are rejected with a
frame_too_largeerror. - Wait for
session.createdbefore sending audio.session.updateis the only frame sent before it.
Speech detection and user transcripts
Telnyx runs voice-activity detection server-side and reports speech edges. Both events are edge-triggered — each is sent only when the speaking state actually changes, andspeech_stopped is never sent without a preceding speech_started:
Receive assistant audio
Each assistant turn arrives as an ordered sequence of frames, correlated byresponse_id:
response.done. Buffer the decoded PCM16 in a queue that your audio output drains at the output sample rate.
Interruption and barge-in
Barge-in is built in. If the user speaks while the assistant is talking, Telnyx detects it, stops generating the response, and starts a new turn — you don’t send anything to make that happen. The interrupted turn finishes withresponse.done and status: "cancelled".
One thing remains your responsibility: audio you have already received and queued locally. When you see input_audio_buffer.speech_started during playback, flush your local playback queue so the assistant doesn’t keep talking out of your speakers over the user:
response.cancel:
response_id to target a specific response, or omit it to cancel the current one. Cancelling flushes playback on the Telnyx side and is followed by a response.done frame with status cancelled. Cancelling a stale or already-finished response is a no-op.
Interruption sensitivity is tuned on the assistant, not the socket — see Interruption Settings.
Send text instead of audio
Inject a completed user turn as text withconversation.item.create. The assistant answers it exactly as it would a spoken turn — including responding with audio:
response.create frame — the assistant owns turn-taking and answers automatically.
Client-side tool results use a function_call_output item (see Client-side tools). Unsupported item shapes are rejected with an invalid_item error.
Add vision with camera or screen-share frames
Beta: The vision tool lets an assistant inspect camera or screen-share snapshots during the same realtime conversation. For example, a user can show a product or share an error message while asking the assistant for help. Your client captures and sends individual images; a continuous video stream is not sent over this WebSocket. Vision does not require an avatar. Add{ "type": "vision" } to the assistant’s tools. Either the base model or its enabled delegation model must support image input. S2S assistants, including GPT Live, can inspect buffered frames through a vision-capable delegation model. Configure the tool and model before connecting; see Vision model support.
You can also start with the Avatar with vision template in the Portal for a configured assistant. Your WebSocket client still needs to capture and send image frames.
After session.created, send snapshots as conversation.item.create frames with "buffer": true and only input_image content:
item.captured_at records the capture time in ISO 8601 format; if omitted, Telnyx uses the receipt time. Image items require buffer: true; send text prompts as separate items.
Optional item.tags labels the image source, for example "tags": ["camera"] or "tags": ["screenshare"]. Tags apply to every image in the item. Send up to 5 tags, each 1–64 characters; untagged is reserved for frames sent without tags. Frames are buffered per tag. See Tag frames for tag matching and multiple-source examples.
The vision function accepts action: "look" (the default) or action: "list", and an optional tag. A look without tag retrieves the most recent frame of any tag; a look with tag retrieves the most recent matching frame. Listing returns available tags and when each last received a frame, without showing an image. For an untagged demo, instruct the assistant to call vision with action: "look" and omit tag; it does not need to list tags first.
Continue streaming microphone audio or send a text turn, such as “What does this error message mean?” Buffered images do not trigger responses. The assistant decides when to call vision and inspect recent frames while answering. Frames older than max_frame_age_secs (10 seconds by default, measured from receipt) are ignored. Keep sending frames for static content too; freshness applies per frame, including when selecting a tag. Buffered images are held in memory for the conversation and are not added to conversation history, transcripts, or webhooks.
Downscale snapshots before sending, for example to 640×480 JPEG, and start with one frame every 1–5 seconds. Keep the entire JSON message below 1 MiB, including base64 overhead. Oversized messages return frame_too_large; malformed buffered items return invalid_frame without closing the socket.
See the Vision guide for tool settings, client capture examples, other transports, and retention details, or the conversation WebSocket reference for the complete frame schema.
Tool calls
Assistants can use two kinds of tools during a realtime conversation. Both are configured on the assistant — see the Tools Library.Server-side tools: observe
Webhook and MCP tools execute on Telnyx. The socket surfaces them as informational frames so you can show tool activity in your UI, but you don’t execute or respond to them:Client-side tools: execute and respond
When the assistant invokes a client-side tool, Telnyx sends aconversation.item.created frame containing a function_call item. Run the tool locally, then return the result with a conversation.item.create frame carrying a function_call_output item that references the same call_id. The assistant continues once the result arrives, or after the tool times out.
Error handling
Errors arrive aserror frames. Some errors are non-fatal and leave the session open; others close the connection after the frame is sent.
If the connection closes, reconnecting starts a new conversation — a fresh
conversation_id is issued in session.created.
Learn more
- Conversation WebSocket reference — The full frame-by-frame reference, under Assistants API → Conversation WebSocket in the sidebar
- Vision — Configure the vision tool and stream camera or screen-share snapshots
- Client-Side Tools — Configure tools that execute in your application
- Transcription Settings and Interruption Settings — Tune how the assistant hears and yields
- Voice Assistant Quickstart — Create and configure an assistant in the Portal