Appearance
Протокол сообщений
Все сообщения передаются в формате JSON с обязательным полем event, определяющим тип сообщения.
Client → Server
session.begin
Аутентификация и инициализация сессии. Должно быть отправлено в течение 10 секунд после подключения.
| Поле | Тип | Обязательное | По умолчанию | Описание |
|---|---|---|---|---|
event | string | да | — | "session.begin" |
api_key | string | да | — | API-ключ (формат sk_live_...) |
voice_id | string | да | — | UUID голоса для синтеза |
speed | float | нет | 1.0 | Скорость речи (0.5–1.5) |
output_format | string | нет | "pcm_24000" | Формат выходного аудио |
normalize | bool | нет | true | Нормализация текста перед синтезом. При true — полная нормализация: раскрытие времени (9:00 → «девять часов»), озвучивание символов (² → «в квадрате», ₽ → «рублей»), обработка ссылок, скобок и пунктуации. При false — только базовая очистка (удаление HTML/эмодзи, нормализация пробелов), косметические преобразования отключены |
inactivity_timeout | int | нет | 30 | Тайм-аут бездействия в секундах (5–180) |
buffer_config | object | нет | — | Настройки буферизации текста |
buffer_config:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
first_chunk_threshold | int | 120 | Порог для первого сброса буфера (50–200 символов) |
subsequent_chunk_threshold | int | 160 | Порог для последующих сбросов (80–200 символов) |
json
{
"event": "session.begin",
"api_key": "sk_live_YOUR_API_KEY",
"voice_id": "9efc2a63-911e-4e19-9d5e-01b0640fc4e6",
"speed": 1.0,
"output_format": "pcm_24000",
"normalize": true,
"inactivity_timeout": 60,
"buffer_config": {
"first_chunk_threshold": 120,
"subsequent_chunk_threshold": 160
}
}text.chunk
Отправка фрагмента текста для синтеза.
| Поле | Тип | Обязательное | По умолчанию | Описание |
|---|---|---|---|---|
event | string | да | — | "text.chunk" |
text | string | да | — | Фрагмент текста (слово, токен, предложение) |
flush | bool | нет | false | Немедленно отправить буфер на синтез |
json
{
"event": "text.chunk",
"text": "Привет, как дела?",
"flush": true
}Когда использовать flush: true
Если вы отправляете короткую фразу (менее 120 символов) и хотите, чтобы синтез начался немедленно — передавайте flush: true. Без этого текст будет ждать в буфере до достижения порога или до text.end.
text.end
Сигнал окончания текста. Сервер сбрасывает остаток буфера и дожидается завершения синтеза. При успехе он отправляет generation.complete, при ошибке — только error.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
event | string | да | "text.end" |
json
{"event": "text.end"}interrupt
Немедленное прерывание текущей генерации. Сервер отменяет синтез, очищает буфер и очередь, затем отправляет generation.interrupted.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
event | string | да | "interrupt" |
json
{"event": "interrupt"}Важно: drain после interrupt
После отправки interrupt в WebSocket-канале могут находиться сообщения audio.chunk, которые сервер уже отправил до обработки вашего interrupt. Клиент обязан продолжать читать сообщения до получения generation.interrupted. Все audio.chunk, полученные после отправки interrupt, следует вычитать из канала, но не воспроизводить.
session.end
Завершение сессии. Сервер дожидается окончания текущей генерации (если есть), затем закрывает WebSocket с кодом 1000.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
event | string | да | "session.end" |
json
{"event": "session.end"}ping
Keepalive-сообщение. Сервер ответит pong. Можно отправлять до аутентификации.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
event | string | да | "ping" |
json
{"event": "ping"}Server → Client
session.ready
Подтверждение успешной аутентификации. Сессия готова к приёму текста.
| Поле | Тип | Описание |
|---|---|---|
event | string | "session.ready" |
session_id | string | UUID сессии (для логирования/отладки) |
json
{
"event": "session.ready",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}audio.chunk
Фрагмент аудио (~200 мс). Данные закодированы в base64.
| Поле | Тип | Описание |
|---|---|---|
event | string | "audio.chunk" |
audio | string | Base64-кодированные raw audio bytes |
sequence | int | Порядковый номер чанка (начинается с 1, монотонно возрастает) |
json
{
"event": "audio.chunk",
"audio": "AQID...base64data...==",
"sequence": 1
}Размер чанка
Каждый чанк содержит ~200 мс аудио. Для pcm_24000 это 9 600 байт (24 000 × 2 × 0.2). После base64-кодирования — ~12 800 символов.
generation.complete
Синтез успешно завершён. Все аудио-чанки отправлены. Сессия возвращается в состояние READY. После события error для той же генерации generation.complete не отправляется.
| Поле | Тип | Описание |
|---|---|---|
event | string | "generation.complete" |
total_chunks | int | Общее количество отправленных audio.chunk |
json
{
"event": "generation.complete",
"total_chunks": 42
}generation.interrupted
Генерация прервана по запросу клиента (interrupt). Сессия возвращается в состояние READY.
| Поле | Тип | Описание |
|---|---|---|
event | string | "generation.interrupted" |
chunks_sent | int | Количество audio.chunk, отправленных до прерывания |
json
{
"event": "generation.interrupted",
"chunks_sent": 15
}WARNING
Значение chunks_sent отражает количество чанков, которые сервер успел отправить. Из-за сетевой задержки клиент может получить generation.interrupted после нескольких дополнительных audio.chunk — см. обработка interrupt.
error
Сообщение об ошибке. В зависимости от типа ошибки соединение может быть закрыто.
| Поле | Тип | Описание |
|---|---|---|
event | string | "error" |
code | string | Код ошибки |
message | string | Человекочитаемое описание |
json
{
"event": "error",
"code": "auth_failed",
"message": "Invalid or revoked API key"
}Коды ошибок
| Код | message | Поведение |
|---|---|---|
invalid_message | Invalid WebSocket message | Соединение открыто; исправьте сообщение |
invalid_text | Text contains no synthesizable content | Ошибка текущей генерации |
request_too_large | Text is too long | Ошибка текущей генерации |
auth_failed | Invalid or revoked API key | Закрытие 4001 |
permission_denied | Access denied | Закрытие 4007 |
insufficient_balance | Insufficient balance | Закрытие 4004 |
voice_not_found | Voice not found | Закрытие 4006 |
voice_not_ready | Voice is not ready yet | Закрытие 4009 |
rate_limited | Realtime connection limit exceeded | Превышен лимит параллельных Realtime-соединений; закрытие 4003 |
service_unavailable | Speech synthesis service is temporarily unavailable | До session.ready: закрытие 4005; во время генерации: соединение открыто |
synthesis_timeout | Speech synthesis timed out | Ошибка текущей генерации |
synthesis_failed | Speech synthesis failed | Ошибка текущей генерации |
internal_error | Internal server error | Закрытие 4500 |
auth_timeout | session.begin was not received in time | Закрытие 4002 |
inactivity_timeout | Session closed due to inactivity | Закрытие 4008 |
Ошибки текущей генерации (invalid_text, request_too_large, service_unavailable, synthesis_timeout, synthesis_failed) не закрывают уже готовую сессию. Для ошибочной генерации сервер не отправляет generation.complete. Если text.end уже был отправлен, можно начинать следующую генерацию; иначе сначала завершите границу текущей генерации через text.end или interrupt.
Рекомендуемая реакция клиента
| Код | Действие |
|---|---|
invalid_message | Исправить формат или порядок WS-сообщений |
invalid_text | Исправить или отфильтровать текст |
request_too_large | Уменьшить объём текста, отправляемого за одну генерацию |
auth_failed | Обновить API-ключ и переподключиться |
permission_denied | Проверить права доступа |
insufficient_balance | Пополнить баланс/лимит и переподключиться |
voice_not_found | Выбрать существующий голос |
voice_not_ready | Подождать готовности голоса и создать новую сессию |
rate_limited | Закрыть лишнее Realtime-соединение и подключиться снова |
service_unavailable | Повторить запрос |
synthesis_timeout | Повторить запрос |
synthesis_failed | Использовать fallback текста или голоса по бизнес-логике |
internal_error | Не повторять автоматически; обратиться в поддержку |
auth_timeout | Переподключиться и сразу отправить session.begin |
inactivity_timeout | Переподключиться, если работу нужно продолжить |
pong
Ответ на ping.
| Поле | Тип | Описание |
|---|---|---|
event | string | "pong" |
json
{"event": "pong"}Следующие шаги
- Жизненный цикл сессии — состояния, буферизация, interrupt
- Примеры интеграции — рабочий код на Python