Skip to main content
Beta: Realtime conversations over WebRTC are in beta. Connect a browser or other WebRTC client directly to a Telnyx AI Assistant. Media travels as WebRTC tracks: the caller’s microphone (and optionally camera) to Telnyx, the assistant’s voice (and optionally its avatar video) back. Conversation events travel as JSON on a WebRTC data channel named oai-events. The assistant’s behavior — instructions, model, voice, tools, transcription and interruption settings — is configured on the assistant itself. The WebRTC connection carries media and events only. Use WebRTC for browser and mobile clients. For a server-side integration that streams PCM audio, use the conversation WebSocket instead.

How a call is set up

  1. The browser creates an RTCPeerConnection that relays through Telnyx TURN, adds the microphone track, the oai-events data channel and, for an avatar or camera, a video transceiver. It waits for ICE gathering to complete.
  2. The browser sends the SDP offer to the application server.
  3. The application server forwards the offer to POST /v2/ai/assistants/{assistant_id}/calls with its Telnyx API key. The API key never reaches the browser.
  4. Telnyx starts a conversation with the assistant and returns the SDP answer.
  5. The application server returns the answer to the browser, which applies it.
  6. Media and the data channel connect. The conversation, including the assistant’s greeting, starts once the oai-events data channel opens.

Migrate an existing avatar client

Replace the avatar WebSocket connection and its signaling frames with the call setup above. Do not send avatar.session.ready, avatar.webrtc.offer, avatar.ice_candidate, avatar.webrtc.answer, or avatar.media.ready over the data channel. Mid-call renegotiation uses the separate webrtc.offer and webrtc.answer events described below. The greeting starts when the data channel opens, rather than waiting for the previous avatar.media.ready playback acknowledgement. Handle rejected media-element play() promises and provide a user action to start playback. An open data channel does not establish that the browser is playing the media; autoplay restrictions can prevent the user from hearing the beginning of the greeting.

Configure ICE servers

Telnyx relays the call’s media only through Telnyx TURN, and Telnyx TURN relays only between Telnyx addresses. Configure the client to relay through Telnyx TURN with iceTransportPolicy: "relay". Direct connections and third-party TURN servers do not connect.
Telnyx TURN requires credentials. See STUN/TURN servers for the endpoints and how to obtain credentials. These credentials authenticate the browser to the relay; they are separate from the Telnyx API key used by the application server. The configuration above requires access to turn.telnyx.com on UDP or TCP port 3478. TCP on port 3478 is not an HTTPS/443 fallback. If a firewall or VPN blocks both transports, the call cannot connect even when HTTPS requests succeed. Handle ICE gathering and connection failures in the client, and release local media tracks when setup fails.

Create the offer

Add the tracks and the data channel before creating the offer, then wait for ICE gathering to complete: Telnyx does not accept trickled candidates, so the offer must carry them.
The offer must include: All m-lines must share one transport (a=group:BUNDLE), as browsers do by default.

Create the call

The application server forwards the offer with its Telnyx API key:
Optional query parameter: Send the offer as a multipart/form-data body with an sdp field, or as a raw application/sdp body. The multipart form also accepts an optional session field to pass dynamic variables for this conversation:
A successful request returns 201 Created with the SDP answer as an application/sdp body. Return the answer to the browser unchanged. Every request to the relay starts a call billed to the Telnyx account. Authenticate and authorize the browser’s user before forwarding the offer, and rate-limit calls per user.
Client-supplied dynamic variables override same-named assistant defaults or values returned by the dynamic-variable webhook. Unspecified variables keep those values.

Errors

Errors return a JSON error object with error.code:

Exchange events on the data channel

The oai-events data channel carries the same JSON events as the conversation WebSocket, with these differences: Every other event works as on the WebSocket, including speech detection and transcripts, text turns, interruption and barge-in, response.cancel, tool calls and vision frames. Keep each serialized message below 1 MiB and within the connection’s negotiated pc.sctp.maxMessageSize. The SCTP limit can be smaller than the Telnyx event limit. Measure the UTF-8 bytes of the entire JSON string, including any base64 image and metadata, before calling send(). The assistant’s voice must stream PCM at 8, 12, 16, 24 or 48 kHz. Otherwise the call starts with an error event on the data channel:

Show an avatar

Beta: AI Assistant avatars are in beta. See the Avatars guide for configuration, voice compatibility and pricing. When the assistant has saved avatar_settings and the offer includes a video m-line that can receive VP8 (recvonly or sendrecv), the avatar’s video arrives on that video track and its voice on the audio track, already synchronized. Play both. An offer without a receivable video m-line starts a voice-only call, even when the assistant has an avatar. To keep a receivable video m-line, for example to send the camera, without the avatar, create the call with ?avatar=false; a video m-line added during the call then does not start the avatar either. The data channel reports the avatar’s state with avatar.updated:
A runtime avatar failure also sends an avatar.error event with error.fatal: false. The conversation continues; the voice switches back on the same audio track automatically.

Send the camera

To let the assistant see the caller, send the camera on the video m-line instead of recvonly. Add the camera track before creating the offer, in place of the recvonly transceiver:
The m-line is then sendrecv: the camera goes up and the avatar, if any, comes down on the same m-line. Telnyx samples the camera every 2 seconds into a JPEG of at most 1280 pixels wide, tagged camera, and buffers it for the vision tool. The assistant needs the vision tool and a vision-capable model; buffered frames do not trigger responses on their own. Image frames sent on the data channel are buffered the same way. For camera or screen snapshots sent as image events, reduce JPEG quality or image dimensions until the complete message fits both data-channel limits. Reducing quality or dimensions can make small text and fine detail harder to interpret. These message-size limits apply to image events, not to the native camera RTP track described above. Send only while the data channel is open, monitor bufferedAmount to avoid accumulating stale snapshots, and handle send() exceptions. If a frame cannot fit or be sent, surface the failure in the application’s camera-sharing UI; reducing the image and retrying are client responsibilities. A successful send() queues the message locally and does not confirm that the assistant has used the image.

Add video during a call

A call that started without a video m-line can add one. Add the transceiver, set the new offer as the local description, wait for ICE gathering, and send it on the data channel as a webrtc.offer event. Telnyx replies with a webrtc.answer event; apply it with setRemoteDescription:
If the assistant has an avatar, it starts once the new video m-line can receive it. Send one offer at a time and keep a single video m-line. A refused offer returns an error event instead:

End the call

Close the peer connection to end the call. Telnyx ends the conversation as soon as the oai-events data channel closes. When a client disappears without closing, for example after a network loss, Telnyx ends the conversation within seconds once media stops arriving.
The data channel must open within 20 seconds of the call request; otherwise Telnyx ends the call.