# Sarvam models, languages and voices available to an agent

Source: https://docs.usetone.ai/reference/catalog/voice

> The legal values for an agent speech stack, fetched from the provider rather than hardcoded — so this changes without a deploy and every list here is an OPEN set.

`GET /v1/catalog/voice`
The legal values for an agent speech stack, fetched from the provider rather than hardcoded — so this changes without a deploy and every list here is an OPEN set. 🔴 A voice belongs to a specific model version: the `(ttsModel, ttsVoice)` pair is the constraint, not the voice name, and each speaker names the model it belongs to. A retired voice is marked deprecated rather than removed, so an agent already using one keeps working.

Requires the `read` scope.

## Response

| Field | Type | | Meaning |
|---|---|---|---|
| `defaultAgentLanguages` | string[] | required | The language list a new agent starts with. |
| `fetchedAt` | string | required | When the underlying document was last fetched successfully. RFC 3339, UTC. |
| `languageNames` | object | required | Display names per language code, for building a picker. |
| `llm` | object | required | What an agent can think on. |
| `llm.defaults` | object | required | What a new agent gets when it names no model. |
| `llm.defaults.model` | string | required | The default language model id. |
| `llm.models` | object[] | required | Every language model an agent may be set to. |
| `llm.models[].deprecated` | boolean | optional | Sarvam no longer lists this. Still accepted on save; hide it from new pickers. |
| `llm.models[].description` | string | required | One line on what this is and when to choose it, written for the person picking. |
| `llm.models[].id` | string | required | The value to send when selecting this language model. |
| `llm.models[].label` | string | required | Human-readable name, for a picker. |
| `llm.models[].provider` | string | required | Who serves this model. Open set — handle an unrecognised value rather than throwing. Today: `gemini`, `sarvam`. |
| `previewSamples` | object | required | The sentence each voice preview speaks, in native script. |
| `source` | string | required | `spec` — parsed from Sarvam’s published document. `seed` — the built-in floor, served before the first successful fetch or when the document is unreachable. Open set — handle an unrecognised value rather than throwing. Today: `spec`, `seed`. |
| `stt` | object | required | What an agent can listen with. |
| `stt.autoDetect` | string | required | The language code that means "detect it". Understood far wider than TTS can speak. |
| `stt.defaults` | object | required | What a new agent gets when it names no transcription settings. |
| `stt.defaults.language` | string | required | The default language code, usually the auto-detect value. |
| `stt.defaults.mode` | string | required | The default mode id. |
| `stt.defaults.model` | string | required | The default transcription model id. |
| `stt.languages` | string[] | required | Language codes the transcriber understands. Wider than the set text-to-speech can speak, which is why a detected language is clamped to the agent own language list before a reply is synthesised. |
| `stt.models` | object[] | required | Every transcription model an agent may be set to. |
| `stt.models[].deprecated` | boolean | optional | Sarvam no longer lists this. Still accepted on save; hide it from new pickers. |
| `stt.models[].description` | string | required | One line on what this is and when to choose it, written for the person picking. |
| `stt.models[].id` | string | required | The value to send when selecting this transcription model. |
| `stt.models[].label` | string | required | Human-readable name, for a picker. |
| `stt.models[].supportsModes` | boolean | required | `mode` is a Saaras v3+ parameter — saarika rejects it outright. |
| `stt.modes` | object[] | required | How the transcriber handles language — transcribing faithfully versus translating. |
| `stt.modes[].description` | string | required | What this does to the text the model sees. Not cosmetic — the wrong mode silently invalidates a prompt written for a different transcript shape. |
| `stt.modes[].id` | string | required | The value to send when selecting this mode. |
| `stt.modes[].label` | string | required | Human-readable name, for a picker. |
| `tts` | object | required | What an agent can speak with. |
| `tts.defaults` | object | required | What a new agent gets when it names no voice. |
| `tts.defaults.model` | string | required | The default speech model id. |
| `tts.defaults.speaker` | string | required | The default voice id, valid for the default model. |
| `tts.languages` | string[] | required | Language codes the speech models can speak. |
| `tts.models` | object[] | required | Every speech model an agent may be set to. |
| `tts.models[].deprecated` | boolean | optional | Sarvam no longer lists this. Still accepted on save; hide it from new pickers. |
| `tts.models[].description` | string | required | One line on what this is and when to choose it, written for the person picking. |
| `tts.models[].id` | string | required | The value to send when selecting this speech model. |
| `tts.models[].label` | string | required | Human-readable name, for a picker. |
| `tts.models[].options` | object[] | required | Tuning knobs THIS model accepts — model-specific and mutually exclusive. v3 dropped `pitch`/`loudness` and added `temperature`; sending a key the model does not know is a 400 mid-call. |
| `tts.models[].options[].default` | number | required | What the model uses when the option is not set. |
| `tts.models[].options[].description` | string | required | One line on what this is and when to choose it, written for the person picking. |
| `tts.models[].options[].key` | string | required | The option name, as a key of `voice.ttsOptions` on an agent. |
| `tts.models[].options[].label` | string | required | Human-readable name, for a picker. |
| `tts.models[].options[].max` | number | required | Highest accepted value. 🔴 Trust this over any vendor prose — Sarvam documents `temperature` up to 2.0 and the API rejects anything above 1.0, so this catalog carries the corrected ceiling. |
| `tts.models[].options[].min` | number | required | Lowest accepted value. |
| `tts.models[].options[].step` | number | required | Granularity, for rendering a slider. |
| `tts.sampleRates` | integer[] | required | Output sample rates in Hz. |
| `tts.speakers` | object[] | required | Every voice, each naming the model it belongs to. 🔴 The (model, speaker) pair is the constraint, not the name — a voice from one model version is rejected on another, at write time rather than mid-call. |
| `tts.speakers[].deprecated` | boolean | optional | Sarvam no longer lists this. Still accepted on save; hide it from new pickers. |
| `tts.speakers[].gender` | string | required | The voice's apparent gender, for building a picker. Open set — handle an unrecognised value rather than throwing. Today: `female`, `male`, `unknown`. |
| `tts.speakers[].id` | string | required | The value to send when selecting this voice. |
| `tts.speakers[].label` | string | required | Human-readable name, for a picker. |
| `tts.speakers[].models` | string[] | required | Which TTS models accept this voice — almost never both. The `(model, speaker)` PAIR is the constraint, not the name: a v3 voice is a hard 400 on v2. |
| `version` | string | required | Content hash of everything below. Also the ETag, and the first segment of every voice-preview URL — so a catalog change mints new preview URLs instead of serving stale audio from an immutable cache. |

