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.
Sending audio
Section titled “Sending audio”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, }, );}Chunking a long utterance
Section titled “Chunking a long utterance”The more query parameter tells 2wai whether to expect additional audio for the current utterance:
more=trueon every chunk except the lastmore=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).
Audio format
Section titled “Audio format”Interrupting
Section titled “Interrupting”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.
- Bring your own brain to also replace what the avatar decides to say.
- Sessions for how audio-driven sessions fit into the wider lifecycle.