wss://coda.api.rime.ai/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. Either encoding can carry word timestamps.
For runnable clients, use the JSON quickstart, the Protobuf quickstart, or the LiveKit integration.
Connect and authenticate
Your server sendsAuthorization: Bearer YOUR_API_KEY in the WebSocket upgrade request. The API accepts credentials only in this header. Create a key on the API Tokens page. Keep it on your server, because anyone who loads browser code can read a key embedded in it. 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 you send start. The same event lists the supported language tags in ready.languages, and the deployment default in ready.defaultLanguage when one is set. ready means that the engine is available. It does not mean that the connection is authenticated.
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 an end 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
SetsplitStrategy on the streaming start:
With
SPLIT_STRATEGY_ACCUMULATE_SENTENCE, send tokens unchanged, spaces included. "Hello" followed by "world." is spoken as Helloworld. With SPLIT_STRATEGY_NONE, the Coda default, a message that ends mid-sentence is spoken as a fragment.
Send end only at the end of the turn. A pause in your input needs no message.
Identify and reuse contexts
- Use a new
contextIdfor every turn: the server answers acancelfor a finished context withcancelled. It also applies a latecancelto any open context with the same ID. With a new ID per turn, a latecancelorcancelledcannot end the next turn, and you can reject stale audio. - Default ID: an omitted or empty ID selects
default, which the server returns as"contextId":"default". - Route by ID: responses from concurrent contexts can arrive between each other. Route them by
contextIdfrom one receive loop, with a separate decoder and playback queue for each context. The engine supports up to 16 open contexts. - Log the request ID: 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:
cancel more than once, and an unknown or finished context still gets cancelled. Cancelling a context leaves the connection open, and closing the socket cancels all of its active contexts.
Connection configuration
Send at most oneconfig message before the first start. The server does not acknowledge it.
Defaults merge at the top level.
start.audioParameters replaces the whole default object, so include every audio setting that you want to keep. 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, and the context fails with invalid_input. Include "audioFormat":"audio/pcm" to keep PCM.
Synthesis parameters
Set these fields instart or config.defaults. The host selects Coda, so there is no modelId field.
Coda does not accept
temperature, topP, topK, repetitionPenalty, maxTokens, or seed. The server can ignore unknown fields, so a successful request does not prove that an unsupported setting took effect.
Audio parameters
Both protocols support these audio formats. Decode the message envelope first: JSON carries base64 audio, and 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 keep any incomplete sample between chunks.
Text lookahead
SetcodaParameters.textLookaheadTokens to control streaming text lookahead:
Server events
These examples show separate server messages. The language list is illustrative, and
error.requestId is optional:
"done" in event, not truthiness, because the empty object {} is falsy 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.
There is no resume operation. Retrying text whose audio already played can repeat speech, so 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 or 503 during the upgrade.
Word timestamps
Settimestamps.enable to true in start to get the time of each word Coda speaks. The times arrive in the done event, after the last audio. Asking for them does not change how the audio streams. Coda returns them for de, en, es, fr, it, and pt.
This start asks for the timestamps of a complete-text context:
done event has one span for each word of the text:
$42.50 keeps its spelling although Coda says “forty-two dollars and fifty cents.” start and end are offsets from the start of the context’s audio as you receive it, in seconds, with timeScaleFactor applied.
- Unspoken words: a word Coda does not speak, such as punctuation on its own, has no span.
- Streaming input: spans follow the text as the server joins it.
"Hello"followed by"world."gives one span,Helloworld.
status means that Coda aligned every word. Otherwise, status has a code, one of the gRPC status codes, and a message that says why, and spans is empty. The audio is unaffected either way. A context in a language Coda does not align, such as ja, or with more than 180 seconds of audio gets no spans.
Limits and connection health
A hosted gateway can apply additional limits. Keep the connection healthy with these practices:
- Answer pings: use a WebSocket library that answers ping frames, because the pongs reset the read inactivity timeout.
- Keep one reader: keep one receive loop active for the whole connection.
- Respect backpressure: await each send.
- Bound the audio queue: a bounded queue limits memory use when the player is slow.
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.Direct engine connections
These details apply when you connect to a Coda engine directly, for example in an on-premises deployment:- Credentials: a client that cannot send upgrade headers can send
config.authorizationin its first message, with the value the engine’s caller authentication expects. Send it within 10 seconds. If you send credentials in both the upgrade headers andconfig, the engine closes the connection. This does not make an API key safe to put in a browser. - License authentication: deployments that use license authentication accept
config.license. Thislicensefield holds the license JSON as a string. - Audio aliases: the engine also accepts
audio/mp3,audio/ogg,audio/webm,audio/x-mulaw, andaudio/l16. Theaudio/l16alias returns little-endian PCM, so useaudio/pcmto avoid confusion with network-byte-order L16. Unknown formats fall back to WAV, and an empty format is invalid. LiveKit accepts only the six canonical MIME types above.

