Skip to main content
Open a real-time voice conversation with a Telnyx AI Assistant over a single WebSocket. Your application streams microphone audio to Telnyx as base64-encoded PCM16 frames, and Telnyx streams back lifecycle events, speech-detection events, user and assistant transcripts, and the assistant’s synthesized speech. Everything about the assistant’s behavior — instructions, model, voice, language, tools, transcription and interruption settings — is configured on the assistant itself, in the Portal or through the Assistants API. The WebSocket carries only the conversation: audio in, events and audio out. There is no session configuration to manage on the socket, and no frame to request a response — the assistant owns turn-taking and answers automatically when the user finishes speaking.
Coming from the OpenAI Realtime API? The wire format is intentionally similar. See Migrate from the OpenAI Realtime API for an event-by-event mapping and the behavioral differences.

How a conversation flows

  1. Open a WebSocket connection, optionally setting the audio format through query parameters.
  2. Telnyx authenticates the connection, starts a conversation with the requested assistant, and sends a session.created frame confirming the negotiated input and output audio formats.
  3. Stream microphone audio as input_audio_buffer.append frames.
  4. Telnyx runs server-side voice-activity detection (VAD) and sends input_audio_buffer.speech_started and input_audio_buffer.speech_stopped on speech edges, followed by the user’s transcript.
  5. The assistant responds with a response.created frame, a stream of audio and transcript deltas, then response.output_audio.done and response.done.
  6. Speaking while the assistant is talking triggers barge-in: Telnyx stops the response and starts a new turn.

Connect and authenticate

Authenticate the WebSocket handshake with a Telnyx API v2 key in the Authorization header:
Browsers cannot set headers on WebSocket connections, so connect from your backend. For browser-based voice agents, use the WebRTC-based @telnyx/ai-agent-lib library instead — it handles media capture, playback, and client-side tools for you.
Audio negotiation happens at connect time through query parameters:
All frames in both directions are JSON text messages with a type field. Audio travels inside JSON frames as base64 — never as binary WebSocket frames.

Session lifecycle

The first frame Telnyx sends is session.created. It confirms the conversation has started and reports the negotiated audio formats — read the output rate from it before playing any assistant audio:
Each WebSocket connection starts a new conversation with the assistant. Turn detection is always server_vad — there is no manual turn mode to configure.
Only assistants whose voice streams raw PCM are supported over this WebSocket. Assistants whose voice outputs a compressed format (for example mp3 or opus) are rejected with an unsupported_voice_output_format error.

Handling audio

Stream microphone audio

Send audio as input_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:
There is no commit step. Keep appending audio continuously — server-side VAD detects when the user starts and stops speaking and turns the buffered audio into conversation turns automatically. A few practical rules:
  • 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_exceeded error.
  • Keep frames under 1 MiB. Larger frames are rejected with a frame_too_large error.
  • Wait for session.created before sending audio.
If your capture pipeline produces float samples (for example the Web Audio API), convert them to PCM16 before encoding:

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, and speech_stopped is never sent without a preceding speech_started:
After the user’s turn ends, Telnyx sends the transcript of what they said:

Receive assistant audio

Each assistant turn arrives as an ordered sequence of frames, correlated by response_id:
For a natural conversation, play deltas as they arrive rather than waiting for 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 with response.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:
You can also interrupt programmatically — for example when the user taps a stop button — with response.cancel:
Include 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 with conversation.item.create. The assistant answers it exactly as it would a spoken turn — including responding with audio:
There is no response.create frame — the assistant owns turn-taking and answers automatically. Items of any other shape are rejected with an invalid_item error.

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 a conversation.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 as error 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