Skip to main content
Stream text input token-by-token for LLM integration. This is the endpoint behind every SDK streaming session; the conceptual guide is Streaming overview and the turn semantics are on Turn lifecycle.

Connection

Protocol

  1. Send config (once): Initial configuration message. voice_id, audio format, and the other settings are sticky for the connection — you do not re-send them on later turns.
  2. Send text: Text chunks for the current turn as they arrive
  3. Send flush: Ends the turn — emits any trailing buffered text, streams its audio, then closes the turn’s session (session_closed). The socket stays open.
  4. Next turn: Send the next turn’s text (a fresh config is optional). Repeat. To end the whole connection, send close_socket.
  5. Receive audio: Audio chunks as they’re generated
One turn = one backend session. A turn ends when you send flush (or after a short idle gap — see below); each turn runs on its own freshly-prefilled voice session. A text WebSocket frame is not a hard sentence boundary by itself. For token streams, send raw tokens and flush once at the end of the turn. If your application sends already-complete phrases without terminal punctuation, include flush: true on that message or send a separate flush message.
Idle turns auto-end after 5 seconds. If you stream text but never flush, the server auto-flushes the buffered text after ~5 s of no new text, emits a warning frame, and ends the turn. WebSocket ping/keep-alive frames do not reset this — only sending flush (or new text) does. End each turn with an explicit flush for the lowest latency and to avoid the auto-flush. Full lifecycle: Turn lifecycle.

Messages

Config Message

All other fields share the meaning and defaults of the Generate Speech parameters. To change generation parameters (cfg_scale, temperature, speed, max_new_tokens, language, normalize) part-way through a connection, send an Update Settings message — the change applies to the next turn.

Text Message

Flush Message

Close Message

End the current session; the WebSocket stays open and the server starts a fresh session on the next config / text message:
{"end_session": true} is accepted as an alias. To end the session and close the WebSocket connection, send {"close_socket": true} instead.

Cancel Message (barge-in)

Abandons the current turn immediately: in-flight generation is cancelled and buffered text dropped. The server acknowledges with {"interrupted": true}; the socket stays open for the next turn. See Barge-in.

Update Settings Message

Change generation parameters mid-connection without reconnecting. Send an update_settings message; the server validates it and replies with a settings_updated acknowledgement carrying the parameters now in effect.
Only these generation parameters are updatable — every field is optional, and a message updates only the fields it carries:
Updates take effect on the next turn. Generation parameters are bound when a turn’s backend session opens, so a turn already in flight keeps the settings it started with — the update applies to the next turn (the next text after a flush). To apply a change immediately, end the current turn first.
Identity, project, dictionary, and audio-format fields (voice_id, model_id, sample_rate, output_format, project_id, dictionary_ids) are not accepted inside update_settings. Including one is rejected with a VALIDATION_ERROR frame (the socket stays open) so an unsupported change is never silently dropped. To change one between turns, send it as an ordinary config message before starting the next turn.

Response Messages

Generation Started

Audio Chunk

Field-by-field reference: Audio formats.

Word Timestamps (when word_timestamps: true)

Chunk Complete

Interrupted

Sent only in response to {"cancel": true} — the turn was cancelled and the session is ready for the next turn:

Settings Updated

Acknowledges an update_settings message. settings holds the generation parameters now in effect for subsequent turns:

Warning

Non-fatal advisory; the socket stays open. Currently emitted when a turn is auto-ended after the idle timeout because no flush was sent:

Final (End of Audio)

Sent after the last audio frame of every gracefully completed turn (explicit flush, close, or idle auto-flush), right before session_closed. Once you receive it, no further audio for the turn will arrive — the equivalent of ElevenLabs’ isFinal. It is not sent after a cancel (barge-in); that path acknowledges with interrupted instead.
Use final to stop waiting for audio (e.g. to end playback or hang up a call); use the session_closed frame that follows for usage/billing data.

Session Closed

Sent at the end of every turn (on flush, idle auto-flush, or close). The socket stays open for the next turn.
The usage object reports the session’s consumed audio time and the actual amount charged (EUR cents) so you can bill per conversation — same fields as the /ws/tts final message. cost_cents is null with cost_unavailable: true if the charge can’t be determined (never a silent 0).

Example

Errors

See Error Codes for the full TTS error lookup table, including HTTP status codes, WebSocket close codes, and rate-limit behavior.