Skip to main content
WSS
Bidirectional WebSocket endpoint for real-time text-to-speech. Push text as you have it; receive audio as it’s synthesized, in strict segment order. Designed for live captions, narration over streaming LLM output, interactive voice apps — anywhere you want playback to start before the writer is finished writing.
Authenticate with your CambAI API key via the x-api-key header (or ?api_key=... query parameter for clients that can’t set headers).

Quickstart

A complete, copy-pasteable client. Connect → configure → stream text → write the audio to a file.
That’s the whole integration surface. Everything below is reference for the four message types you’ll exchange.

Integration in 4 steps

1

Open the socket with your API key

Missing or invalid key → server closes with code 4401.
2

Send `session.start` as the first frame

voice_id is the only required field — everything else has a sensible default. The tuning knobs mirror the regular POST /tts-stream API one-for-one (enhance_named_entities_pronunciation, apply_enhancement, enhance_reference_audio_quality, maintain_source_accent, speaking_rate), so you can port a working /tts-stream payload directly. See the full reference at the top of the page for types and defaults.Wait for the session.ready reply (carries session_id and run_id). A malformed first frame, forbidden voice, or unsupported language → session.error then close 4400.
3

Stream text in

Push as fast or as slowly as you like. The server segments by content (sentence boundaries), and idle-flushes after idle_timeout seconds of silence (default 1.0) — so for live use cases (LLM token stream, transcribed mic input) you don’t need to send text.done until the session is truly over.idle_timeout is only a fallback flush for trailing fragments without a boundary. A complete sentence (terminal punctuation, paragraph break, etc.) is flushed immediately — it never waits on idle_timeout. Bump the value on session.start (e.g. 2.5) if your producer routinely stalls mid-sentence — slower LLMs, token-level jitter — to avoid splitting one sentence across two segments.
Slow producers fragment sentences. If your LLM (or other source) is not producing text fast enough to land consecutive chunks within idle_timeout (default 1s), each chunk will be flushed as its own segment — even if together they would have formed a single sentence. The result is choppier audio and prosody that resets at each fragment boundary. Raise idle_timeout to cover the worst-case gap between your producer’s tokens.
4

Read ordered audio + lifecycle frames

For each segment N, the server emits, in order:
Segment N’s frames are completely emitted before any of segment N+1’s, even though synthesis runs concurrently behind the scenes. Concatenate the binary frames per segment_id and you have playable audio.When everything is done you’ll receive session.done, followed by a clean close.

Common patterns

Stream from an LLM

Push tokens straight from the model. Don’t call text.done — let the idle flush handle in-flight buffering, then close when the LLM is done.

Play audio while it’s still synthesizing

Hand each segment to your player as soon as segment.done arrives:

Recover from a skipped segment

segment.skipped means TTS retries (3 by default, exponential backoff) were exhausted for that segment. The session keeps running — re-send the text in a new text.chunk if you need the audio:

Word-level timestamps

Set "word_timestamps": true in session.start. When resolution succeeds, segment.start carries a word_timestamps array:
Word-timestamp failures (timeout, 5xx, network) are silently swallowed; the segment is still delivered without the word_timestamps field. Treat it as best-effort — don’t block playback on it.

Reference

The AsyncAPI spec above documents every message type and field. Quick lookup:

Close codes

