---
title: Rechazos y fallback
url: https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback
description: Cómo los modelos Claude Fable, los modelos Claude Opus, Claude Sonnet 5.5 y Claude Haiku 5.5 devuelven rechazos del clasificador y cómo reintentar las solicitudes rechazadas en un modelo de fallback.
---

Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5 y Claude Haiku 5.5 incluyen clasificadores de seguridad que pueden rechazar una solicitud. Cuando eso ocurre, recibes una respuesta normal, no un error, con `stop_reason: "refusal"`. Su `stop_details.category` indica el área de política (consulta [Cómo se ve un rechazo](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#refusal-response)). Por lo general, aún puedes obtener una respuesta enviando la misma solicitud a otro modelo de Claude. Esta página te muestra cómo reconocer un "refusal" (rechazo) y cómo configurar ese reintento.

Lee esta página cuando construyas sobre cualquiera de estos modelos y quieras que las solicitudes declinadas pasen automáticamente a otro modelo. También aplica cuando has visto `"refusal"` en una respuesta y quieres saber qué hacer a continuación.

Páginas relacionadas:

* [Motivos de parada y fallback](https://platform.claude.com/docs/es/build-with-claude/handling-stop-reasons): la lista completa de valores de `stop_reason`.
* [Crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit): cómo evitar pagar dos veces el costo de la caché de prompts cuando construyes el reintento tú mismo.
* [Middleware del SDK](https://platform.claude.com/docs/es/cli-sdks-libraries/middleware): el asistente del SDK que envuelve todo esto.
* [Cookbook de fallback y facturación](https://platform.claude.com/cookbook/fable-5-fallback-billing-guide): un ejemplo completo desarrollado de principio a fin.

La configuración más sencilla, en beta en la Claude API: establece `fallbacks` en `"default"`, y la API reintenta una solicitud rechazada en el modelo de fallback que Anthropic recomienda para su categoría de rechazo. Para las categorías sin un fallback recomendado, el rechazo se mantiene. Claude Haiku 5.5 no tiene fallback del lado del servidor, así que para él usa [fallback del lado del cliente con el middleware del SDK](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#client-side-fallback) o [escribe el reintento tú mismo](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#manual-retry).

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: server-side-fallback-2026-07-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-fable-5",
      "max_tokens": 1024,
      "fallbacks": "default",
      "messages": [{"role": "user", "content": "Hello, Claude"}]
    }'
  ```

  ```bash CLI
  ant beta:messages create \
    --model claude-fable-5 \
    --max-tokens 1024 \
    --message '{"role":"user","content":"Hello, Claude"}' \
    --fallbacks default \
    --beta server-side-fallback-2026-07-01
  ```

  ```python Python
  client = Anthropic()

  response = client.beta.messages.create(
      model="claude-fable-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello, Claude"}],
      fallbacks="default",
      betas=["server-side-fallback-2026-07-01"],
  )
  print(response.model)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Claude" }],
    fallbacks: "default",
    betas: ["server-side-fallback-2026-07-01"]
  });
  console.log(response.model);
  ```

  ```csharp C#
  AnthropicClient client = new();

  BetaMessage response = await client.Beta.Messages.Create(
      new()
      {
          Model = Messages::Model.ClaudeFable5,
          MaxTokens = 1024,
          Messages = [new() { Content = "Hello, Claude", Role = Role.User }],
          Fallbacks = new Default(),
          Betas = [AnthropicBeta.ServerSideFallback2026_07_01],
      }
  );

  Console.WriteLine(response.Model.Raw());
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeFable5,
  	MaxTokens: 1024,
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
  	},
  	Fallbacks: anthropic.BetaFallbacksParamOfDefault(),
  	Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaServerSideFallback2026_07_01},
  })
  if err != nil {
  	panic(err)
  }

  fmt.Println(response.Model)
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  BetaMessage response = client.beta().messages().create(MessageCreateParams.builder()
      .model(Model.CLAUDE_FABLE_5)
      .maxTokens(1024L)
      .addUserMessage("Hello, Claude")
      .fallbacksDefault()
      .addBeta(AnthropicBeta.SERVER_SIDE_FALLBACK_2026_07_01)
      .build());

  IO.println(response.model().asString());
  ```

  ```php PHP
  $client = new Client();

  $response = $client->beta->messages->create(
      model: 'claude-fable-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      fallbacks: 'default',
      betas: ['server-side-fallback-2026-07-01'],
  );

  echo $response->model, PHP_EOL;
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    fallbacks: :default,
    betas: ["server-side-fallback-2026-07-01"]
  )

  puts response.model
  ```
</CodeGroup>

Las siguientes secciones cubren qué contiene una respuesta de rechazo, cuándo usar fallback del lado del servidor o del lado del cliente, y cómo se factura cada uno.

## Cómo se ve un rechazo

Un rechazo es una respuesta HTTP 200 exitosa con `stop_reason: "refusal"`:

```json
{
  "id": "msg_01XFUDYJgAACzvnptvVoYEL",
  "type": "message",
  "role": "assistant",
  "model": "claude-fable-5",
  "content": [],
  "stop_reason": "refusal",
  "stop_details": {
    "type": "refusal",
    "category": "cyber",
    "explanation": "This request was declined because it could enable cyber harm."
  },
  "usage": {
    "input_tokens": 412,
    "output_tokens": 0
  }
}
```

El objeto `stop_details` explica la declinación:

* **`category`:** nombra el área de política que activó el clasificador.
* **`explanation`:** una descripción legible para humanos. El texto no es estable, así que muéstralo en lugar de analizarlo.
* **`recommended_model`:** presente solo en solicitudes que establecen `fallbacks` ([fallback del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback), beta). Nombra un modelo para reintentar directamente cuando la API omitió el intento de fallback (por ejemplo, el modelo de fallback alcanzó su límite de velocidad), y es `null` en caso contrario. Es una sugerencia, no una garantía.
* `category` y `explanation` son ambos `null` cuando el rechazo no corresponde a una categoría con nombre. Ese `null` es un valor normal y permanente, no un marcador de posición.
* `stop_details` en sí es `null` para todos los motivos de parada distintos de `refusal`.

| `category`               | Qué significa                                                                                                                                                                                                                                                                                                                                                                                                                 | Se factura antes de cualquier salida |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `"cyber"`                | La solicitud podría facilitar daño cibernético, como el desarrollo de malware o exploits. El trabajo benigno de ciberseguridad también puede activar esta categoría.                                                                                                                                                                                                                                                          | No                                   |
| `"bio"`                  | La solicitud podría facilitar daño biológico, como métodos de laboratorio peligrosos. El trabajo beneficioso en ciencias de la vida también puede activar esta categoría.                                                                                                                                                                                                                                                     | Sí                                   |
| `"frontier_llm"`         | La solicitud podría ayudar al desarrollo de modelos de IA competidores, lo cual está restringido bajo los [términos comerciales de Anthropic](https://www.anthropic.com/legal/commercial-terms). El trabajo benigno de aprendizaje automático también puede activar esta categoría.                                                                                                                                           | Sí                                   |
| `"reasoning_extraction"` | La solicitud pide al modelo que reproduzca su razonamiento interno en el texto de la respuesta. Para obtener el razonamiento en forma estructurada, usa [pensamiento adaptativo](https://platform.claude.com/docs/es/build-with-claude/thinking). Consulta [Mantén el razonamiento en bloques de pensamiento](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks). | Sí                                   |
| `"general_harms"`        | La solicitud cae dentro de un área de la política de uso fuera de las cuatro categorías con nombre. El trabajo benigno también puede activar esta categoría.                                                                                                                                                                                                                                                                  | No                                   |

Un rechazo puede llegar antes de cualquier salida, o a mitad del stream después de una salida parcial. En cualquier caso, trata cualquier salida parcial como incompleta y descártala.

### Mantén el razonamiento en bloques de pensamiento

Un rechazo `reasoning_extraction` suele provenir de un prompt que pide al modelo que ponga su pensamiento o razonamiento en la salida, ya sea textualmente o en un formato fijo. Ejemplos comunes:

* Una sección `<thinking>`, `<reasoning>` o de borrador que el modelo completa antes de responder
* Un campo `reasoning`, `thinking` o `trace` en la salida JSON o en la entrada de una herramienta
* Las notas privadas del modelo, o un registro continuo de su razonamiento
* Razonamiento solicitado textualmente o de forma completa

La redacción puede estar en una indicación del sistema, una skill o la descripción de una herramienta, así que revisa también esos lugares.

Aún puedes pedirle a Claude que explique su respuesta. Pide una explicación breve, la evidencia detrás de un resultado o un resumen de las acciones que realizó. Para ver cómo razonó el modelo, establece `display: "summarized"` y lee los [bloques `thinking`](https://platform.claude.com/docs/es/build-with-claude/thinking#summarized-thinking). Esta categoría no tiene un modelo de fallback recomendado, así que cambia el prompt en lugar de reintentar la solicitud. Si una solicitud reformulada sigue siendo rechazada, [contacta al soporte](https://support.claude.com/en/articles/9015913-how-to-get-support) e incluye el [ID de solicitud](https://platform.claude.com/docs/es/api/errors#request-id).

## Cómo se facturan los rechazos

Estas reglas de facturación se aplican en todas las plataformas: la Claude API, Amazon Bedrock, Claude Platform on AWS, Google Cloud y Microsoft Foundry.

**Rechazos antes de cualquier salida:** Para frustrar los intentos de eludir las salvaguardas de Anthropic a gran escala, un rechazo que llega antes de cualquier salida se factura cuando su `stop_details.category` es `"bio"`, `"frontier_llm"` o `"reasoning_extraction"`. Estas son las categorías en las que Anthropic mide volúmenes bajos de falsos positivos, a septiembre de 2026. Estos rechazos se facturan como cualquier otra solicitud, a las tarifas del modelo que la ejecutó. Un rechazo antes de cualquier salida en cualquier otra categoría, o con una categoría `null`, no se factura. En cualquier caso, `content` está vacío y los conteos de tokens aparecen en `usage`. La solicitud aún cuenta contra tus límites de velocidad.

**Rechazos a mitad del stream:** Un rechazo a mitad del stream factura los tokens de entrada y la salida ya transmitida a las tarifas normales.

**Fallback:** Cuando usas fallback, el rechazo que lo activó se factura, además de la solicitud de fallback, cuando llegó a mitad del stream o pertenece a una de las categorías facturadas. El [crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit) compensa el fallo de caché de prompts de la solicitud de fallback, para que no pagues dos veces por almacenar en caché la conversación. Un rechazo de Claude Haiku 5.5 no incluye crédito de fallback, así que un fallback posterior paga el costo completo de escribir la caché de prompts del modelo de fallback. Para saber cómo el fallback del lado del servidor reporta cada intento, consulta [Facturación y límites de velocidad](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#billing-and-rate-limits).

Las categorías facturadas pueden cambiar a medida que Anthropic sigue midiendo y refinando las tasas de falsos positivos de sus salvaguardas. La columna **Se factura antes de cualquier salida** de la [tabla de categorías de rechazo](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#refusal-response) enumera las categorías facturadas.

## Elegir un enfoque de fallback

Hay tres formas de reintentar una solicitud rechazada en otro modelo. La correcta depende de dónde estés ejecutando y cuánto control necesites.

| Tu situación                                     | Usa                                                                                                                                                                                                              | Por qué                                                                    |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Claude API, configuración más simple             | [Fallback del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback)                                                                               | Una solicitud, una respuesta. La API maneja el reintento.                  |
| Cualquier plataforma, usando un SDK de Anthropic | [El middleware del SDK](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#client-side-fallback)                                                                                        | Configúralo una vez en el cliente. Los reintentos ocurren automáticamente. |
| HTTP directo o lógica de reintento personalizada | [Un reintento manual](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#manual-retry) con [crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit) | Control total. El crédito de fallback mantiene bajo el costo.              |

El fallback del lado del servidor y el middleware del SDK aplican el crédito de fallback por ti. Solo necesitas la página de [Crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit) cuando construyes el reintento tú mismo. Claude Haiku 5.5 no tiene fallback del lado del servidor ni crédito de fallback, así que usa el middleware del SDK o un reintento manual.

## Fallback del lado del servidor

El fallback del lado del servidor reintenta una solicitud rechazada dentro de una sola llamada a la API. En el modo predeterminado, cuando el modelo principal declina y la categoría de rechazo tiene un fallback recomendado, la API ejecuta la misma solicitud en el modelo que Anthropic recomienda para esa categoría. En su lugar, puedes [nombrar hasta tres modelos de fallback propios](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#naming-your-own-fallback-models). De cualquier forma, recibes una sola respuesta que nombra el modelo que respondió, de modo que tu usuario obtiene una respuesta en un solo viaje de ida y vuelta.

<Note>
  El fallback del lado del servidor está en beta en la Claude API. El parámetro `fallbacks` no es compatible con la [Message Batches API](https://platform.claude.com/docs/es/build-with-claude/batch-processing) (un elemento de lote que lo incluye se devuelve como un resultado con error) y no está disponible en Amazon Bedrock, Google Cloud ni Microsoft Foundry. En esas plataformas, usa [fallback del lado del cliente con el middleware del SDK](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#client-side-fallback) en su lugar. Claude Haiku 5.5 no tiene fallback del lado del servidor: con `fallbacks: "default"`, una solicitud rechazada permanece rechazada, y una lista de modelos de fallback devuelve un error 400.
</Note>

### Realizar la solicitud

Establece el parámetro `fallbacks` en la cadena `"default"` y envía el encabezado beta `server-side-fallback-2026-07-01`. La API entonces aplica el enrutamiento predeterminado definido por el servidor para el modelo solicitado, que selecciona un modelo de fallback recomendado según la categoría de rechazo que reporta el clasificador, de modo que las solicitudes rechazadas se atienden sin que tengas que mantener una lista de modelos a medida que cambian las recomendaciones.

El enrutamiento predeterminado nunca provoca el [rechazo anticipado por imagen sobredimensionada](https://platform.claude.com/docs/es/build-with-claude/vision-coordinates#oversized-image-error) para modelos que no elegiste: un modelo enrutado que redimensionaría una imagen marcada con `"oversized_image": "error"` se elimina del enrutamiento en su lugar, de modo que una imagen marcada nunca se sirve redimensionada.

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: server-side-fallback-2026-07-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-fable-5",
      "max_tokens": 1024,
      "fallbacks": "default",
      "messages": [{"role": "user", "content": "Hello, Claude"}]
    }' |
    jq -c '{
      stop_reason,
      model,
      # Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
      # combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
      served_by_fallback: (
        any(.usage.iterations[]?; .type == "fallback_message")
        and .stop_reason != "refusal"
      )
    }'
  ```

  ```bash CLI
  ant beta:messages create \
    --model claude-fable-5 \
    --max-tokens 1024 \
    --message '{"role":"user","content":"Hello, Claude"}' \
    --fallbacks default \
    --beta server-side-fallback-2026-07-01 \
    --format json |
    jq -c '{
      stop_reason,
      model,
      # Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
      # combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
      served_by_fallback: (
        any(.usage.iterations[]?; .type == "fallback_message")
        and .stop_reason != "refusal"
      )
    }'
  ```

  ```python Python
  client = Anthropic()

  response = client.beta.messages.create(
      model="claude-fable-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello, Claude"}],
      fallbacks="default",
      betas=["server-side-fallback-2026-07-01"],
  )

  # Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
  # combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
  fallback_ran = any(
      iteration.type == "fallback_message"
      for iteration in response.usage.iterations or []
  )
  served_by_fallback = fallback_ran and response.stop_reason != "refusal"

  print(
      json.dumps(
          {
              "stop_reason": response.stop_reason,
              "model": response.model,
              "served_by_fallback": served_by_fallback,
          }
      )
  )
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Claude" }],
    fallbacks: "default",
    betas: ["server-side-fallback-2026-07-01"]
  });

  // Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
  // combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
  const { stop_reason, model, usage } = response;
  const servedByFallback =
    (usage.iterations ?? []).some((entry) => entry.type === "fallback_message") &&
    stop_reason !== "refusal";

  console.log(
    JSON.stringify({
      stop_reason,
      model,
      served_by_fallback: servedByFallback
    })
  );
  ```

  ```csharp C#
  AnthropicClient client = new();

  var response = await client.Beta.Messages.Create(
      new()
      {
          Model = Messages::Model.ClaudeFable5,
          MaxTokens = 1024,
          Messages =
          [
              new() { Content = "Hello, Claude", Role = Role.User },
          ],
          Fallbacks = new Default(),
          Betas = [AnthropicBeta.ServerSideFallback2026_07_01],
      }
  );

  // Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
  // combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
  bool fallbackRan = (response.Usage.Iterations ?? []).Any(iteration =>
      iteration.TryPickBetaFallbackMessageIterationUsage(out _)
  );
  bool servedByFallback =
      fallbackRan && response.StopReason?.Value() != BetaStopReason.Refusal;

  Console.WriteLine(
      JsonSerializer.Serialize(
          new
          {
              stop_reason = response.StopReason?.Raw(),
              model = response.Model.Raw(),
              served_by_fallback = servedByFallback,
          }
      )
  );
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeFable5,
  	MaxTokens: 1024,
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
  	},
  	Fallbacks: anthropic.BetaFallbacksParamOfDefault(),
  	Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaServerSideFallback2026_07_01},
  })
  if err != nil {
  	panic(err)
  }

  // Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
  // combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
  fallbackRan := slices.ContainsFunc(
  	response.Usage.Iterations,
  	func(iteration anthropic.BetaIterationsUsageItemUnion) bool {
  		_, isFallback := iteration.AsAny().(anthropic.BetaFallbackMessageIterationUsage)
  		return isFallback
  	},
  )
  servedByFallback := fallbackRan && response.StopReason != anthropic.BetaStopReasonRefusal

  summary, err := json.Marshal(struct {
  	StopReason       anthropic.BetaStopReason `json:"stop_reason"`
  	Model            anthropic.Model          `json:"model"`
  	ServedByFallback bool                     `json:"served_by_fallback"`
  }{response.StopReason, response.Model, servedByFallback})
  if err != nil {
  	panic(err)
  }
  fmt.Println(string(summary))
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  BetaMessage response = client.beta().messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_FABLE_5)
          .maxTokens(1024L)
          .addUserMessage("Hello, Claude")
          .fallbacksDefault()
          .addBeta(AnthropicBeta.SERVER_SIDE_FALLBACK_2026_07_01)
          .build()
  );

  // Una entrada de uso fallback_message significa que un modelo de respaldo produjo la
  // respuesta; un stop_reason de refusal significa que ningún modelo la atendió.
  List<BetaUsage.Iteration> iterations =
      response.usage().iterations().orElse(List.of());
  boolean servedByFallback =
      iterations.stream().anyMatch(BetaUsage.Iteration::isFallbackMessage)
          && response.stopReason().filter(BetaStopReason.REFUSAL::equals).isEmpty();

  IO.println("""
      {"stop_reason":"%s","model":"%s","served_by_fallback":%b}\
      """.formatted(
          response.stopReason().map(BetaStopReason::asString).orElse("null"),
          response.model().asString(),
          servedByFallback));
  ```

  ```php PHP
  $client = new Client();

  $response = $client->beta->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      model: 'claude-fable-5',
      fallbacks: 'default',
      betas: ['server-side-fallback-2026-07-01'],
  );

  // Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
  // combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
  $iterations = $response->usage->iterations ?? [];
  $servedByFallback = array_any($iterations, fn($entry) => $entry->type === 'fallback_message')
      && $response->stopReason !== 'refusal';

  echo json_encode([
      'stop_reason' => $response->stopReason,
      'model' => $response->model,
      'served_by_fallback' => $servedByFallback,
  ]), PHP_EOL;
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    fallbacks: :default,
    betas: ["server-side-fallback-2026-07-01"]
  )

  # Una entrada fallback_message en usage.iterations indica que se ejecutó un modelo de respaldo;
  # combínala con stop_reason para confirmar que el respaldo sirvió la respuesta.
  iterations = response.usage.iterations || []
  served_by_fallback = iterations.any? { it.type == :fallback_message } &&
    response.stop_reason != :refusal

  stop_reason = response.stop_reason
  model = response.model
  puts JSON.generate({stop_reason:, model:, served_by_fallback:})
  ```
