Skip to content

Bring your own TTS

If you already run a text-to-speech pipeline you like (for voice consistency across products, latency reasons, or licensing), you don’t have to use 2wai’s. POST /v1/sessions/{id}/audio accepts raw audio bytes and lipsyncs the avatar against them, skipping /speak and voice_id entirely for that session.

The request body is the raw audio itself (not JSON). Set Content-Type: application/octet-stream and send bytes directly:

async function pushAudioChunk(sessionId, chunk, isFinal) {
await fetch(
`https://api.2wai.ai/v1/sessions/${sessionId}/audio?more=${!isFinal}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TWAI_SECRET_KEY}`,
'Content-Type': 'application/octet-stream',
},
body: chunk,
},
);
}

The more query parameter tells 2wai whether to expect additional audio for the current utterance:

  • more=true on every chunk except the last
  • more=false (or omit it) on the final chunk, so the pipeline knows to flush and finish rendering
async function speakWithMyTTS(sessionId, text) {
const chunks = await myTtsEngine.synthesizeStreaming(text); // however your TTS streams audio out
for (let i = 0; i < chunks.length; i++) {
const isFinal = i === chunks.length - 1;
await pushAudioChunk(sessionId, chunks[i], isFinal);
}
}

If your TTS produces the whole clip at once rather than streaming, that’s fine too. Just send it as a single chunk with more omitted (or false).

Whether the audio came from /speak or /audio, POST /v1/sessions/{id}/interrupt stops it immediately, useful for barge-in the same way it is with /speak. See Bring your own brain.