wss://api.rime.ai/coda/ws to send text and receive audio on one persistent connection. Each synthesis has a contextId. JSON and Protobuf support the same parameters, audio formats, and lifecycle. Neither returns word timestamps.
For runnable clients, use the JSON quickstart, Protobuf quickstart, or LiveKit integration.
Connect and authenticate
Your server sendsAuthorization: Bearer YOUR_API_KEY in the WebSocket upgrade request. Create a key on the API Tokens page. Keep it on your server. Browser applications must connect through your backend.
Select one subprotocol in the upgrade request and check the negotiated value:
Wait for
ready and check that ready.protocol is 1 before sending start. ready.languages lists supported language tags. ready.defaultLanguage, when present, gives the deployment default. Readiness reports engine availability; it does not replace authentication.
Client messages
Each message contains one payload field and an optionalcontextId. Do not send a type discriminator or a payload wrapper. JSON uses camelCase names; generated Python Protobuf objects use snake_case, such as audio_parameters.sampling_rate.
Use the same
contextId for start, text, end, and cancel in one turn. A repeated end, or one for a complete-text or unknown context, has no effect.
This message appends a sentence to an open streaming context:
Run a synthesis context
The value ofstart.text selects the input mode:
This sequence streams two sentences. The client can send text while it receives audio:
Complete text in one request
Thisstart supplies the complete utterance. The server finishes it without an end message:
Stream text as it becomes available
- Buffer LLM tokens locally, then send complete sentences or stable clauses. The server normalizes each message separately, so splitting a number, abbreviation, or word can change its pronunciation.
- Preserve spaces at chunk boundaries, for example,
"Hello. "followed by"How can I help? ". - An input pause needs no message. Send
endonly when the turn is complete. It leaves the socket open.
splitStrategy splits text within each message. It does not buffer incomplete sentences across messages.
Identify and reuse contexts
- Use a new
contextIdper turn to help reject stale audio. An omitted or empty ID selectsdefault, which the server returns as"contextId":"default". - Responses from concurrent contexts can arrive between each other. Route them by
contextIdfrom one receive loop, with a separate decoder and playback queue per context. - You can reuse an ID after its terminal event. The engine supports up to 16 open contexts.
- Log
started.requestIdwith yourcontextIdfor error reports. A request can fail or be cancelled beforestarted, so handle terminal events as soon as you sendstart.
Stop speech on interruption
- Stop local playback and discard the turn’s queued audio.
- Send
cancelwith that turn’scontextId. - Keep reading until
cancelled, ordoneif synthesis completed first. A context-scopederroralso ends the run. Discard audio for the interrupted turn that was already in transit.
turn-1:
cancelled acknowledgement. Cancelling a context leaves the connection open; closing the socket cancels all its active contexts.
Connection configuration
Send at most oneconfig message before the first start. It has no acknowledgement.
Defaults merge at the top level.
start.audioParameters replaces the whole default object, so include every audio setting you want to retain. The same rule applies to codaParameters.
This configuration uses header authentication and sets PCM output for later turns:
start.audioParameters contains only {"samplingRate":8000}, it discards the default audioFormat. Include "audioFormat":"audio/pcm" to retain PCM.
Synthesis parameters
Set these fields instart or config.defaults. The URL selects Coda; there is no modelId field.
Coda does not accept
temperature, topP, topK, repetitionPenalty, maxTokens, or seed. Unknown fields can be ignored, so a successful request does not prove an unsupported setting took effect.
Audio parameters
Both protocols support these audio formats. Decode the message envelope first: JSON carries base64 audio; Protobuf carries audio bytes.
Message boundaries are transport chunks, not separate files or guaranteed decoder frames. Keep one decoder alive for each context. Raw PCM consumers must retain any incomplete sample between chunks.
Direct engine audio aliases
Direct engine audio aliases
Direct engine requests also accept
audio/mp3, audio/ogg, audio/webm, audio/x-mulaw, and audio/l16. The audio/l16 alias returns little-endian PCM. Use audio/pcm to avoid confusion with network-byte-order L16. Unknown formats fall back to WAV; an empty format is invalid. LiveKit accepts only the six canonical MIME types above.Text splitting and lookahead
Set
codaParameters.textLookaheadTokens to a non-negative integer, default 0. More leading text tokens can improve the start of an utterance but delay first audio. The engine caps lookahead at the available tokens and ignores it for complete-text input.
Server events
These examples show separate server messages. The language list is illustrative, and
error.requestId is optional:
"done" in event; the empty object {} is false in Python. For Python Protobuf, use response.WhichOneof("payload"). Allow unknown fields for compatible schema additions.
Handle completion and errors
An active context finishes with one terminal event:done, cancelled, or a context-scoped error. No more audio belongs to that run after its terminal event.
Duplicate
start, text outside an open streaming input, and too many open contexts cause connection-level errors. They do not terminate an existing context. Malformed envelopes and failed authentication can close the socket.
There is no resume operation. Retrying text whose audio already played can repeat speech. Choose retries at a turn or sentence boundary.
Error kinds
error.kind is a string, not an HTTP status code. Authentication can also fail with HTTP 401 during the upgrade.
An undecodable envelope produces
invalid_input and close code 1007. Invalid or conflicting message credentials produce close code 1008. Treat an unexpected close before a terminal event as incomplete synthesis, even if some audio arrived.
Limits and connection health
A hosted gateway can apply additional limits. Use a WebSocket library that answers ping frames. Keep one reader active, await sends to respect backpressure, and bound your audio queue to limit memory use with a slow player.
Advanced connection options
Subprotocol negotiation
If you offer both supported subprotocols, the server selects the first in your list. An offer with only unknown values gets no selected subprotocol, which can cause the client to reject the handshake. The server decodes incoming messages by their WebSocket type, text or binary. The selected subprotocol fixes the response encoding for the connection. Use the same encoding in both directions unless you need a mixed client.Message-based authentication
Clients that cannot send upgrade headers can sendconfig.authorization, such as Bearer YOUR_API_KEY, in their first message. This does not make an API key safe to put in a browser.
On a direct engine connection, send credentials within 10 seconds. Do not send credentials in both the upgrade headers and config; the engine closes the connection. Deployments with license authentication accept config.license as a JSON string.