</CodeGroup>

Anthropic establece salvaguardas para cada modelo individualmente y para cada categoría de política, de acuerdo con la capacidad del modelo: dependiendo de la categoría, una solicitud marcada puede recurrir a un modelo menos capaz o ser declinada. El modo `"default"` codifica por ti estas recomendaciones por modelo y por categoría, de modo que una solicitud rechazada se reintenta en el modelo que Anthropic recomienda para esa categoría. Los fallbacks son visibles de cualquier forma: la respuesta nombra el modelo que la atendió, y el bloque de contenido `fallback` marca el traspaso.

El enrutamiento se aplica del lado del servidor y no se publica por modelo en la [Models API](https://platform.claude.com/docs/es/api/models/list). Para ver qué modelo atendió una solicitud rechazada, revisa el campo `model` de nivel superior de la respuesta y busca una entrada `fallback_message` en `usage.iterations`, como lo hacen los ejemplos de esta página.

Solo una declinación del clasificador de seguridad activa el fallback. Un límite de velocidad, una sobrecarga o un error del servidor en el modelo solicitado se te devuelve tal cual.

<Note>
  El encabezado beta debe llevar exactamente la fecha `2026-07-01`, que admite tanto `"default"` como la forma de lista explícita, o `2026-06-01`, que acepta solo la forma de lista explícita. Bajo cualquier otro valor `server-side-fallback-*`, el parámetro `fallbacks` se rechaza con un error 400. Si construiste contra una vista previa anterior de esta función, actualiza juntos el encabezado beta y las formas de solicitud y respuesta a las de esta página.
</Note>

### Nombrar tus propios modelos de fallback

En lugar del enrutamiento predeterminado, puedes establecer `fallbacks` en una lista de hasta tres modelos. Cuando el modelo solicitado declina, la API ejecuta el siguiente modelo de la cadena con la misma solicitud. Usa esta forma cuando quieras controlar exactamente qué modelos atienden las solicitudes rechazadas, como fijar un modelo que tu aplicación ha calificado.

Los modelos de fallback nombrados cuentan para la [verificación de imagen sobredimensionada](https://platform.claude.com/docs/es/build-with-claude/vision-coordinates#oversized-image-error): una solicitud cuyo bloque de imagen establece `"oversized_image": "error"` se verifica de antemano contra el modelo solicitado y cada fallback nombrado, se rechaza si cualquiera de ellos redimensionaría esa imagen, y el objetivo de reescalado reportado en el rechazo se ajusta a todos ellos.

Las líneas resaltadas son la única diferencia con respecto a la solicitud de enrutamiento predeterminado.

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: server-side-fallback-2026-07-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-fable-5",
      "max_tokens": 1024,
      "fallbacks": [{"model": "claude-opus-4-8"}],
      "messages": [{"role": "user", "content": "Hello, Claude"}]
    }'
  ```

  ```bash CLI
  ant beta:messages create \
    --model claude-fable-5 \
    --max-tokens 1024 \
    --message '{"role":"user","content":"Hello, Claude"}' \
    --fallbacks '[{"model":"claude-opus-4-8"}]' \
    --beta server-side-fallback-2026-07-01
  ```

  ```python Python
  client = Anthropic()

  response = client.beta.messages.create(
      model="claude-fable-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello, Claude"}],
      fallbacks=[{"model": "claude-opus-4-8"}],
      betas=["server-side-fallback-2026-07-01"],
  )
  print(response.model)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Claude" }],
    fallbacks: [{ model: "claude-opus-4-8" }],
    betas: ["server-side-fallback-2026-07-01"]
  });
  console.log(response.model);
  ```

  ```csharp C#
  AnthropicClient client = new();

  BetaMessage response = await client.Beta.Messages.Create(
      new()
      {
          Model = Messages::Model.ClaudeFable5,
          MaxTokens = 1024,
          Messages = [new() { Content = "Hello, Claude", Role = Role.User }],
          Fallbacks = new([new(Messages::Model.ClaudeOpus4_8)]),
          Betas = [AnthropicBeta.ServerSideFallback2026_07_01],
      }
  );

  Console.WriteLine(response.Model.Raw());
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeFable5,
  	MaxTokens: 1024,
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
  	},
  	Fallbacks: anthropic.BetaFallbacksParamUnion{
  		OfBetaFallbackArray: []anthropic.BetaFallbackParam{{Model: anthropic.ModelClaudeOpus4_8}},
  	},
  	Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaServerSideFallback2026_07_01},
  })
  if err != nil {
  	panic(err)
  }

  fmt.Println(response.Model)
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  BetaMessage response = client.beta().messages().create(MessageCreateParams.builder()
      .model(Model.CLAUDE_FABLE_5)
      .maxTokens(1024L)
      .addUserMessage("Hello, Claude")
      .fallbacksOfFallbackParams(List.of(BetaFallbackParam.builder()
          .model(Model.CLAUDE_OPUS_4_8)
          .build()))
      .addBeta(AnthropicBeta.SERVER_SIDE_FALLBACK_2026_07_01)
      .build());

  IO.println(response.model().asString());
  ```

  ```php PHP
  $client = new Client();

  $response = $client->beta->messages->create(
      model: 'claude-fable-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      fallbacks: [['model' => 'claude-opus-4-8']],
      betas: ['server-side-fallback-2026-07-01'],
  );

  echo $response->model, PHP_EOL;
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    fallbacks: [{model: "claude-opus-4-8"}],
    betas: ["server-side-fallback-2026-07-01"]
  )

  puts response.model
  ```
</CodeGroup>

Algunas reglas aplican a la lista `fallbacks`:

* Las entradas se prueban en orden. Cada una debe ser distinta de las demás entradas y del modelo solicitado.
* Cada entrada debe ser uno de los destinos permitidos del modelo solicitado. Con el encabezado beta establecido, esa lista se publica como `allowed_fallback_models` en la entrada del modelo en la [Models API](https://platform.claude.com/docs/es/api/models/list).
* Cada entrada indica un `model` y puede sobrescribir `max_tokens`, `thinking`, `output_config` y `speed` solo para ese intento.
* La solicitud debe ser válida como solicitud directa a cada modelo nombrado. Si un modelo de respaldo no admite una función que usa la solicitud, la API rechaza la solicitud de antemano. Con el encabezado `2026-07-01`, `thinking: {"type": "between_tools"}` es la excepción: cuando una solicitud a Claude Sonnet 5.5 pasa a Claude Sonnet 5, el intento de respaldo se ejecuta con `thinking: {"type": "disabled"}` y `display: "omitted"`. Con `2026-06-01`, establece `thinking` en la entrada de Claude Sonnet 5, o la solicitud se rechaza de antemano.
* Al igual que en el modo predeterminado, solo un rechazo del clasificador de seguridad activa el respaldo. Un límite de velocidad, una sobrecarga o un error del servidor en el modelo solicitado se te devuelve tal cual.
* Si un modelo de respaldo alcanza su límite de velocidad o está sobrecargado, el intento de respaldo no se realiza y en su lugar se devuelve el rechazo anterior. El `stop_details.recommended_model` del rechazo indica entonces un modelo al que reintentar directamente. Dimensiona los límites de velocidad del modelo de respaldo para el volumen de rechazos que esperas, o los respaldos se degradarán a rechazos bajo carga.

La respuesta tiene la misma forma en ambos modos: el modelo que atendió el turno aparece en el campo `model` de nivel superior, un bloque de contenido `fallback` marca el traspaso, y `usage.iterations` registra cada intento.

### Qué contiene la respuesta

La respuesta se ve como cualquier otro mensaje, con dos adiciones:

* El campo `model` de nivel superior reporta el modelo que produjo el mensaje devuelto, ya sea el modelo solicitado o un fallback.

* Un bloque de contenido `fallback` marca cada punto en `content` donde la salida de un modelo da paso al siguiente: `{"type": "fallback", "from": {"model": ...}, "to": {"model": ...}}`.

  * `from.model` repite la cadena de modelo que enviaste cuando el salto que declina es el modelo solicitado.
  * `to.model` es siempre el ID resuelto del modelo que continúa.

En un rechazo antes de cualquier salida, el bloque `fallback` es el primer bloque de contenido. Por ejemplo, cuando el enrutamiento predeterminado selecciona Claude Opus 4.8 para la categoría del rechazo:

```json
{
  "id": "msg_01XFUDYJgAACzvnptvVoYEL",
  "type": "message",
  "role": "assistant",
  "model": "claude-opus-4-8",
  "content": [
    {
      "type": "fallback",
      "from": { "model": "claude-fable-5" },
      "to": { "model": "claude-opus-4-8" }
    },
    { "type": "text", "text": "Hi! How can I help you today?" }
  ],
  "stop_reason": "end_turn",
  "stop_details": null,
  "usage": {
    "input_tokens": 412,
    "output_tokens": 264,
    "cache_read_input_tokens": 0,
    "cache_creation_input_tokens": 0,
    "iterations": [
      {
        "type": "message",
        "model": "claude-fable-5",
        "input_tokens": 535,
        "output_tokens": 0,
        "cache_read_input_tokens": 0,
        "cache_creation_input_tokens": 0
      },
      {
        "type": "fallback_message",
        "model": "claude-opus-4-8",
        "input_tokens": 412,
        "output_tokens": 264,
        "cache_read_input_tokens": 0,
        "cache_creation_input_tokens": 0
      }
    ]
  }
}
```

El arreglo `usage.iterations` registra cada intento. Un modelo que declinó aparece como una entrada `message` ordinaria, y el modelo que atendió el turno aparece como una entrada `fallback_message`. Si todos los modelos de la cadena declinan, la respuesta es el rechazo del último modelo, con una entrada `message` por cada salto anterior y una entrada `fallback_message` para el último.

El [enrutamiento persistente](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#sticky-routing) puede enviar un turno posterior directamente al modelo de fallback. Dicho turno no lleva ningún bloque de contenido `fallback`, porque ningún modelo declinó ese turno. Identifícalo por la entrada `fallback_message` en `usage.iterations`, la ausencia de una entrada `message` para el modelo solicitado, y el campo `model` de la respuesta.

### Continuar la conversación

En el siguiente turno, envía de vuelta el contenido del asistente tal como lo recibiste. Después de un fallback a mitad de la salida, `content` puede incluir tipos de bloque que el modelo que declinó produjo antes del traspaso. La siguiente tabla cubre cuáles conservar y cuáles descartar cuando repites el turno.

| Tipo de bloque                                                                       | En el siguiente turno                                                                                                                                                                                                                                   |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fallback`                                                                           | Consérvalo exactamente donde apareció. La API usa su posición para validar los bloques de pensamiento a su alrededor, por lo que una solicitud que repite bloques de pensamiento de ambos lados del límite se rechaza si el bloque se omite o se mueve. |
| `text`                                                                               | Conservar.                                                                                                                                                                                                                                              |
| Cualquier bloque después del bloque `fallback` final                                 | Conservar.                                                                                                                                                                                                                                              |
| `thinking`, `redacted_thinking` o `connector_text` antes del bloque `fallback` final | Descartar.                                                                                                                                                                                                                                              |
| `tool_use` del lado del cliente antes del bloque `fallback` final                    | Descartar.                                                                                                                                                                                                                                              |
| `server_tool_use` antes del bloque `fallback` final                                  | Conservar cuando está emparejado con su resultado. Descartar cuando no tiene un resultado correspondiente.                                                                                                                                              |

