Do not send audio in JSON messages. The
init, configure, flush, and server event shapes are the same across transports.
Protocol Overview
Connection Lifecycle
Key Concepts
- Init: Sent once per connection. Sets session-level parameters (sample rates, conversation ID).
- Configure: Sent after init and again at any point to change languages, voice, or glossary. Each configure starts a new pipeline configuration. The server responds with
configuredwhen ready. - Flush: Signals the server to finalize the current input and produce all remaining output. The server responds with
flushedwhen complete. Use this for manual turn-taking or to force processing of buffered audio. - Utterance: A continuous segment of speech. The server assigns an incrementing
utterance_idxto each detected utterance. When VAD (Voice Activity Detection) is active, the server automatically splits speech into utterances.
Client Messages (Client -> Server)
Client control messages are JSON objects with a"type" field. Send them as WebSocket text frames on the WebSocket transport, or as stringified JSON messages on the WebRTC data channel. Audio transport is described in Input Audio.
init
Required as the first message on every connection. Sets connection-level parameters that remain fixed for the session lifetime.
configure
Initializes or reconfigures the translation pipeline. Must be sent after init. Can be sent again mid-session to change languages, voice, glossary, or features without reconnecting.
The server responds with a
configured message when the pipeline is ready to accept audio.
Language Routes
Language routes define the translation directions the pipeline supports. Each route specifies a source and target language.
Single route — translates one direction only:
can_lang_swap behavior):
language_route event indicating which route was chosen for each utterance.
Auto-detect mode (any language to one target) — use "*" as lang_in to accept any supported language and translate it to a fixed target. The server identifies the spoken language automatically and translates to the specified output language:
language_route event with the detected language filled in:
language_route messages (is_final: false) while language identification is still refining, followed by a single is_final: true message once the route is locked in. Clients that only care about the final answer can ignore tentative messages and act on is_final: true.
You can combine auto-detect with an explicit route. For example, to translate English to Spanish and auto-detect everything else to English:
* matches any detected language. In this case, the consequence is: English speech will remain as English.
"*" -> "*" route alongside your normal routes to handle speech in unexpected languages gracefully. When the detected language does not match any explicit route, the wildcard route is selected and the pipeline enters a transcription-only mode (no translation or TTS output). This is useful for prompting the user to switch languages:
language_route event with the detected language echoed into both lang_in and lang_out. For the configuration above, if the user speaks French you will see:
transcription / transcription_ended for the utterance but no translation, translation_ended, output_audio, output_speech_started, or output_speech_ended (transcription-only mode).
You can use this language_route to show a prompt to the user, for example: “It seems like you are speaking French. Would you like to switch your language?” If the user confirms, send a new configure with the appropriate routes.
Glossary Entries
Glossary entries either (a) keep a phrase verbatim across languages or (b) lock in a specific translation between two languages. Each entry is a map of language codes to the desired term in that language. Keep a phrase verbatim across languages — use"*". For brand names, product names, and other proper nouns that should never be translated, use the "*" wildcard:
es will match all variants of Spanish (es-419 and es-ES). You are also free to specify a long code (es-419) if you need more fine-grained tuning.
Combining both. You can mix "*" with explicit language codes in the same entry. The "*" then acts as the term used whenever a route’s input or output language isn’t explicitly listed. For example, {"terms": {"*": "customer service", "es": "servicio al cliente", "fr": "service client"}} resolves Spanish → French to “service client” via the explicit codes, while Spanish → Russian falls back to “customer service” via the wildcard.
Features
Thefeatures array controls which pipeline mode is used. By default, you can leave the features array empty or omitted. Note that feature names are case-sensitive:
Input Audio
Streams audio data to the server after receivingconfigured.
Audio transport depends on the API you are using:
For WebSocket binary audio, use this format:
- Encoding: 16-bit signed integer PCM (little-endian)
- Channels: Mono (1 channel)
- Sample rate: Must match
input_sample_ratefrominit
flush
Signals the server to finalize processing of all buffered audio and produce remaining results. The server will respond with a flushed message after all output has been sent.
Server Messages (Server -> Client)
Server control and text messages are JSON objects with a"type" field. Receive them as WebSocket text frames on the WebSocket transport, or as stringified JSON messages on the WebRTC data channel. Audio transport is described in Output Audio.
Note on optional fields: Fields documented as “or null” (such as request_id, probability, utterance_idx on errors) are omitted from the JSON when not present, rather than sent as "field": null. Your client should treat a missing key the same as a null value.
configured
Confirms the pipeline is ready to accept audio after a configure message.
input_speech_started
Indicates the server’s VAD has detected the start of speech in the input audio.
language_route
Indicates which language route was selected for the current utterance. Sent when the system identifies the spoken language, particularly useful when multiple language routes are configured.
transcription
Real-time transcription of the input audio. Complete words have been finalized and will not change. Partial words are in-progress and may be updated in subsequent messages.
Word object:
transcription_ended
Indicates that all transcription for a given utterance is complete. No further transcription messages will be sent for this utterance.
translation
Translated text derived from the transcription. Like transcription, includes complete (finalized) and partial (in-progress) words.
translation_ended
Indicates that all translation for a given utterance is complete.
input_speech_ended
Indicates the server’s VAD has detected the end of speech in the input audio. After this, the server will not accept further input audio for this utterance and will finalize the translation.
output_speech_started
Indicates the server has begun sending synthesized output audio for an utterance.
Output Audio
Synthesized audio from the translated text. Audio transport depends on the API you are using:
For WebSocket binary audio, the format is:
- Encoding: 16-bit signed integer PCM (little-endian)
- Channels: Mono (1 channel)
- Sample rate: Matches
output_sample_ratespecified ininit
output_text_boundary
Timing information that maps a position in the output audio stream back to positions in the transcription and translation text. Use this to synchronize text highlighting with audio playback.
Sent by default. Clients that don’t need word-level highlighting can opt out by setting the "hide_output_text_boundary" feature in configure, which suppresses these messages.
TextPosition object:
output_speech_ended
Indicates all synthesized audio for an utterance has been sent.
flushed
Confirms that a flush has been fully processed and all pending output has been sent.
error
Indicates an error occurred during processing.
Non-fatal errors (e.g., malformed JSON, unknown message type) are sent as error messages with code
"STREAM_ERROR" but the connection remains open. You can continue sending messages after receiving an error. Errors originating from the backend translation pipeline will have their own error codes.
Examples
Simultaneous Mode (default)
In simultaneous mode, the server produces translation and audio output in real-time as the speaker talks, with minimal delay. Setup:- Send
initwith"realtime_playback": true, thenconfigure - Wait for
configured - Stream input audio over the selected transport continuously
- Receive streaming
transcription,translation, and output audio in real time - The server uses VAD to segment utterances automatically; each utterance gets its own
utterance_idx - Use
flushwhen you want to force-finalize any pending output
Consecutive Mode with VAD (Automatic End-of-Speech)
In this mode, the speaker talks, the system detects when they stop, and then produces the full translation. This is the simplest approach for turn-based conversations. Setup:- Send
init, thenconfigurewith"features": ["consecutive"] - Wait for
configured - Stream input audio over the selected transport as the speaker talks
- The server’s VAD detects end-of-speech and sends
input_speech_ended - Receive
transcription,translation, and output audio - Receive
output_speech_endedwhen all audio for the utterance has been sent - For a multi-turn conversation, send a new
configureto start the next turn (you can swap languages), or just keep streaming audio for the next utterance
Consecutive Mode with Manual Flush
Use manual flush when you want explicit control over when the system processes the audio (e.g., push-to-talk with a button release, or after a fixed recording duration). Setup:- Send
init, thenconfigurewith"features": ["consecutive"] - Wait for
configured - Stream input audio over the selected transport while the user is speaking
- When the user finishes (e.g., releases push-to-talk button), send
flush - Receive transcription, translation, and audio output
- Receive
flushedwhen all output is complete - To start the next turn, simply resume sending audio (or send a new
configureto change languages)
flushed, you can immediately stream more audio and send another flush for the next turn. You do not need to re-send init or configure (unless you want to change settings). This avoids reconnection overhead.
Auto-Detect Mode (Any Language to One Target)
Use a wildcard"*" as lang_in to automatically detect the spoken language and translate it to a fixed target. This is ideal for scenarios like a help desk where callers may speak any language and all input should be translated to English:
Setup:
-
Send
init, thenconfigurewith a wildcardlang_in -
Wait for
configured - Stream input audio over the selected transport — the speaker can talk in any supported language
-
The server identifies the language and sends a
language_routeevent with the detected language. You may receive intermediateis_final: falsemessages while detection is still refining, followed by the finalis_final: truemessage once the route is locked: - Receive transcription (in the original language), translation (in English), and English output audio
- Display the detected language to the user so they know it was recognized correctly
Bidirectional Language Detection
For two-way conversations where either party may speak either language:language_route message for each utterance indicating which route was chosen.
Mid-Session Reconfiguration
You can change languages, voice, glossary, or features at any time by sending a newconfigure message. This is useful for:
- Swapping speaker/language direction between turns
- Changing the output voice
- Updating the glossary
configured before sending new audio.
Supported Languages
Language codes use BCP-47 format (e.g.,"en-US", "es-ES", "fr-FR"). To retrieve the current list of supported languages, use the Gateway languages API:
Error Handling
Connection Errors
Authentication is handled by the transport before the message layer starts. If authentication fails, the server returns an HTTP error before accepting the WebSocket connection:Runtime Errors
During a session, errors are delivered aserror messages over the active Stream transport. Non-fatal errors (bad JSON, unknown message type) do not close the connection — you can continue sending messages.
Fatal errors (backend connection failure, gRPC stream errors) may be followed by the connection closing.
Inactivity Timeout
If no messages or media are sent or received for 60 seconds (configurable), the server closes the transport session.Best Practices
- Audio chunking: Send audio in small chunks (10-20 ms of audio per chunk) for the lowest latency. Larger chunks add latency equal to the chunk duration.
- Real-time pacing: When streaming from a live microphone, audio is naturally paced. When streaming from a file, pace sends to match real-time (e.g., sleep 20 ms between 20 ms chunks) for optimal results. Set
realtime_playback: truein init when playing audio back in real-time. - Handle partial results: Display partial transcriptions and translations for a responsive UX, but only persist complete words. Complete words will not change and are sent only once.
- Buffer output audio: Accumulate a small buffer of output audio before starting playback to prevent stuttering. Most client-side audio playback libraries handle this automatically.
- Use
request_id: Includerequest_idinconfigureandflushmessages to correlate responses, especially when sending multiple operations. - Reconnection logic: Implement exponential backoff for connection failures.
- Graceful shutdown: Send
flushbefore closing the connection to ensure all pending output is received. - Reuse sessions: For multi-turn conversations, reuse the same transport session and send new
configuremessages between turns rather than reconnecting.
Migration Guide: v2 Consecutive API to v3 Stream API
This guide helps you migrate from the v2/v2/consecutive WebSocket endpoint to the v3 /v3/stream WebSocket endpoint. The message shape changes also apply to clients using the WebRTC data channel for Stream v3 messages.
Endpoint Change
Important: In v2, authentication failures happened after the WebSocket was established (close code 1008). In v3, authentication is validated before the upgrade, so failures result in a standard HTTP 401 response. Update your client error handling accordingly — you will receive an HTTP error, not a WebSocket close frame.
Protocol Changes
The biggest change is that v3 separates session initialization into two explicit steps (init + configure) instead of a single config message.
Initialization: config -> init + configure
v2: Single config message
- Sample rates moved to
init(sent once per connection) lang_in/lang_outreplaced by thelanguage_routesarraycan_lang_swap: truereplaced by adding a second route with swapped languagesglossarychanged from a flat string array to an array of{"terms": {...}}objects. Use"*"as the language key to apply a term across all languages (recommended default), and override specific languages as needed.- You must include
"features": ["consecutive"]to get consecutive mode behavior voice_idremains available onconfigure(same as v2config)
Ready Signal: ready -> configured
v2:
session_id field is no longer provided in the ready response. If you need a session identifier, use the conversation_id from init.
Audio Messages: JSON audio -> Binary WebSocket frames
v2 (input):
data. In v3, audio is sent and received as raw binary WebSocket frames, eliminating the ~33% base64 overhead. All other messages remain JSON text frames.
Transcription and Translation: added utterance_idx
v2 transcription and translation messages contained only complete and partial word arrays. v3 adds an utterance_idx field to each message so you can associate results with a specific utterance when multiple utterances are in flight.
v2:
End-of-Speech: stop/flush / speech_stop -> flush / flushed + lifecycle events
v2 (client sends):
stop and flush were accepted in v2 and had identical behavior. In v3, only flush exists, and it now accepts an optional request_id for correlation.
v3 (client sends):
speech_stop message is replaced by input_speech_ended (when the server detects the speaker stopped) and output_speech_ended (when all output audio has been sent). The flushed message confirms a manual flush is complete.
Note: in v2, a flushed message could arrive without a client-initiated stop/flush (triggered automatically by VAD via output_speech_ended). In v3, output_speech_ended and flushed are distinct events — flushed only arrives in response to a client flush.
Language Detection: languages -> language_route
v2:
utterance_idx to associate the language detection with a specific utterance, and is_final to distinguish the locked-in route from tentative routes emitted while language identification is still refining.
can_lang_swap -> Multiple Language Routes
v2:
"*" -> "*" catch-all route to detect unexpected languages and prompt the user to switch (see Language Routes for details):
Glossary Format
v2:"*" wildcard key as the default so the term is handled correctly for any language. You can override specific languages alongside the wildcard. For brand names and proper nouns that should never change, a single "*" entry is all you need.
Speech Delimiter: speech_delimiter -> output_text_boundary
v2:
utterance_idx moved to the top level, time was renamed to output_time, and the inner position objects no longer repeat utterance_idx. v3 emits boundaries by default (matching v2’s unconditional speech_delimiter), and clients that don’t need them can opt out by adding "hide_output_text_boundary" to the configure.features list to skip server-side alignment work.
Renamed Events
New Events in v3
The following server events are new in v3 and were not exposed on the v2 WebSocket:Quick Migration Checklist
- Change WebSocket URL from
/v2/consecutiveto/v3/stream - Replace the single
configmessage withinit+configure - Add
"features": ["consecutive"]to theconfiguremessage - Replace JSON
"type": "audio"input messages with raw PCM bytes in WebSocket Binary frames - Handle output audio as WebSocket Binary frames containing raw PCM bytes (instead of JSON
"type": "audio"text frames) - Replace
"type": "stop"(or"type": "flush") with"type": "flush"(now supportsrequest_id) - Handle
"type": "configured"instead of"type": "ready" - Handle
"type": "language_route"instead of"type": "languages" - Handle
"type": "input_speech_ended"instead of"type": "speech_stop" - Handle
"type": "output_text_boundary"instead of"type": "speech_delimiter"(emitted by default like v2; add"hide_output_text_boundary"toconfigure.featuresto opt out) - Update glossary format from flat strings to
{"terms": {"*": "term"}}objects (always include the"*"wildcard key) - Replace
can_lang_swap: truewith a second language route - Handle
utterance_idxontranscriptionandtranslationmessages (new in v3) - Handle
"type": "translation_ended"(renamed from v2"type": "translation_end") - (Optional) Handle new lifecycle events for richer UX