Realtime Text to Speech v1
Maintain clients that use the frozen Realtime TTS WebSocket v1 protocol.
Realtime TTS v1 is a frozen compatibility protocol for existing clients. New integrations should use the parent v3 contract. Do not migrate a v1 client by changing only the URL: v2 and v3 use different event and request contracts.
Endpoint and protocol
| Item | Value |
|---|---|
| WebSocket | wss://realtime.fishaudio.org/v1/tts/live |
| Subprotocol | realtime.tts.msgpack.v1 |
| Encoding | MessagePack binary frames |
Authenticate from a trusted backend during the WebSocket handshake:
Authorization: Bearer FISHAUDIO_API_KEY
Sec-WebSocket-Protocol: realtime.tts.msgpack.v1Do not place credentials in the URL or expose a long-lived API key to browser code.
Lifecycle
connect → authenticated → start → ready
→ text → flush → audio (one or more) → usage
→ [text → flush → audio → usage] → stop → finishSend every application message as a MessagePack map. One connection represents one session. A flush generates the currently buffered text; you may submit more segments before stop.
Client events
| Event | Required fields | Notes |
|---|---|---|
start | event, request | Starts the session after authenticated. |
text | event, text | Adds non-empty UTF-8 text to the current segment. |
flush | event | Generates the buffered segment; an empty segment is rejected. |
stop | event | Flushes remaining text, completes the session, and returns finish. |
ping | event | Optional timestamp; the server returns pong. |
Each event may include an optional request_id string of up to 128 characters.
start.request
| Field | Required | Constraints |
|---|---|---|
model_id | Yes | Public voice/model UUID used by the v1 client. |
engine_model_id | No | Legacy engine selector, when assigned. |
format | No | mp3, wav, pcm, or opus; default mp3. |
speed | No | 0.5 to 2. |
volume | No | 0 to 10. |
language | No | Language hint, 1–32 characters. |
chunk_length | No | Integer from 100 to 300. |
latency | No | normal or balanced. |
Legacy camelCase aliases modelId and engineModelId are also accepted. Unknown fields are rejected.
Server events
Handle authenticated, ready, audio, usage, pong, warning, finish, and error. Audio bytes are returned in audio.data. The usage event reports segment units and remaining quota; finish.units reports accepted session units.
import { decode, encode } from '@msgpack/msgpack';
import WebSocket from 'ws';
const ws = new WebSocket('wss://realtime.fishaudio.org/v1/tts/live', 'realtime.tts.msgpack.v1', {
headers: { Authorization: `Bearer ${process.env.FISHAUDIO_API_KEY}` },
});
ws.on('message', (raw) => {
const message = decode(raw);
if (message.event === 'authenticated') {
ws.send(encode({ event: 'start', request: { model_id: 'PUBLIC_VOICE_UUID', format: 'mp3' } }));
} else if (message.event === 'ready') {
ws.send(encode({ event: 'text', text: 'Hello from Realtime TTS v1.' }));
ws.send(encode({ event: 'flush' }));
ws.send(encode({ event: 'stop' }));
} else if (message.event === 'audio') {
// Append message.data to the output stream.
} else if (message.event === 'error') {
console.error(message.code, message.message, message.retryable);
}
});Billing and errors
Billing is settled per accepted flush segment. If generation fails before a segment is committed, its reservation is refunded. Do not retry blindly after receiving audio; v1 has no durable request replay or segment idempotency contract.
Protocol failures arrive as error with code, message, and retryable, followed by a WebSocket close. Common validation failures include invalid_message, empty_segment, segment_too_large, model_not_found, and insufficient_quota.
For camelCase fields, provider-neutral model switching, browser tickets, and durable recovery, migrate to Realtime Text to Speech v3.