Auth & billing

  • API key auth is identical to the rest of /apis/*.
  • A TTS_API Run is created on session.start; its run_id is in session.ready and can be queried later via the standard run endpoints.
  • Credits are deducted per segment, immediately before that segment is synthesized. If you run out mid-session, the server emits a single session.error and closes with 4402.

Voice & language

Voice access uses the same rules as /tts-stream. The session is pinned to the mars-8.1-flash-beta speech model — see the streaming TTS docs for the supported BCP-47 locales. For best results, supply a reference voice in the same language/accent as language.

Server-side TTS retries

ConnectionError / TimeoutError / OSError / aiohttp.ClientError against the underlying TTS engine trigger up to 3 retries per segment with exponential backoff. On exhaustion the segment becomes segment.skipped (see Recover from a skipped segment above) and the rest of the session continues normally.
Session Accepted
type:object

Sent immediately after session.start is accepted.

type
type:string
required

session.ready

session_id
type:string
required
run_id
type:integer
required

ID of the TTS_API Run created for this session.

config
type:object
required

Echo of the resolved session configuration (without reference_audio).

Segment Start
type:object

Marks the beginning of a synthesized segment. Followed by one or more binary audio frames and then segment.done.

type
type:string
required

segment.start

segment_id
type:integer
required
text
type:string
required

The exact text that produced this segment's audio.

word_timestamps
type:array

Per-word timing data. Present only when word_timestamps=true was set on session.start and resolution succeeded.

word
type:string
required
start
type:number
required

Start time in seconds, relative to the segment.

end
type:number
required

End time in seconds, relative to the segment.

Binary Audio Frame
type:string

Raw audio bytes for the current segment. Up to LIVE_TTS_AUDIO_FRAME_MAX_BYTES (default 65536) per frame.

Segment Done
type:object

All audio for the current segment has been emitted.

type
type:string
required

segment.done

segment_id
type:integer
required
Segment Skipped
type:object

TTS retries were exhausted for this segment. The session continues; resend the text via text.chunk if needed.

type
type:string
required

segment.skipped

segment_id
type:integer
required
text
type:string
required
Session Done
type:object

Pipeline drained, all segments emitted. Followed by a normal close.

type
type:string
required

session.done

Session Error
type:object

Fatal session-level error. Followed by a close with code 4400 / 4401 / 4402.

type
type:string
required

session.error

error
type:string
required
Start Session (first frame)
type:object

Must be the very first message sent on the WebSocket. Configures the synthesis run.

type
type:string
required

session.start

voice_id
type:integer
required

CambAI voice ID. Validated using the same rules as /tts-stream.

language
type:string

BCP-47 locale (e.g. en-us, hi-in, zh-cn). Must be supported by mars-8.1-flash-beta.

output_format
type:enum
Available options: mp3, wav, flac, aac
word_timestamps
type:boolean

When true, the server includes per-word timing data (word_timestamps) on each segment.start.

idle_timeout
type:number

Fallback flush, in seconds, for trailing text fragments that don't end in a sentence boundary. Complete sentences (terminal punctuation, paragraph break, etc.) are flushed immediately and never wait on this timer. Bump up (e.g. 2.5) when the producer stalls mid-sentence — slower LLMs, token-level jitter — to avoid splitting a sentence across two segments. Lower it for tighter tail-latency on live captioning / mic input.

enhance_named_entities_pronunciation
type:boolean

If true, improves pronunciation of names, brands, and other named entities. Mirrors /tts-stream.

apply_enhancement
type:boolean

If true, applies output audio enhancement (loudness, denoising, polish). Defaults to the speech-model's per-engine default when omitted (off for the speed-oriented mars-flash and mars-8.1-flash-beta models, on otherwise). Mirrors /tts-stream output_configuration.apply_enhancement.

enhance_reference_audio_quality
type:boolean

If true, removes noise/compression from the reference audio before cloning. Mirrors /tts-stream voice_settings.enhance_reference_audio_quality.

maintain_source_accent
type:boolean

If true, preserves the accent of the reference voice. Mirrors /tts-stream voice_settings.maintain_source_accent.

speaking_rate
type:number

Speech pace multiplier (e.g. 1.5). Mirrors /tts-stream voice_settings.speaking_rate. Pass-through to the TTS engine.

sample_rate
type:integer

Output sample rate in Hz. Mirrors /tts-stream output_configuration.sample_rate.

inference_steps
type:integer

TTS quality/latency knob.

Append Text
type:object

Push more text into the synthesis buffer. The server segments based on content, not chunk boundaries.

type
type:string
required

text.chunk

text
type:string
required
index
type:integer

Optional informational ordering hint.

End of Input
type:object

Flush whatever is buffered and finish. Optional — the server also flushes after LIVE_TTS_IDLE_FLUSH_SECONDS (default 1s) of silence.

type
type:string
required

text.done