<Note>
  Un bloque `connector_text` lleva texto de narración que algunas respuestas con uso de herramientas incluyen entre llamadas a herramientas.
</Note>

### Streaming

En una solicitud con streaming, el reintento ocurre en el mismo stream, y nada de lo que ya recibiste se invalida. Lo que ves depende de cuándo ocurre la declinación.

**Cuando la declinación ocurre antes de cualquier salida:**

* `message_start` nombra el modelo de fallback, y el bloque `fallback` es el primer bloque de contenido.
* Debido a que `message_start` espera a que comience el intento de fallback, el tiempo hasta el primer byte incluye el intento declinado.

**Cuando la declinación ocurre a mitad de la salida:**

* El bloque de contenido abierto se cierra, y el bloque `fallback` (un par ordinario de `content_block_start` y `content_block_stop` sin deltas) marca el límite.
* El modelo de fallback continúa desde la salida parcial. Solo los bloques `text` de la salida parcial se pasan al modelo de fallback como contexto. Los demás tipos de bloque permanecen en `content`.
* `message_start` ya nombró el modelo solicitado, así que lee el modelo que atiende desde el `to.model` del bloque `fallback` y desde la entrada `fallback_message` en el `usage.iterations` del `message_delta` final.

### Respuestas sin streaming

En una solicitud sin streaming, una declinación a mitad de la salida se comporta de manera diferente: la respuesta omite la salida parcial del modelo declinado, y el modelo de fallback responde desde cero. El resultado se ve como una declinación antes de cualquier salida, con el bloque `fallback` primero. El intento declinado y sus tokens de salida aún aparecen en `usage.iterations`.

