Voices and languages
Twelve Indian languages, and the (model, voice) pairing rule that catches most first-time mistakes.
Read the current options rather than hardcoding them:
curl "$TONE_API/v1/catalog/voice" -H "Authorization: Bearer $TONE_KEY"π΄ This catalog is fetched from the speech provider, not hardcoded by us. It
changes without a Tone deploy, which is why every list in it is an open set.
A voice that is retired is marked deprecated and hidden from pickers, but keeps
working for agents already using it β a vendor's documentation edit must not
break a tenant that changed nothing.
The pairing rule
A voice belongs to a specific model version. The constraint is the
(ttsModel, ttsVoice) pair, not the voice name:
{ "ttsModel": "bulbul:v3", "ttsVoice": "simran" } // ok
{ "ttsModel": "bulbul:v2", "ttsVoice": "simran" } // 422 β simran is a v3 voiceTone rejects a bad pair when you save the agent, rather than letting you find out
mid-call. The request is well-formed, so the answer is a 422
unprocessable_entity, not a 400: the pair is judged
against the live catalog. details.path names the field (["ttsVoice"]), and the
message lists the voices that model does have. Each speaker in the catalog names the
model it belongs to; use that rather than assuming a name carries across versions.
Model-specific tuning is the same trap. bulbul:v2 takes pace, pitch and
loudness; v3 dropped the last two and added temperature. An option the model
does not take is refused the same way, with details.path naming it.
A voice may speak fewer languages than its model. Every Sarvam voice speaks
every catalog language. A voice from another vendor β Murf's falcon-2, or
Cartesia's sonic-3.6 where it is offered β speaks only the codes in its
speakers[].languages, and saving an agent with a language its voice cannot
speak is the same 422, with details.path pointing at that entry of
languages. Cartesia voices also carry a persona.language: the accent the
voice was recorded in.
The voice a new agent gets
Leave ttsModel and ttsVoice out when you create an agent and Tone picks one
for its languages: a Murf voice (falcon-2) that speaks every language on
the list, female like the catalog's default speaker, preferring one native to the
first language. When no Murf voice speaks them all, the agent gets the catalog
default, bulbul:v3 / simran, which speaks every catalog language. Name a voice
yourself and that is what you get.
{ "voice": { "languages": ["hi-IN", "en-IN"] } } // β falcon-2 / en-IN-pooja
{ "voice": { "languages": ["gu-IN", "en-IN", "hi-IN"] } } // β bulbul:v3 / simranWhich Murf voices speak which languages changes with the vendor's catalog, so read
speakers[].languages from GET /v1/catalog/voice rather than relying on these
examples.
Languages
Transcription understands more languages than speech can speak. With auto-detect on, a caller speaking one of the wider set is detected correctly β but replying in a language the speech model cannot produce would simply fail.
So a detected language is clamped to the agent's own languages list. Set it to
the languages you actually want to handle:
{ "languages": ["hi-IN", "en-IN"] }An empty list means "no opinion" β adopt whatever was detected. That is rarely what you want on a production agent.
Code-switching
Indian callers switch language mid-sentence constantly, and the stack is built
for it: detection runs per utterance, not once at the start of the call. An
agent with ["hi-IN", "en-IN"] will follow a caller who opens in English and
answers in Hindi.