> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vocily.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Backups

> Give the transcriber, the model and the voice a second provider to switch to if the first stops responding.

Each of the three engines an agent runs on - `transcriber`, `model` and `voice` - can have a
`fallback`: a second one, **from a different provider**, that takes over if the first provider stops
responding during a call.

You set them with [`PATCH /v1/agents/{agent_id}`](/developers/agents/update) (or on create), like
any other setting.

## What to send

| Block                  | Send                          | Filled in for you | Example                                         |
| ---------------------- | ----------------------------- | ----------------- | ----------------------------------------------- |
| `transcriber.fallback` | `model`                       | `provider`        | `{ "model": "nova-3-general" }`                 |
| `model.fallback`       | `model`                       | `provider`        | `{ "model": "gemini-3.5-flash-lite" }`          |
| `voice.fallback`       | `provider` **and** `voice_id` | `model`           | `{ "provider": "sarvam", "voice_id": "shubh" }` |

A model id belongs to exactly one provider, so for the transcriber and the model you only name the
model - a `provider` you send is ignored. A voice id does not say whose voice it is, so the voice
backup needs both.

Pick values from the catalogues: [`GET /v1/transcribers`](/developers/catalogues/transcribers),
[`GET /v1/models`](/developers/catalogues/models), and
[`GET /v1/voices?provider=`](/developers/catalogues/voices).

## Example

```http theme={"dark"}
PATCH /v1/agents/{agent_id}
```

```json theme={"dark"}
{
  "transcriber": { "fallback": { "model": "nova-3-general" } },
  "model":       { "fallback": { "model": "gemini-3.5-flash-lite" } },
  "voice":       { "fallback": { "provider": "sarvam", "voice_id": "shubh" } }
}
```

The response is the whole agent. Each block now shows its backup in full, next to the main engine it
stands in for:

```json theme={"dark"}
{
  "transcriber": {
    "provider": "sarvam", "model": "saaras:v4",
    "fallback": { "provider": "deepgram", "model": "nova-3-general" }
  },
  "model": {
    "provider": "openai", "model": "gpt-5.4-nano",
    "fallback": { "provider": "gemini", "model": "gemini-3.5-flash-lite" }
  },
  "voice": {
    "provider": "vocily", "model": "v2",
    "fallback": { "provider": "sarvam", "model": "bulbul:v3", "voice_id": "shubh" }
  }
}
```

**Read `fallback` back to confirm it was saved.** If it comes back `null`, there is no backup.

## Rules

### The backup must be from a different provider

What fails during an outage is the provider - it is unreachable, the key is rejected, the region is
down - and every model behind it fails together. So a backup on the **same** provider as the main
engine is refused:

```json theme={"dark"}
// model is gpt-5.4-nano (openai), and so is this backup
{ "model": { "fallback": { "model": "gpt-5-mini" } } }
```

```json theme={"dark"}
{ "detail": { "code": "document_invalid",
  "message": "model.fallback: 'gpt-5-mini' is on the same provider as the main model (openai), so it would fail with it. Choose one from a different provider." },
  "code": "BAD_REQUEST" }
```

Changing the **main** engine onto the backup's provider is the same clash, and refused the same way.

### Changing one field keeps the others

`fallback` merges like the rest of the body. `{ "voice": { "fallback": { "voice_id": "anushka" } } }`
changes the backup voice and keeps its provider.

The exception is the voice backup's **provider**: a voice belongs to its provider, so switching
provider without a `voice_id` clears the old voice and the request is refused until you name one.
Send both together:

```json theme={"dark"}
{ "voice": { "fallback": { "provider": "cartesia", "voice_id": "<a Cartesia voice id>" } } }
```

### Remove a backup with null

```json theme={"dark"}
{ "model": { "fallback": null } }
```

## During a call

The backup is only used if the main provider stops responding. When it is, the agent **switches for
the rest of that call** - it does not go back and forth.

| Block         | What the caller notices                                                            |
| ------------- | ---------------------------------------------------------------------------------- |
| `transcriber` | A short announcement, and the conversation returns to the agent's primary language |
| `voice`       | A short announcement, and the conversation returns to the primary language         |
| `model`       | A pause on that turn, and nothing else                                             |

A backup only has to support the agent's **primary** language, which is why a failover returns the
conversation to it - see [If a provider fails mid-call](/building-agents/language#if-a-provider-fails-mid-call).
The call is billed at the engine it actually ran on.

## Errors

| Status | `detail.code`          | When                                                          | `detail.message`                                                                             |
| ------ | ---------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `400`  | `voice_config_invalid` | `transcriber.fallback.model` is not in `GET /v1/transcribers` | `Unsupported backup speech model: nope. Supported: …`                                        |
| `400`  | `voice_config_invalid` | `model.fallback.model` is not in `GET /v1/models`             | `Unsupported backup LLM model: nope. Supported: …`                                           |
| `400`  | `voice_config_invalid` | `voice.fallback` has no `provider`                            | `The backup voice names no provider, so it could never be used. Choose one of: …`            |
| `400`  | `voice_config_invalid` | `voice.fallback.provider` is not a voice provider             | `Unknown backup voice provider 'acme'. Supported: …`                                         |
| `400`  | `voice_config_invalid` | `voice.fallback` has no `voice_id`                            | `The backup voice names no voice. A provider alone cannot speak — choose one of its voices.` |
| `400`  | `document_invalid`     | the backup is on the main engine's own provider               | `model.fallback: 'gpt-5-mini' is on the same provider as the main model (openai)…`           |
| `422`  | `validation_error`     | `fallback` is not an object, e.g. a bare string               | `model.fallback: Must be an object.`                                                         |

A backup the configuration itself refuses reads `voice_config_invalid`:

```json theme={"dark"}
{
  "detail": {
    "code": "voice_config_invalid",
    "message": "Unsupported backup LLM model: nope. Supported: gemini-3.5-flash, gemini-3.5-flash-lite, …"
  },
  "code": "BAD_REQUEST"
}
```

The same-provider clash reads `document_invalid`: the field is real and the value well-formed, but
the agent cannot use it - the example above under [Rules](#rules) shows it in full.

A `422` lists every problem in the request - see
[Validation errors](/developers/api-reference#validation-errors).
