Для голосовых агентов и сценариев с минимальной задержкой используйте WebSocket — wss://tryaxolotl.ru/v1/tts/live. Протокол JSON: вы шлёте текст по кусочкам, аудио приходит потоком. Один сокет обслуживает весь диалог: интонация остаётся плавной внутри реплики (без полей previous_text / next_text), а конец аудио каждой реплики сервер отмечает явным сообщением.
Для разовой озвучки целого текста достаточно HTTP POST /v1/speech. WebSocket нужен, когда текст генерируется по ходу (LLM, диалог) или важна непрерывная интонация.

Подключение

Авторизация — как у HTTP API:
  • заголовок Authorization: Bearer $AXOLOTL_API_KEY, или
  • заголовок X-API-Key: $AXOLOTL_API_KEY, или
  • query-параметр ?api_key=$AXOLOTL_API_KEY (удобно в браузере).
Все управляющие сообщения — JSON text frames. Аудио по умолчанию тоже приезжает в JSON (base64); при audio_encoding: "binary" — бинарными кадрами.

Реплики (turns)

Сессия — это последовательность реплик. Реплика — один непрерывный кусок речи: вы стримите text, при желании форсируете синтез через flush, а затем закрываете реплику через end_turn. Сервер отвечает turn_end после того, как отдал последний чанк аудио этой реплики — угадывать конец по таймауту тишины не нужно. После turn_end сокет остаётся открытым: следующий text начинает следующую реплику. Авторизация и TCP/TLS-хендшейк не повторяются.
flush и end_turn — разные вещи. flush только просит начать синтез накопленного текста и не имеет подтверждения: интонация продолжает течь дальше, поэтому его удобно слать после каждого предложения внутри реплики. end_turn закрывает реплику целиком, подтверждается turn_end и сбрасывает интонационный контекст перед следующей репликой.

Протокол

Клиент → сервер

string
default:"axolotl"
Идентификатор голоса (axolotl, daniil, sergey, dmitriy, polina, tatyana или ваш private id). См. Голоса. Голос и формат задаются один раз на сессию.
string
default:"mp3"
Формат аудио: mp3, mp3_high, mp3_low или pcm.
string
default:"low"
Компромисс задержка/качество: low, balanced, normal. Для агентов рекомендуем low + pcm.
string
default:"base64"
base64 — аудио приходит в JSON-сообщениях audio. binary — аудио приходит бинарными кадрами WebSocket, без base64 и JSON-обвязки: на треть меньше байт и меньше работы на обеих сторонах. Управляющие сообщения в обоих случаях остаются текстовым JSON, так что различать их можно по типу кадра.

Сервер → клиент

Пример сессии

Дождитесь ready, затем отправляйте текст:
Ответ сервера (упрощённо):
Дальше можно сразу слать текст следующей реплики. В конце диалога — например, после ещё двух таких же реплик — закройте сессию:
Если вы отправите end, не закрыв последнюю реплику через end_turn, сервер сначала досинтезирует её и пришлёт turn_end, и только потом done. Поэтому done — всегда надёжный признак того, что аудио больше не будет.

Node.js

Python

Пример с бинарными кадрами — реплики отправляются одна за другой по одному сокету, конец каждой определяется по turn_end:
Не отправляйте text сразу после подключения — дождитесь ответа ready. Серверу нужно время на инициализацию сессии после start.
Лимит — 10 000 символов на одну реплику. Каждая реплика тарифицируется и попадает в историю запросов отдельно, как обычный HTTP-синтез; done показывает сумму по сессии. Общей длительности сессия не ограничена.

Как встроить в голосового агента

Типичный пайплайн: STT → LLM → TTS → воспроизведение. Axolotl закрывает блок TTS через одну долгоживущую WebSocket-сессию на реплику агента (или на весь диалог).

Рекомендуемый цикл

  1. Подключиться к wss://tryaxolotl.ru/v1/tts/live с API-ключом — один раз на весь диалог.
  2. Отправить start с format: "pcm" и latency: "low".
  3. По мере генерации LLM слать text (токены или целые фразы).
  4. После каждого предложения — flush, чтобы аудио не ждало буфера.
  5. Когда реплика закончена — end_turn, дождаться turn_end.
  6. Для следующей реплики — снова с шага 3, по тому же сокету.
  7. В конце диалога — end, дождаться done.

Как понять, что аудио закончилось

Только по turn_end (или done для всей сессии). Не используйте таймаут тишины: паузы между чанками зависят от длины реплики и загрузки синтеза, и любой порог рано или поздно либо обрежет хвост, либо добавит лишнюю задержку. Единственный «таймаут» в протоколе — серверная страховка: если синтез завис и за 30 секунд после end_turn не отдал ни одного чанка, сервер всё равно закроет реплику сообщением turn_end, чтобы клиент не ждал вечно.

Почему нет previous_text / next_text

Эти поля есть у некоторых провайдеров в HTTP API. У Axolotl контекст интонации держится внутри реплики: каждый новый text учитывает уже озвученное с начала реплики. Границей контекста служит end_turn — поэтому длинный ответ не надо резать на отдельные реплики, достаточно flush после предложений.

Pipecat

Создайте сервис на базе WebsocketTTSService:
В _receive_messages декодируйте audio.data из base64 в TTSAudioRawFrame (sample rate 44 100 Hz для pcm).

Barge-in (прерывание пользователем)

Когда пользователь перебивает агента:
  1. Остановите воспроизведение у себя.
  2. Отправьте end_turn и игнорируйте аудио до turn_end — сокет останется живым и готовым к следующей реплике.
  3. Если нужно оборвать мгновенно и сессия больше не нужна — просто закройте сокет: уже отданное аудио будет затарифицировано, остальное отменится.

Настройки для минимальной задержки

См. также: Стриминг HTTP, Длинные тексты.
Эмоциональные теги [смеётся], [шёпотом] и другие [...] работают и в WebSocket-сессии — см. Эмоции и интонации.