<Note>
  **Declinaciones durante el uso de herramientas:** el trabajo de herramientas completado no bloquea el fallback. Cuando una declinación se dispara después de que las herramientas del servidor (por ejemplo, búsqueda web o ejecución de código) han terminado de ejecutarse dentro de una solicitud, el intento de fallback procede: los resultados de herramientas completados se transfieren, y el modelo de fallback puede seguir invocando herramientas del servidor. El único caso que no se reintenta es una declinación con streaming que se dispara mientras un bloque de uso de herramientas de cualquier tipo (una herramienta del cliente, una herramienta del servidor o una llamada a herramienta MCP) aún está abierto en el stream: ese rechazo se devuelve directamente, y si el encabezado `fallback-credit-2026-07-01` está establecido, aún lleva un token de crédito canjeable al continuar la respuesta parcial. Las solicitudes sin streaming no se ven afectadas; la API limpia el trabajo parcial y reintenta antes de responder.
</Note>

### Facturación y límites de velocidad

Cada intento sigue las reglas de [Cómo se facturan los rechazos](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#how-refusals-are-billed), a las tarifas del modelo que lo ejecutó. Un intento que declinó antes de producir cualquier salida se factura solo cuando su categoría de rechazo es facturable, y sus tokens se reportan en su entrada de `usage.iterations` en cualquier caso. Cada intento que produjo salida, incluido uno que declinó a mitad de su respuesta, se factura por separado. El arreglo `usage.iterations` es el registro por intento de lo que se te factura. Los conteos de `usage` de nivel superior describen solo el intento que produjo el mensaje devuelto. Los tokens de diferentes modelos nunca se suman en un solo campo.

Cada intento que se ejecuta, incluido uno que declinó, cuenta contra los límites de velocidad de su propio modelo.

### Enrutamiento persistente

Después de que una conversación recurre al fallback, la API registra qué modelo la atendió. Las solicitudes posteriores de esa conversación que incluyen `fallbacks` van directamente a ese modelo de fallback, sin ejecutar el modelo solicitado. Este "sticky routing" (enrutamiento persistente) evita pagar por un intento que previsiblemente sería declinado de nuevo en cada turno.

Algunas propiedades de la decisión de enrutamiento:

* Se conserva durante aproximadamente 1 hora y está limitada a tu organización.
* Se almacena como un hash de contenido del prefijo de la conversación más el modelo que la atendió. El contenido del mensaje en sí no se almacena.
* Es de mejor esfuerzo, por lo que tu código debe manejar que el modelo solicitado se vuelva a probar en cualquier momento.

El enrutamiento persistente aplica tanto a solicitudes con streaming como sin streaming. En una solicitud con streaming, la decisión de enrutamiento se toma antes de que se abra el stream, por lo que el campo `model` del evento `message_start` ya lleva el ID del modelo de fallback.

## Fallback del lado del cliente con el middleware del SDK

El SDK incluye un middleware de fallback ante rechazos. Lo configuras una vez en el cliente con tu lista de modelos de fallback. Las llamadas a través de `client.beta.messages` (csharp, go: `client.Beta.Messages`; java: `client.beta().messages()`; php: `$client->beta->messages`) reintentan entonces las solicitudes rechazadas automáticamente, en cualquier plataforma. El middleware también envía el encabezado beta `fallback-credit-2026-07-01` en cada solicitud que maneja, de modo que los reintentos se vuelven a tarificar sin configuración por solicitud. Un rechazo de Claude Haiku 5.5 no incluye crédito de fallback, así que un reintento posterior paga el costo completo de escribir la caché de prompts del modelo de fallback.

### Configurarlo

Pasa `BetaRefusalFallbackMiddleware` (typescript: `betaRefusalFallbackMiddleware`; go: `betafallback.BetaRefusalFallbackMiddleware`; csharp: `BetaRefusalFallbackHandler`; java: `BetaRefusalFallbackInterceptor`; php: `RefusalFallbackMiddleware`) al constructor del cliente, y comparte una instancia de `BetaFallbackState` entre las solicitudes de una conversación.

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    El middleware de fallback ante rechazos forma parte del SDK de Claude, así que una solicitud cURL no puede usarlo. Para recurrir al fallback dentro de una sola solicitud, usa el [fallback del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback). Para escribir el reintento tú mismo, consulta [Crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit).
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    La CLI `ant` no incluye el middleware de fallback ante rechazos. Para recurrir al fallback dentro de una sola solicitud, usa el [fallback del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback). Para escribir el reintento tú mismo, consulta [Crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit).
  </CodeGroupItem>

  ```python Python
  from anthropic import Anthropic, BetaFallbackState, BetaRefusalFallbackMiddleware

  # Ante un rechazo, el middleware reintenta con el modelo de respaldo indicado y
  # envía automáticamente el encabezado beta fallback-credit en cada solicitud que maneja.
  client = Anthropic(
      middleware=[BetaRefusalFallbackMiddleware([{"model": "claude-opus-4-8"}])],
  )

  state = BetaFallbackState()  # pins follow-ups to the model that accepted

  # Streaming: ante un rechazo, el middleware reintenta con el modelo de respaldo y
  # empalma sus eventos en el stream abierto.
  with (
      state,
      client.beta.messages.stream(
          max_tokens=1024,
          model="claude-fable-5",
          messages=[{"role": "user", "content": "Hello, Claude"}],
      ) as stream,
  ):
      for text in stream.text_stream:
          print(text, end="", flush=True)
      final_message = stream.get_final_message()
  print(f"\nserved by: {final_message.model}")

  # Sin streaming: reutilizar el estado mantiene la conversación fijada.
  with state:
      message = client.beta.messages.create(
          max_tokens=1024,
          model="claude-fable-5",
          messages=[{"role": "user", "content": "Hello, Claude"}],
      )
  print(f"served by: {message.model}")
  ```

  ```typescript TypeScript
  import { BetaFallbackState, betaRefusalFallbackMiddleware } from "@anthropic-ai/sdk";

  // Ante un rechazo, el middleware reintenta con el modelo de respaldo indicado y
  // envía automáticamente el encabezado beta fallback-credit en cada solicitud que maneja.
  const client = new Anthropic({
    middleware: [betaRefusalFallbackMiddleware([{ model: "claude-opus-4-8" }])]
  });

  // Comparte un único estado en toda la conversación para que las solicitudes posteriores
  // queden fijadas al modelo que aceptó.
  const fallbackState = new BetaFallbackState();

  // Streaming: ante un rechazo, el middleware reintenta con el modelo de respaldo y
  // empalma sus eventos en el stream abierto.
  const stream = client.beta.messages
    .stream(
      {
        max_tokens: 1024,
        model: "claude-fable-5",
        messages: [{ role: "user", content: "Hello, Claude" }]
      },
      { fallbackState }
    )
    .on("text", (text) => process.stdout.write(text));

  const finalMessage = await stream.finalMessage();
  console.log("\nserved by:", finalMessage.model);

  // Sin streaming: reutilizar el estado mantiene la conversación fijada.
  const message = await client.beta.messages.create(
    {
      max_tokens: 1024,
      model: "claude-fable-5",
      messages: [{ role: "user", content: "Hello, Claude" }]
    },
    { fallbackState }
  );
  console.log("served by:", message.model);
  ```

  ```csharp C#
  using Anthropic;
  using Anthropic.Helpers;
  using Anthropic.Models.Beta.Messages;
  using Messages = Anthropic.Models.Messages;

  // Ante un rechazo, el handler reintenta con el modelo de respaldo indicado y
  // envía automáticamente el encabezado beta fallback-credit en cada solicitud que maneja.
  AnthropicClient client = new()
  {
      Handlers =
      [
          new BetaRefusalFallbackHandler { Fallbacks = [new(Messages::Model.ClaudeOpus4_8)] },
      ],
  };

  // Fija las solicitudes de seguimiento que comparten este estado al modelo que aceptó.
  BetaFallbackState fallbackState = BetaFallbackState.Create();

  MessageCreateParams parameters = new()
  {
      Model = Messages::Model.ClaudeFable5,
      MaxTokens = 1024,
      Messages = [new() { Content = "Hello, Claude", Role = Role.User }],
  };

  // Streaming: si el stream termina en un rechazo, el handler empalma los eventos del modelo
  // de respaldo en el stream aún abierto.
  BetaMessageContentAggregator aggregator = new();
  using (fallbackState.Use())
  {
      var responseUpdates = client.Beta.Messages.CreateStreaming(parameters);
      await foreach (BetaRawMessageStreamEvent rawEvent in responseUpdates.CollectAsync(aggregator))
      {
          if (
              rawEvent.TryPickContentBlockDelta(out var deltaEvent)
              && deltaEvent.Delta.TryPickText(out var textDelta)
          )
          {
              Console.Write(textDelta.Text);
          }
      }
  }
  BetaMessage streamedMessage = aggregator.Message();
  Console.WriteLine($"\nserved by: {streamedMessage.Model.Raw()}");

  // Sin streaming: reutilizar el estado mantiene la conversación fijada al modelo que aceptó.
  using (fallbackState.Use())
  {
      BetaMessage message = await client.Beta.Messages.Create(parameters);
      Console.WriteLine($"served by: {message.Model.Raw()}");
  }
  ```

  ```go Go
  import (
  // ...
  	"github.com/anthropics/anthropic-sdk-go/lib/betafallback"
  // ...
  )

  func main() {
  	ctx := context.Background()

  	// El middleware reintenta una solicitud rechazada en cada modelo de respaldo
  	// por turno, e inscribe las solicitudes en la beta fallback-credit automáticamente.
  	client := anthropic.NewClient(
  		option.WithMiddleware(betafallback.BetaRefusalFallbackMiddleware(
  			[]anthropic.BetaFallbackParam{{Model: anthropic.ModelClaudeOpus4_8}},
  		)),
  	)

  	// Un estado por conversación: las solicitudes que lo comparten quedan fijadas al
  	// modelo que aceptó, así un seguimiento nunca vuelve a pedir a un modelo que rechazó.
  	state := &betafallback.BetaFallbackState{}
  	conversation := betafallback.WithBetaFallbackState(state)

  	params := anthropic.BetaMessageNewParams{
  		MaxTokens: 1024,
  		Model:     anthropic.ModelClaudeFable5,
  		Messages: []anthropic.BetaMessageParam{
  			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
  		},
  	}

  	// Streaming: ante un rechazo el middleware reintenta en el lugar, empalmando los
  	// eventos del modelo de respaldo en el stream abierto como un mensaje continuo.
  	stream := client.Beta.Messages.NewStreaming(ctx, params, conversation)
  	defer stream.Close()
  	var streamed anthropic.BetaMessage
  	for stream.Next() {
  		event := stream.Current()
  		if err := streamed.Accumulate(event); err != nil {
  			panic(err)
  		}
  		switch eventVariant := event.AsAny().(type) {
  		case anthropic.BetaRawContentBlockDeltaEvent:
  			if textDelta, ok := eventVariant.Delta.AsAny().(anthropic.BetaTextDelta); ok {
  				fmt.Print(textDelta.Text)
  			}
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  	fmt.Println("\nserved by:", streamed.Model)

  	// Sin streaming: el estado compartido fija este seguimiento al modelo que
  	// atendió el turno transmitido por streaming.
  	message, err := client.Beta.Messages.New(ctx, params, conversation)
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println("served by:", message.Model)
  }
  ```

  ```java Java
  import com.anthropic.client.AnthropicClient;
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.core.RequestOptions;
  import com.anthropic.core.http.StreamResponse;
  import com.anthropic.helpers.BetaFallbackState;
  import com.anthropic.helpers.BetaMessageAccumulator;
  import com.anthropic.helpers.BetaRefusalFallbackInterceptor;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaRawMessageStreamEvent;
  import com.anthropic.models.beta.messages.MessageCreateParams;
  import com.anthropic.models.messages.Model;

  void main() {
      // El interceptor reintenta las solicitudes rechazadas en el modelo de respaldo. Automáticamente
      // agrega el encabezado beta fallback-credit a cada solicitud que maneja.
      AnthropicClient client = AnthropicOkHttpClient.builder()
          .fromEnv()
          .addInterceptor(BetaRefusalFallbackInterceptor.builder()
              .addFallback(Model.CLAUDE_OPUS_4_8)
              .build())
          .build();

      // Comparte un solo estado entre solicitudes para que los seguimientos queden fijados al modelo que aceptó.
      BetaFallbackState state = BetaFallbackState.create();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_FABLE_5)
          .maxTokens(1024)
          .addUserMessage("Hello, Claude")
          .build();

      // Streaming: ante un rechazo, los eventos del modelo de respaldo se empalman en el stream abierto.
      BetaMessageAccumulator accumulator = BetaMessageAccumulator.create();
      try (StreamResponse<BetaRawMessageStreamEvent> streamResponse = client.beta()
              .messages()
              .createStreaming(params, RequestOptions.builder().fallbackState(state).build())) {
          streamResponse.stream()
              .peek(accumulator::accumulate)
              .forEach(event -> event.contentBlockDelta()
                  .flatMap(deltaEvent -> deltaEvent.delta().text())
                  .ifPresent(textDelta -> IO.print(textDelta.text())));
      }
      IO.println("\nserved by: " + accumulator.message().model().asString());

      // Sin streaming: reutilizar el mismo estado mantiene la conversación fijada.
      BetaMessage message = client.beta()
          .messages()
          .create(params, RequestOptions.builder().fallbackState(state).build());
      IO.println("served by: " + message.model().asString());
  }
  ```

  ```php PHP
  use Anthropic\Beta\Messages\BetaRawContentBlockDeltaEvent;
  use Anthropic\Beta\Messages\BetaTextDelta;
  use Anthropic\Client;
  use Anthropic\Lib\Middleware\BetaFallbackState;
  use Anthropic\Lib\Middleware\RefusalFallbackMiddleware;
  use Anthropic\Lib\Streaming\MessageAccumulator;

  // Configura la cadena de fallback una vez. Ante un rechazo, el middleware reintenta la
  // solicitud a lo largo de la cadena y envía el encabezado beta fallback-credit por ti.
  $client = new Client(
      requestOptions: [
          'middleware' => [new RefusalFallbackMiddleware([['model' => 'claude-opus-4-8']])],
      ],
  );

  // Comparte un solo estado en toda la conversación para que las solicitudes siguientes queden fijadas
  // al modelo que aceptó.
  $state = new BetaFallbackState();

  // Streaming: ante un rechazo, el middleware empalma los eventos del modelo de fallback
  // en el stream aún abierto. El modelo del acumulador es el modelo que atiende.
  $stream = $client->beta->messages->createStream(
      model: 'claude-fable-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      requestOptions: ['fallbackState' => $state],
  );
  $accumulator = MessageAccumulator::forBetaMessages();
  foreach ($stream as $event) {
      $accumulator->accumulate($event);
      if ($event instanceof BetaRawContentBlockDeltaEvent
          && $event->delta instanceof BetaTextDelta) {
          echo $event->delta->text;
      }
  }
  echo "\nserved by: {$accumulator->message()->model}\n";

  // Sin streaming: el mismo middleware. Reutilizar el estado mantiene la conversación
  // fijada al modelo que aceptó.
  $message = $client->beta->messages->create(
      model: 'claude-fable-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      requestOptions: ['fallbackState' => $state],
  );
  echo "served by: {$message->model}\n";
  ```

  ```ruby Ruby
  # Ante un rechazo, el middleware reintenta la solicitud siguiendo la cadena de respaldo.
  # Envía el encabezado beta fallback-credit en cada solicitud que maneja.
  client = Anthropic::Client.new(
    middleware: [Anthropic::BetaRefusalFallbackMiddleware.new([{model: "claude-opus-4-8"}])]
  )

  # Comparte un solo estado en toda la conversación para que las solicitudes de seguimiento
  # queden fijadas al modelo que aceptó.
  state = Anthropic::BetaFallbackState.new

  # Streaming: ante un rechazo, el middleware empalma los eventos del modelo de respaldo
  # en el stream aún abierto.
  stream = client.beta.messages.stream(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    request_options: {fallback_state: state}
  )
  stream.text.each { print it }
  puts "\nserved by: #{stream.accumulated_message.model}"

  # Sin streaming: reutilizar el estado mantiene la conversación fijada al modelo que aceptó.
  message = client.beta.messages.create(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    request_options: {fallback_state: state}
  )
  puts "served by: #{message.model}"
  ```
</CodeGroup>

### Cómo se comporta

* Los reintentos recorren tu lista de respaldo en orden. Un modelo de respaldo que a su vez rechaza pasa la solicitud a la siguiente entrada.
* Cuando todos los modelos de la lista han rechazado, el middleware devuelve el rechazo final (la respuesta de rechazo del último modelo) en lugar de generar un error.
* Los bloques de pensamiento de Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, Claude Haiku 5.5 o Claude Fable 5 pasan sin cambios. Cada reintento reenvía el cuerpo de tu solicitud original, y los únicos bloques que el middleware elimina del historial de la conversación en solicitudes posteriores son los bloques de límite `fallback` que él mismo agregó. El modelo de fallback no puede leer los bloques de Claude Fable 5.1, que solo [leen Claude Fable 5.1 y Claude Mythos 5.1](https://platform.claude.com/docs/es/build-with-claude/thinking#preserved-for-model), así que la API los descarta. La API también descarta los bloques de Claude Opus 5.5 para todos los modelos de fallback excepto Claude Fable 5.1 y Claude Mythos 5.1 (consulta [Cambiar de modelo a mitad de la conversación](https://platform.claude.com/docs/es/build-with-claude/preserved-thinking#switching-models)). También descarta los bloques de Claude Sonnet 5.5, para todos los modelos de fallback excepto Claude Opus 5.5 en la Claude API y Google Cloud. Claude Opus 5.5 y Claude Sonnet 5.5 leen los bloques de Claude Haiku 5.5 en la Claude API y Google Cloud, y la API los descarta para un modelo de fallback que no puede leerlos.
* Las respuestas atendidas a través del middleware incluyen un bloque de contenido `fallback` en cada límite entre modelos, igual que las respuestas del respaldo del lado del servidor. El middleware gestiona esos bloques por ti en solicitudes posteriores.
* El modelo que aceptó se registra en `BetaFallbackState`, de modo que las solicitudes de seguimiento que comparten el estado permanecen fijadas a él en lugar de volver a preguntar a un modelo que rechazó.

<Note>
  El middleware y el parámetro `fallbacks` del lado del servidor hacen el mismo trabajo. Configura uno u otro, nunca ambos en la misma solicitud. Para enviar una solicitud con `fallbacks` del lado del servidor desde una aplicación que instala el middleware, usa una instancia de cliente separada sin él.
</Note>

## Escribir el reintento tú mismo

Sobre HTTP directo o con lógica de reintento personalizada, implementa el patrón que el middleware envuelve:

<Steps>
  <Step title="Detecta el rechazo">
    Revisa la respuesta en busca de `stop_reason: "refusal"`.
  </Step>

  <Step title="Reenvía en un modelo de respaldo">
    Envía la misma solicitud con `model` establecido en un modelo de respaldo, como Claude Opus 4.8. Si la solicitud rechazada envió `thinking: {"type": "between_tools"}`, cambia `thinking` primero: solo Claude Sonnet 5.5 acepta ese valor, así que omite `thinking` o establece un valor que el modelo de respaldo acepte. El [fallback del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback) con el encabezado `2026-07-01` hace este cambio por ti cuando pasa a Claude Sonnet 5. Otro modelo normalmente puede atender una solicitud que Claude Fable 5.1 o Claude Fable 5 rechaza. Cómo manejes el historial de la conversación depende de si canjeas un [crédito de respaldo](https://platform.claude.com/docs/es/build-with-claude/fallback-credit):

    * **Sin canjear un crédito:** puedes dejar los bloques `thinking` y `redacted_thinking` anteriores en su lugar o eliminarlos para ahorrar tokens de entrada. El modelo de fallback normalmente no puede usarlos en ningún caso: solo Claude Fable 5.1 y Claude Mythos 5.1 [leen los bloques de Claude Fable 5.1](https://platform.claude.com/docs/es/build-with-claude/thinking#preserved-for-model), y solo esos dos modelos, además de Claude Fable 5 y Claude Mythos 5, leen los bloques de Claude Fable 5, así que para cualquier otro modelo de fallback la API los descarta. La API también descarta los bloques de Claude Opus 5.5 para todos los modelos de fallback excepto Claude Fable 5.1 y Claude Mythos 5.1 (consulta [Cambiar de modelo a mitad de la conversación](https://platform.claude.com/docs/es/build-with-claude/preserved-thinking#switching-models)). También descarta los bloques de Claude Sonnet 5.5, para todos los modelos de fallback excepto Claude Opus 5.5 en la Claude API y Google Cloud. Un reintento después de un rechazo de Claude Haiku 5.5 siempre corresponde a este caso. Claude Opus 5.5 y Claude Sonnet 5.5 leen los bloques de Claude Haiku 5.5 en la Claude API y Google Cloud, así que deja esos bloques en su lugar cuando pases a cualquiera de esos dos modelos en esas plataformas. Para un modelo de fallback que no puede leerlos, la API los descarta sin facturarlos.
    * **Canjeando un crédito:** envía el cuerpo sin cambios, porque el canje requiere una coincidencia exacta. El servidor maneja los bloques de pensamiento del modelo anterior en un canje, así que no los elimines (consulta [Campos que deben coincidir con la solicitud rechazada](https://platform.claude.com/docs/es/build-with-claude/fallback-credit#reference)).
  </Step>

  <Step title="Permanece en el modelo de respaldo">
    En conversaciones de varios turnos, sigue usando el modelo de respaldo en los turnos siguientes en lugar de volver al anterior.
  </Step>
</Steps>

Un reintento manual escribe la caché de prompts del modelo de fallback desde cero, lo cual cuesta más que leer una caché existente. El [crédito de fallback](https://platform.claude.com/docs/es/build-with-claude/fallback-credit) reembolsa ese costo; canjéalo en cada reintento que construyas tú mismo. Un rechazo de Claude Haiku 5.5 no incluye crédito de fallback, así que un reintento posterior paga el costo completo de escribir la caché de prompts del modelo de fallback.

## Rechazos en Message Batches

Una solicitud rechazada en un [Message Batch](https://platform.claude.com/docs/es/build-with-claude/batch-processing) regresa como `result.type: "succeeded"` con `stop_reason: "refusal"`. Los resultados de lote llevan el mismo objeto `stop_details` que las respuestas síncronas, por lo que puedes detectar rechazos a través de `stop_reason` o de `stop_details.type`. Una diferencia: los rechazos en lote no emiten créditos de fallback, por lo que `stop_details` en un resultado de lote nunca incluye un `fallback_credit_token`.

El fallback del lado del servidor no está disponible para lotes (una solicitud de lote que incluye `fallbacks` produce un resultado con error por elemento). Para reintentar los elementos de lote rechazados:

1. Recopila los elementos rechazados de los resultados.
2. Deja en su lugar los bloques de pensamiento de los historiales de varios turnos, o elimínalos para ahorrar tokens de entrada. Para un modelo de fallback que no puede leerlos, la API los descarta sin facturarlos, como en [un reintento manual](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#manual-retry).
3. Si un elemento rechazado envió `thinking: {"type": "between_tools"}`, omite `thinking` o establece un valor que el modelo de fallback acepte. Solo Claude Sonnet 5.5 acepta `between_tools`.
4. Reenvíalos en un modelo de fallback como un nuevo lote o como solicitudes directas.

## Errores comunes

* **Reintenta en un modelo diferente.** Reenviar una solicitud rechazada al mismo modelo generalmente obtiene otro rechazo. Dirige el reintento al modelo de fallback.
* **Presupuesta los reintentos por solicitud, no por turno ni por sesión.** Un solo turno puede producir varios rechazos, por ejemplo un agente más sus subagentes.
* **Configura el fallback en cada ruta de solicitud.** Los manejadores de reintento, las ramas de recuperación de errores y los workers en segundo plano lo necesitan. Un manejador que vuelve a emitir una solicitud sin fallback pierde la protección exactamente en las solicitudes que más probablemente la necesiten.
* **Da a las llamadas de subagentes su propio fallback.** El parámetro `fallbacks` no se propaga a las llamadas a modelos realizadas desde dentro de la ejecución de herramientas.
* **Haz del fallback una propiedad de la solicitud, no del estado ambiental.** Una bandera compartida, un valor de configuración en caché o un interruptor global pueden desincronizarse y dejar silenciosamente una solicitud sin protección. Cuando no puedas confirmar que el fallback está activo, configúralo en lugar de asumir que está activado.
* **Instrumenta los rechazos como su propia señal.** Un rechazo es un HTTP 200, por lo que el monitoreo basado en tasas de error o respuestas 5xx nunca lo ve. Emite un evento por rechazo y uno por respuesta atendida mediante fallback (la entrada `fallback_message` en `usage.iterations` marca esta última), y luego alerta sobre la diferencia entre los dos conteos.
* **Ramifica según `stop_reason` o `stop_details.type`, no según `content` ni los campos internos de `stop_details`.** El objeto `stop_details` siempre está presente en un rechazo, pero sus campos `category` y `explanation` pueden ser `null`. Verifica directamente que `stop_reason` sea igual a `"refusal"`.

## Próximos pasos

<CardGroup>
  <Card title="Crédito de fallback" icon="scales" href="https://platform.claude.com/docs/es/build-with-claude/fallback-credit">
    Evita pagar dos veces el costo de la caché de prompts cuando construyes el reintento tú mismo.
  </Card>

  <Card title="Motivos de parada y fallback" icon="code" href="https://platform.claude.com/docs/es/build-with-claude/handling-stop-reasons">
    Cada valor de `stop_reason` y cómo manejarlo.
  </Card>

  <Card title="Middleware del SDK" icon="settings" href="https://platform.claude.com/docs/es/cli-sdks-libraries/middleware">
    Cómo funciona el middleware del SDK, incluido el asistente de fallback ante rechazos.
  </Card>

  <Card title="Guía de migración" icon="arrow-right" href="https://platform.claude.com/docs/es/models/fable-5-1/migration-guide">
    Migra una aplicación existente a Claude Fable 5.1.
  </Card>
</CardGroup>
