# Provider failover

A provider that fails before it answers is passed over without you noticing. One that fails mid-answer is your call, fixed before the request is sent. Either way the answer names who gave it.

## Failure before the model speaks

Invisible. A provider that refuses or breaks before producing output is stepped over, the next candidate is tried,
and you are never told, under either streaming policy. What reaches you is the answer, from whoever gave it, named
in the reply.

```json
"model": { "name": "kimi-k3", "tier": "xhigh", "publisher": "MoonshotAi", "provider": "TogetherAi" }
```

This is why you read `model` back rather than assuming it echoes your request. The
[call record](/docs/gateway/reference/generation/) lists every provider tried, in order, with the status each ended on.

## Failure after the model has started

On [`POST /models`](/docs/gateway/reference/chat/) it is your call, set in `streamFailureBehavior` and fixed before
dispatch: changing it requires a new request. Momor will not silently stitch two providers' half-answers together in
either mode.

| `streamFailureBehavior` | Behaviour |
|---|---|
| `"stop"` (default) | The break is the ending. No second provider speaks into a stream the first was already answering. No `stop`, no usage record. |
| `"rollover"` | The next rung finishes what the first started, on the same connection, and the frames say where that happened. |

```http
# stop
streamStart:Anthropic → contentStart → content:"It is " → contentEnd
  → streamError:Anthropic → error:502:The provider closed the connection mid-answer.

# rollover
streamStart:Anthropic → contentStart → content:"The three causes were " → contentEnd
  → streamError:Anthropic → error:502:…
  → streamStart:Groq → contentStart → content:"a stale cache, a retry storm, and a bad deploy." → contentEnd
  → stop:stop
```

> **The frames name both sides**
> `streamError` names who stopped; the second `streamStart` names who took over. If you render partial answers, that
> pair is your cue to show the handover rather than splice the text.

A [Chat Completions](/docs/gateway/reference/chat-completions/) stream always stops: a failure after output ends the
stream on one error chunk, with no `[DONE]`, and the call is never moved to another provider once output has begun.

## What this means for a UI

A design that renders a stream as one undifferentiated column of text cannot show a handover. Give the answer a
provider-attributed structure from the start: each `streamStart` opens a segment stamped with the model and provider
that produced it, and the `streamError` between segments is a visible seam, not a swallowed retry.

The `usage` on `stop` reports the provider that **finished**. Tokens spent before the break were spent with the
first provider.

## Moving a conversation between providers

A conversation is not tied to the model that started it. Send a turn back as history and route it anywhere.
Reasoning, open tool calls, tool results and files all travel; what a destination can carry natively it carries, and
what it cannot is adapted and labelled rather than dropped.

```json
{
  "model": "gemini-3.6-flash",
  "systemPrompt": "Answer in one sentence.",
  "messages": [
    { "type": "user",     "value": "What is the capital of France?" },
    { "type": "thinking", "value": "Recall the capitals of Europe.", "data": { "anthropic.signature": "…" } },
    { "type": "content",  "value": "Checking the almanac." },
    { "type": "toolCall", "call": { "id": "toolu_01", "name": "almanac", "arguments": "{\"q\":\"capital\"}" } },
    { "type": "toolResult", "result": { "id": "toolu_01", "name": "almanac", "content": "Paris" } }
  ]
}
```

| Destination | What happens to reasoning |
|---|---|
| gemini, vertex | Becomes a thought part carrying the text. Another family's signature is not sent, because Gemini cannot verify it. |
| Groq, TogetherAi, MoonshotAi, Fireworks, DeepInfra | Rides the host's own reasoning field on the assistant message. |
| azure | Rides Kimi's reasoning field for the Kimi models, and goes back as the assistant's text for every other model. |
| Mistral | Becomes a thinking part ahead of the answer in the assistant's content, carrying the signature Mistral issued where it issued one. |
| Anthropic | Replayed as its own signed thinking block where Anthropic issued the signature. Otherwise sent as text prefixed `[reasoning]`, so the model cannot read a predecessor's working as something it already said aloud. |

Tool identities survive the crossing. Your history keeps every id as you received it, and each destination is handed
the ids in the form it takes:

| Destination | The ids it is sent |
|---|---|
| Anthropic | Yours, with any character other than a letter, a digit, `_` or `-` written as `_`. Gemini's `functions.almanac:1` becomes `functions_almanac_1`. |
| OpenAI | Yours, with any id longer than 64 characters shortened to 64. |
| Mistral's models, on every host | Nine letters and digits: an id already in that form as it is, any other as a stable hash of itself. |
| Kimi K2.5 and K2.6 | `functions.{name}:{index}`, the form the model was trained on, numbered across the conversation. |
| Every other model | Yours, as they are. |

Every rewritten id is made the same way on every turn, so a call and its result stay paired and a provider's prompt
cache still matches the conversation. You never normalise an id before sending it, and the ids in an answer are the
serving provider's own. A completed tool is never re-executed because history was adapted.

## What crossing costs

No provider can verify another's signed reasoning state, so what crosses is the readable text plus whatever state
the destination can carry. Reasoning that started on Anthropic and moves to Gemini arrives without Anthropic's
signature; moving back, it arrives as labelled text rather than a signed block.

If exact reasoning fidelity matters more than provider independence for a conversation, pin the provider for it.
