Getting and sending WebSocket messages
Learn to handle real-time communication between Twilio and your application by sending and receiving JSON messages over a WebSocket connection. Process incoming prompt messages and respond with text or play tokens to exchange speech data, DTMF digits, and media commands.
You can use this guide for AI agents and AI/ML transcription.
See Related reference documentation to learn more about the <ConversationRelay> and WebSocket headers used in this guide.
Your WebSocket server must validate incoming messages from Twilio using the X-Twilio-Signature header. For detailed guidance on setting up signature validation, see Configure your WebSocket server. The messages that you receive from Twilio will be one of the following types:
Conversation Relay sends this message immediately after establishing the WebSocket connection. The customParameters field contains any parameters you defined using <Parameter> child elements inside your <ConversationRelay> TwiML.
1{2"type": "setup",3"sessionId": "VX00000000000000000000000000000000",4"accountSid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",5"parentCallSid": "",6"callSid": "CA00000000000000000000000000000000",7"from": "+18005550100",8"to": "+18005550101",9"forwardedFrom": "+18005550102",10"callType": "PSTN",11"callerName": "",12"direction": "inbound",13"callStatus": "RINGING",14"customParameters" : {15"callReference": "bar"16}17}
When you set applicationReconnect="true" in your TwiML, the setup message also includes the following fields:
| Field | Type | Description |
|---|---|---|
connectionType | string | initial for the first connection of the session, or reconnect when Conversation Relay re-establishes the connection after a drop. |
reconnectAttempt | integer | Included only when connectionType is reconnect. The number of reconnect attempts during the call, starting at 1. |
To learn how to handle a reconnect, see Handling connection drops.
Conversation Relay sends this message when the caller says something. The last field indicates whether the caller speech has completed based on the report from the speech-to-text provider and the configuration of this ConversationRelay session. For example, when speechTimeout is enabled, last will be sent after the specified speechTimeout is reached.
How the lang field is populated depends on your session's speech-to-text (STT) configuration:
- Automatic Language Detection (
transcriptionLanguage="multi"): The underlying STT provider uses a multilingual model to dynamically classify caller speech per utterance. The detected BCP-47 tag is then attached tolangin real time. - Static Language Setting: If a fixed language is configured for transcription (e.g.,
transcriptionLanguage="en-US"), lang echoes that configured language tag for everypromptmessage.
You can use the lang value in your application to dynamically inform your LLM system prompt or trigger Text-to-Speech (TTS) voice changes mid-conversation.
1{2"type": "prompt",3"voicePrompt": "Hi! Can you tell me about life?",4"lang": "en-US",5"last": true6}
Conversation Relay sends this message when you turn on DTMF detection and the caller presses a key.
1{2"type": "dtmf",3"digit": "1"4}
Conversation Relay sends this message when the caller interrupts TTS playback by speaking.
1{2"type": "interrupt",3"utteranceUntilInterrupt": "Life is a complex set of",4"durationUntilInterruptMs": 4605}
Conversation Relay sends this message when you subscribe to the config-updated event in the events attribute of your <ConversationRelay> TwiML. Conversation Relay sends it at session startup and whenever a configuration change takes effect.
1{2"type": "info",3"name": "configUpdated",4"value": {5"session": {6"ttsLanguage": "es-ES"7}8}9}
The value object has the same shape as a config message. To learn how Conversation Relay reports configuration changes, see Update configuration during a call.
Conversation Relay sends this message when an error occurs during the session.
1{2"type": "error",3"description": "Invalid message received, code: 64107, type: config, error: Unexpected fields in 'session': [foo]"4}
Conversation Relay validates the messages that your application sends. A message that it can't parse, or one with unknown fields or invalid values, returns error 64107. An invalid interruptible or preemptible value returns error 64101. Rejected config, hints, and language updates return an error message with a description and no code. None of these errors end the session, and Conversation Relay doesn't process the message.
Send text tokens messages to Twilio and Conversation Relay will convert them to speech. We recommend streaming text tokens as soon as they are available, rather than waiting for the entire message to be ready. This reduces latency. Use the last attribute to indicate the final token in the talk cycle.
1{2"type": "text",3"token": "Hello world!",4"last": false,5"interruptible": false,6"preemptible": false7}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal text. |
token | The text that will be converted to speech and played to the caller. Don't trim LLM tokens; the tokens should have appropriate spaces between them. | Required. Cannot be null. |
last | Whether this is the final token in the current message. Defaults to false. | Optional, can be true or false. |
lang | An optional BCP 47 language tag. Speaks this reply in a specific language without changing the session's TTS language. The lang on the first token sets the language for the whole reply. Conversation Relay ignores lang on later tokens of the same reply. See Speak one reply in another language. | Optional. An unsupported code returns error 64107. A language without TTS settings returns error 64106, and the tokens aren't spoken. |
interruptible | Whether the caller can interrupt this text by speaking or dtmf digits. When set to true, caller speech and/or dtmf digits stops TTS playback of this message. Overrides the interruptible attribute in TwiML. | Optional boolean value. |
preemptible | Whether subsequent text or play messages from your application will stop this media playback. When set to true, sending a new message replaces the current one. Overrides the preemptible attribute in TwiML. | Optional boolean value. |
To fine-tune synthesized speech, you can passthrough SSML tags within the token to provide the pronounciation of a word or an acronym, specify where pauses should be, or increase or decrease the speed of spoken text. Supported SSML tags depend on the active ttsProvider for the session.
If the ttsProvider is Google or Amazon, see the Speech Synthesis Markup Language (SSML) documentation for the list of supported tags.
If the ttsProvider is ElevenLabs and you use v3 or v4, SSML isn't supported. Use audio tags instead. See Use audio tags with ElevenLabs v3 and v4. For earlier ElevenLabs models, the language must be en-US, and only the <phoneme> tag is supported. See the ElevenLabs documentation for guidance on defining phoneme tags.
Example:
1{2"type": "text",3"token": "Hello from <phoneme alphabet=\"ipa\" ph=\"ˈtwɪlioʊ\">Twilio</phoneme>.",4"last": false,5"interruptible": false,6"preemptible": false7}
Request to play media to the caller.
1{2"type": "play",3"source": "https://api.twilio.com/cowbell.mp3",4"loop": 1,5"preemptible": false,6"interruptible": true7}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal play. |
source | The URL of the media to play. | Required. Must contain a valid URL. |
loop | Number of times to play the media. A value of 0 plays it 1,000 times (maximum). Default is 1. | Optional, integer value. |
preemptible | If set to true, subsequent text or play messages will stop this media playback. Default is false | Optional boolean value |
interruptible | Whether the caller can interrupt this media. Overrides the interruptible attribute in TwiML. | Optional boolean value |
Request to send DTMF digits to the caller. Conversation Relay sends digits as per Twilio's <Play> digits attribute.
1{2"type": "sendDigits",3"digits": "9www4085551212"4}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal sendDigits. |
digits | The DTMF digits to send. | Required. Cannot be null or empty. Must only contain the characters 0-9, w, #, and *. |
Change the transcription and TTS language during the session. This affects future TTS and STT sessions.
1{2"type": "language",3"ttsLanguage": "sv-SE",4"transcriptionLanguage": "en-US",5"hints": "faktura,saldo,betalning"6}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal language. |
ttsLanguage | The language to use for text-to-speech conversion. | Optional, but either this or transcriptionLanguage must be present. Must be one of the supported languages. |
transcriptionLanguage | The language to use for speech-to-text transcription. | Optional, but either this or ttsLanguage must be present. Must be one of the supported languages. |
hints | A comma-separated list of speech recognition hints for the transcriptionLanguage. | Optional. Requires transcriptionLanguage. |
ttsProvider, voice | Text-to-speech settings for the ttsLanguage. | Optional. Require ttsLanguage. |
transcriptionProvider, speechModel | Speech recognition settings for the transcriptionLanguage. | Optional. Require transcriptionLanguage. |
This message is shorthand for a config message that sets ttsLanguage and transcriptionLanguage, and the settings of those languages. To switch to a language that the session doesn't have, give each part that you switch in full: ttsProvider and voice, or transcriptionProvider and speechModel. See Add a language and switch to it.
Update language and session settings during the conversation without interrupting the call. An update changes only the settings that you include. Every setting that you leave out keeps its current value.
1{2"type": "config",3"language": {4"code": "en-US",5"speechModel": "nova-3-general",6"hints": "invoice,autopay"7},8"session": {9"dtmfDetection": true,10"interruptible": "speech"11}12}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal config. |
language | An object, or an array of objects, with <Language> element attributes. Set code to edit a specific language. Omit code, or use current, to edit the active language. | Optional. Each entry needs at least one setting besides code, and each code can appear once. |
session | An object with <ConversationRelay> attributes, using the same names as in TwiML. Set ttsLanguage or transcriptionLanguage to switch languages. | Optional, but the message needs at least one setting in language or session. Must not include settings that can't change during a session. |
If any value in the message is invalid, Conversation Relay rejects the entire message and the call continues with the previous configuration. For the list of settings that you can update, when each update takes effect, and how to troubleshoot errors, see Update configuration during a call.
Update the speech recognition hints for the active transcription language. This message is shorthand for a config message that sets hints on the active language.
1{2"type": "hints",3"hints": "invoice,balance,autopay,routing number"4}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal hints. |
hints | A comma-separated list of words or phrases that might appear in speech. | Required. An empty string clears the hints. |
End the session and return control of the call to Twilio through Conversation Relay.
1{2"type": "end",3"handoffData": "{\"reasonCode\":\"live-agent-handoff\", \"reason\": \"The caller wants to talk to a real person\"}"4}
| Attribute | Description | Validation rules |
|---|---|---|
type | The type of message you're sending to Twilio. | Required. Must equal end. |
handoffData | A string containing data to pass back in the <Connect> action callback. To pass structured data, use a JSON-encoded string (for example, "{\"reason\": \"transfer\"}") and parse it in your action URL handler. Do not include PCI data in this field, as it is not treated as containing PCI data. | Optional. |
For errors, such as messages that Conversation Relay doesn't understand, we will respond with an error message.
If your WebSocket sends unidentified messages to Conversation Relay and the last 10 messages remain unidentified, we will terminate the connection. The status code will be 1007 with the reason "Too many consecutive malformed messages." In that case, we will report an error 64105 "WebSocket Ended."
The following error codes are specific to Conversation Relay. The Ends session column indicates whether the error ends the ConversationRelay session.
| Error code | Description | Ends session |
|---|---|---|
| 64101 | Invalid Parameter — invalid TwiML configuration parameters. | Yes |
| 64102 | Unable to Connect to WebSocket URL — could not establish a WebSocket connection to your endpoint. | Yes |
| 64103 | Internal Server Error — an internal Twilio error affected the session. | Yes |
| 64104 | Max Call Duration Reached — the call exceeded the maximum allowed duration. | Yes |
| 64105 | WebSocket Ended — your WebSocket connection disconnected unexpectedly. | Yes |
| 64106 | Invalid Argument — a parameter is not supported by the chosen provider (for example, an unsupported language/voice combination). | Depends* |
| 64107 | Invalid Message Received — a WebSocket message could not be processed. After 10 consecutive invalid messages, the connection is closed. | No |
| 64108 | RTP Timeout — the media stream timed out due to no audio packets being received. | Yes |
| 64109 | Concurrency Limit Reached — the maximum number of concurrent sessions was temporarily reached. | Yes |
| 64110 | Account Opted Out — the account has been opted out of ConversationRelay. | Yes |
| 64111 | TTS Provider Service Error — the TTS provider returned a service error. | No |
| 64112 | TTS Conversion Error — the TTS provider failed to convert text to speech. | No |
Note: *This error code covers both warning-level and error-level events. While minor configuration issues (like DTMF detection arguments) may allow the session to continue, critical failures—such as the inability to play or synthesize audio due to invalid arguments—will terminate the session.
If the WebSocket disconnects unexpectedly and you haven't set applicationReconnect="true" in your TwiML, Conversation Relay doesn't reconnect, and the call disconnects with a failed status.
When you set applicationReconnect="true" on your <ConversationRelay> noun, Conversation Relay keeps the call active if the WebSocket drops. It buffers up to 500 inbound events (or up to 10 seconds of call history), opens a new WebSocket connection to your url, and sends a setup message with connectionType set to reconnect. It then replays the buffered events. To learn how to restore your session state, see Handling connection drops.
If you don't use applicationReconnect, or if automatic reconnection doesn't restore the session, implement reconnection logic by initiating a new <Connect><ConversationRelay> request:
- Re-establish the connection: If you lose the WebSocket connection, handle the disconnect in your
<Connect>element'sactionURL callback by returning new TwiML containing<Connect><ConversationRelay>to restore the session. - Validate call consistency in Conversation Relay: Ensure the callSid remains the same to confirm continuity of the original call session.
This approach helps maintain session stability and consistency following any connection disruptions.
This guide teaches the basics required for the following use cases:
You can use this guide to build intelligent, conversational voice agents that interact with callers in real-time. By processing WebSocket messages, your AI can understand speech and respond dynamically. To learn more advanced features that you can use with AI agents, see AI agents.
You can use this guide to capture and process real-time audio data for advanced machine learning workflows. This allows you to generate live transcriptions or perform sentiment analysis during a call.
To learn more advanced features that you can use with AI/ML transcription, see Voice AI/ML transcription.
After following this guide, you can successfully exchange real-time JSON messages between your server and Twilio via a WebSocket. You can confirm it works by verifying that your server receives a setup message upon connection and can successfully trigger text-to-speech by sending a text token message.
Explore the following guides to build on what you've learned in this guide:
- Picking a voice: Configure different text-to-speech voices and providers for your session.
- Update configuration during a call: Change language, voice, and session settings with
configmessages. - Consume a real-time Media Stream using WebSockets, Python, and Flask: Handle raw audio streams using a similar WebSocket architecture.