### Example response

```json
{
  "data": {
    "defaultAgentLanguages": [
      "en-IN",
      "hi-IN"
    ],
    "fetchedAt": "2026-08-25T04:00:00Z",
    "languageNames": {},
    "llm": {
      "defaults": {
        "model": "gemini-2.5-flash"
      },
      "models": [
        {
          "deprecated": true,
          "description": "Latest speech model. Adds `temperature`; drops `pitch` and `loudness`.",
          "id": "gemini-2.5-flash",
          "label": "Bulbul v3",
          "provider": "gemini"
        }
      ]
    },
    "previewSamples": {},
    "source": "spec",
    "stt": {
      "autoDetect": "auto",
      "defaults": {
        "language": "auto",
        "mode": "transcribe",
        "model": "saaras:v3"
      },
      "languages": [
        "en-IN",
        "hi-IN",
        "gu-IN",
        "ta-IN"
      ],
      "models": [
        {
          "deprecated": true,
          "description": "Latest speech model. Adds `temperature`; drops `pitch` and `loudness`.",
          "id": "saaras:v3",
          "label": "Bulbul v3",
          "supportsModes": true
        }
      ],
      "modes": [
        {
          "description": "Transcribes faithfully in the language spoken, without translating.",
          "id": "transcribe",
          "label": "Bulbul v3"
        }
      ]
    },
    "tts": {
      "defaults": {
        "model": "bulbul:v3",
        "speaker": "anushka"
      },
      "languages": [
        "en-IN",
        "hi-IN",
        "gu-IN"
      ],
      "models": [
        {
          "deprecated": true,
          "description": "Latest speech model. Adds `temperature`; drops `pitch` and `loudness`.",
          "id": "bulbul:v3",
          "label": "Bulbul v3",
          "options": [
            {
              "default": 1,
              "description": "Latest speech model. Adds `temperature`; drops `pitch` and `loudness`.",
              "key": "pace",
              "label": "Bulbul v3",
              "max": 2,
              "min": 0.5,
              "step": 0.1
            }
          ]
        }
      ],
      "sampleRates": [
        8000,
        16000,
        22050
      ],
      "speakers": [
        {
          "deprecated": true,
          "gender": "female",
          "id": "simran",
          "label": "Bulbul v3",
          "models": [
            "bulbul:v3"
          ]
        }
      ]
    },
    "version": "c7f2a9e1b4d6"
  }
}
```
