Skip to main content
Convert a chat message to speech and stream the audio. Audio is generated via our TTS provider on first request and cached automatically — subsequent requests are served from cache with no added latency.
The response is a WAV stream (RIFF container, IEEE float32 PCM, mono, 44.1 kHz). Standard audio players — browser <audio>, Web Audio decodeAudioData, ffplay, afplay, expo-av — play it directly with no custom decoding.

Request

Authorization
string
required
Bearer token obtained from the authenticate endpoint.
message_id
string
required
ID of the chat message to synthesize. The message must belong to your organization.

Response

Returns a streaming audio/wav body.
PropertyValue
Content-Typeaudio/wav
ContainerWAV (RIFF)
EncodingPCM float32, little-endian
Sample rate44100 Hz
ChannelsMono
Content-Dispositioninline; filename="message-{message_id}.wav"
During the first request for a message the audio is streamed live; the WAV header’s size fields are written as 0xFFFFFFFF (read-until-EOF). Cached responses carry real sizes, so seek and duration work from the first byte.

Playback

Browser (Web Audio API)

async function playMessageAudio(messageId, token) {
  const response = await fetch(
    `https://api.trellis.sh/v1/tts/messages/${messageId}/audio`,
    { headers: { Authorization: `Bearer ${token}` } }
  );

  const audioCtx = new AudioContext();
  const audioBuffer = await audioCtx.decodeAudioData(await response.arrayBuffer());

  const source = audioCtx.createBufferSource();
  source.buffer = audioBuffer;
  source.connect(audioCtx.destination);
  source.start();
}

Browser (<audio> element)

async function playMessageAudio(messageId, token) {
  const response = await fetch(
    `https://api.trellis.sh/v1/tts/messages/${messageId}/audio`,
    { headers: { Authorization: `Bearer ${token}` } }
  );
  const blob = await response.blob();
  const audio = new Audio(URL.createObjectURL(blob));
  await audio.play();
}

React Native (Expo AV)

import { Audio } from "expo-av";
import * as FileSystem from "expo-file-system";

async function playMessageAudio(messageId, token) {
  const localPath = FileSystem.cacheDirectory + `${messageId}.wav`;

  await FileSystem.downloadAsync(
    `https://api.trellis.sh/v1/tts/messages/${messageId}/audio`,
    localPath,
    { headers: { Authorization: `Bearer ${token}` } }
  );

  const { sound } = await Audio.Sound.createAsync({ uri: localPath });
  await sound.playAsync();
}
curl -X GET "https://api.trellis.sh/v1/tts/messages/8f14e45f-ceea-467f-a83c-0a06b8901a45/audio" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  --output message_audio.wav
import requests

message_id = "8f14e45f-ceea-467f-a83c-0a06b8901a45"

response = requests.get(
    f"https://api.trellis.sh/v1/tts/messages/{message_id}/audio",
    headers={"Authorization": "Bearer YOUR_TOKEN"},
    stream=True,
)

with open("message_audio.wav", "wb") as f:
    for chunk in response.iter_content(chunk_size=4096):
        f.write(chunk)
const messageId = "8f14e45f-ceea-467f-a83c-0a06b8901a45";

const response = await fetch(
  `https://api.trellis.sh/v1/tts/messages/${messageId}/audio`,
  {
    headers: { Authorization: "Bearer YOUR_TOKEN" },
  }
);

const wavBuffer = await response.arrayBuffer();
// See Playback section above — the buffer is a self-describing WAV,
// decode with AudioContext.decodeAudioData or play via `<audio>` + blob URL.
<binary WAV audio stream>
{
  "detail": "Missing Authorization header"
}
{
  "detail": "Message not found"
}
{
  "detail": "Audio generation failed"
}