Voice preview
Voice preview
Hear a voice before you ship it. Every preview endpoint returns a audio/wav body you can play directly — no polling, no job id.
There are two ways to preview, and they answer different questions:
| Endpoint | Question it answers |
|---|---|
POST /api/agents/{id}/voice-preview | "What will this agent sound like on a call?" |
POST /api/providers/tts/{provider}/preview<br/>POST /api/providers/s2s/{provider}/preview | "What does this voice sound like?" — before anything is saved |
All of them accept an API key (x-api-key) or a dashboard JWT.
Preview a saved agent
POST /api/agents/{id}/voice-preview
Loads the agent and auditions the configuration that is actually stored, using the same keys a live call would use. It also picks the engine the way a call does: if the agent's s2sConfig names a provider, you hear the realtime model — otherwise you hear its TTS voice. That matters, because a realtime agent never plays its ttsVoice, so previewing that field would play a voice the caller will never hear.
curl -X POST https://api.telenow.ai/api/agents/{id}/voice-preview \
-H "x-api-key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{"text": "Hi, thanks for calling Acme. How can I help?"}' \
--output preview.wav
| Field | Type | Notes |
|---|---|---|
text | string | Optional. What the voice should say. Defaults to a short sample. Capped at 500 characters (200 on the realtime engine). |
The response body is the audio. Which engine answered comes back in headers, so the body stays playable:
| Header | Example | Meaning |
|---|---|---|
X-Voice-Engine | s2s / cascade | Which stack produced this audio |
X-Voice-Provider | openai / elevenlabs | The engine that spoke |
X-Voice-Model | gpt-realtime | Realtime model (empty on the cascade) |
X-Voice-Id | marin | The voice that spoke (a cloned voice is reported resolved) |
These four are CORS-exposed, so browser clients can read them.
Check X-Voice-Engine when you expect a realtime agent: cascade means the agent will run the STT→LLM→TTS stack instead — usually because the realtime engine is not enabled on this deployment. This route deliberately mirrors what a call would do rather than refusing, so what you hear is always what the caller will hear.
If the agent's voice is a cloned voice, this route resolves it to the real provider voice — so the clone is what you hear, not a stand-in.
Preview a voice before saving
Use these while a user is still choosing — they take the settings in the request body and touch nothing stored.
Text-to-speech
POST /api/providers/tts/{provider}/preview
curl -X POST https://api.telenow.ai/api/providers/tts/elevenlabs/preview \
-H "x-api-key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from Acme.", "voice": "9BWtsMINqrJLrRacOk9x"}' \
--output sample.wav
| Field | Type | Notes |
|---|---|---|
text | string | Required. Capped at 500 characters. |
voice | string | Voice id from GET /api/providers/tts/{provider}/voices. Omit for the provider's default. |
config | object | Optional — the same shape as an agent's ttsConfig (model, language, tunable settings, and a BYOK apiKey). Pass it so the preview matches what a real call will sound like. |
Realtime (speech-to-speech)
POST /api/providers/s2s/{provider}/preview
Opens one short real session with the engine and has the model speak your line, so what you hear is genuine realtime output rather than a TTS approximation.
curl -X POST https://api.telenow.ai/api/providers/s2s/openai/preview \
-H "x-api-key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-realtime", "voice": "marin"}' \
--output sample.wav
| Field | Type | Notes |
|---|---|---|
model | string | Required. A model id from GET /api/catalog/s2s. |
voice | string | Required. A voice that model offers. |
text | string | Optional line to speak. Capped at 200 characters. |
config | object | Optional — the agent's s2sConfig bag, including a BYOK apiKey. On gemini the voice-direction keys ride here too (audioProfile, scene, directorNotes, accent, language), so the sample is spoken in the configured accent and language, not the model's default. |
Returns 404 when the realtime engine is not enabled on the deployment — check enabled on GET /api/catalog/s2s first. (The by-agent route above does not: it falls through to the cascade, because that is what the agent's calls do.)
Rate limits and cost
Every preview opens a real provider session on a real key, so it costs money — your own when you pass a BYOK apiKey, the platform's otherwise. They are throttled per organization, independently for each kind:
| Kind | Minimum gap between previews |
|---|---|
| Text-to-speech | 1.5 seconds |
| Realtime | 6 seconds |
Exceeding it returns 429 carrying a Retry-After header and a retryAfter field (whole seconds, rounded up) in the body. The two kinds are throttled independently, so auditioning a TTS voice never locks out the realtime picker.
Previews are meant to be driven by a person auditioning voices — build a picker, not a batch job. A read-only API key (role member or viewer) cannot call them at all, because a preview spends against a real vendor key. POST /api/agents/{id}/voice-preview additionally refuses when the org is suspended or out of credit. Previews themselves are not metered or billed per call — the throttle is the only ceiling, which is why it is strict. If you need bulk synthesis, use the App AI gateway (ai:tts), which is metered instead.
Errors
| Status | Meaning |
|---|---|
400 | The request was rejected before or by the vendor — an unsupported provider, a failed init, or a TTS 4xx (usually a voice id that doesn't exist on the key in use, BYOK vs. platform). The message carries the provider's own detail. |
401 | Missing or invalid credential. |
403 | The API key's role is read-only (member/viewer), or the org is suspended / out of credit. |
404 | Unknown agent — or an agent in another organization, which is deliberately indistinguishable from an unknown id. Also returned by /providers/s2s/{provider}/preview when the realtime engine is disabled here. |
429 | Throttled — see the table above. |
502 | The engine ran but failed or produced no audio (code: provider_error). Not worth retrying when the message names a voice or a key. |
Related
- Agents API — configuring
ttsVoiceands2sConfig - Catalog & providers — discovering voices and realtime models
- Authentication — API keys and roles