---
title: Crédito de respaldo
url: https://platform.claude.com/docs/es/build-with-claude/fallback-credit
description: Evita pagar dos veces el costo de la caché de prompts cuando reintentas en otro modelo una solicitud rechazada.
---

Las cachés de prompts son por modelo. Cuando un modelo rechaza una solicitud y reintentas en otro modelo, el prefijo de la conversación ya almacenado en caché para el primer modelo debe escribirse desde cero en la caché del nuevo modelo. Las escrituras en caché cuestan más que las lecturas de caché. El "fallback credit" (crédito de respaldo) elimina ese costo adicional. El rechazo incluye un token de crédito, tú devuelves el token en el reintento, y el reintento se factura como si la conversación hubiera estado en el nuevo modelo desde el principio.

Necesitas esta página solo cuando construyes el reintento tú mismo: sobre HTTP sin procesar o con lógica de reintento personalizada. El [respaldo del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback) y el [middleware del SDK](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#client-side-fallback) aplican el crédito de respaldo automáticamente. Si usas cualquiera de los dos, omite esta página.

[Rechazos y respaldo](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback) cubre cómo detectar rechazos y elegir un enfoque de respaldo. [Almacenamiento en caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching) explica las lecturas de caché y las escrituras en caché si esos términos son nuevos para ti.

## El flujo básico

<Steps>
  <Step title="Activa la función con el encabezado beta">
    Envía la solicitud que podría ser rechazada con el encabezado `anthropic-beta: fallback-credit-2026-07-01`. El encabezado `server-side-fallback-2026-07-01` también otorga los mismos campos, excepto en Amazon Bedrock y Google Cloud, que devuelven un error 400 para ese encabezado. El encabezado anterior `fallback-credit-2026-06-01` sigue siendo aceptado y otorga los mismos campos.
  </Step>

  <Step title="Lee dos campos del rechazo">
    En un rechazo, `stop_details` incluye dos campos:

    * **`fallback_credit_token`:** una cadena opaca que representa el crédito.
    * **`fallback_has_prefill_claim`:** un booleano que te indica qué forma de cuerpo de reintento usar.

    Ambos son `null` cuando no hay crédito disponible para el rechazo.
  </Step>

  <Step title="Construye el reintento">
    Parte del cuerpo de la solicitud rechazada. Establece `model` en el modelo de respaldo y agrega el token como el parámetro de nivel superior `fallback_credit_token`. Elige la forma del cuerpo según la siguiente tabla.
  </Step>

  <Step title="Envía el reintento con el mismo encabezado">
    Envía el reintento con el mismo encabezado beta `fallback-credit-2026-07-01`. El reintento necesita el encabezado para canjear el token.
  </Step>
</Steps>

El campo `fallback_has_prefill_claim` te indica si el reintento puede continuar la salida parcial del modelo que rechazó en lugar de empezar de nuevo:

| `fallback_has_prefill_claim` | Cuerpo del reintento                                                                                                                                                                                                                                                                                                                    |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `true`                       | El cuerpo de la solicitud rechazada, sin cambios, más un mensaje de asistente añadido al final cuyo `content` replica el `content` de la respuesta rechazada. El modelo de reintento continúa la respuesta desde donde se detuvo el modelo que rechazó, y las llamadas a herramientas de servidor completadas no se vuelven a ejecutar. |
| `false`                      | El cuerpo de la solicitud rechazada, sin cambios.                                                                                                                                                                                                                                                                                       |

## Ejemplo

El siguiente ejemplo realiza una solicitud que podría ser rechazada y canjea el token de crédito en un reintento contra Claude Opus 4.8. Cuando un intento de reintento es rechazado, el ejemplo desciende por la escalera de rechazos: la secuencia de formas de reintento progresivamente más simples que se cubre en [Cuando un reintento es rechazado](https://platform.claude.com/docs/es/build-with-claude/fallback-credit#when-a-retry-is-rejected).

<CodeGroup>
  ```bash cURL
  # Solicitud inicial (puede ser rechazada)
  response=$(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: fallback-credit-2026-07-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-fable-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello, Claude"}]
    }')

  # Un rechazo incluye un token de crédito de un solo uso en stop_details
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # Reintenta en el modelo de respaldo con el token de crédito (mismo cuerpo)
    response=$(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: fallback-credit-2026-07-01" \
      -H "content-type: application/json" \
      -d "$(jq -n --arg token "${token}" '{
        model: "claude-opus-4-8",
        max_tokens: 1024,
        messages: [{"role": "user", "content": "Hello, Claude"}],
        fallback_credit_token: $token
      }')")
  fi

  # Consulta los ejemplos del SDK para ver la escalera completa de manejo de rechazos.
  jq -c '{stop_reason, model}' <<<"${response}"
  ```

  ```bash CLI
  # Solicitud inicial (puede ser rechazada)
  response=$(ant beta:messages create \
    --model claude-fable-5 \
    --max-tokens 1024 \
    --message '{"role":"user","content":"Hello, Claude"}' \
    --beta fallback-credit-2026-07-01 \
    --format json)

  # Un rechazo incluye un token de crédito de un solo uso en stop_details
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # Reintenta en el modelo de respaldo con el token de crédito
    response=$(ant beta:messages create \
      --model claude-opus-4-8 \
      --max-tokens 1024 \
      --message '{"role":"user","content":"Hello, Claude"}' \
      --fallback-credit-token "${token}" \
      --beta fallback-credit-2026-07-01 \
      --format json)
  fi

  # Consulta los ejemplos del SDK para ver la escalera completa de manejo de rechazos.
  jq -c '{stop_reason, model}' <<<"${response}"
  ```

  ```python Python
  client = Anthropic()

  request = {
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello, Claude"}],
  }


  def send(model: str, body: dict[str, object]) -> BetaMessage:
      return client.beta.messages.create(
          model=model, betas=["fallback-credit-2026-07-01"], **body
      )


  response = send("claude-fable-5", request)

  if (
      response.stop_reason == "refusal"
      and (details := response.stop_details)
      and (token := details.fallback_credit_token)
  ):
      exact_body = request | {"fallback_credit_token": token}
      # Prefiere la forma de continuación a menos que la afirmación sea False
      if details.fallback_has_prefill_claim is not False:
          echoed = [block.model_dump() for block in response.content]
          match echoed:
              case [*_, {"type": "text"} as final_block]:
                  final_block["text"] = final_block["text"].rstrip()
          attempt = exact_body | {
              "messages": [
                  *request["messages"],
                  {"role": "assistant", "content": echoed},
              ]
          }
      else:
          attempt = exact_body

      try:
          response = send("claude-opus-4-8", attempt)
      except BadRequestError as error:
          if "redemption temporarily unavailable" in error.message:
              raise  # Transient: retry with the token within its five-minute window
          try:
              # Recurre al cuerpo sin cambios, aún con el token
              response = send("claude-opus-4-8", exact_body)
          except BadRequestError as retry_error:
              if "redemption temporarily unavailable" in retry_error.message:
                  raise  # Transient: retry with the token within its five-minute window
              # El token en sí fue rechazado: descártalo y reintenta sin él.
              response = send("claude-opus-4-8", request)

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

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

  const request: Anthropic.Beta.MessageCreateParamsNonStreaming = {
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Claude" }],
    betas: ["fallback-credit-2026-07-01"]
  };

  let response = await client.beta.messages.create(request);

  if (
    response.stop_reason === "refusal" &&
    response.stop_details?.type === "refusal" &&
    response.stop_details.fallback_credit_token
  ) {
    const { fallback_credit_token, fallback_has_prefill_claim } = response.stop_details;
    const fallbackModel = "claude-opus-4-8";

    const exactRetry: Anthropic.Beta.MessageCreateParamsNonStreaming = {
      ...request,
      model: fallbackModel,
      fallback_credit_token
    };

    // La forma más rica primero, degradando en cada rechazo: la forma de
    // continuación (salvo que la afirmación sea falsa), el cuerpo sin cambios que aún lleva
    // el token y, finalmente, renunciar al token.
    let attempt = exactRetry;
    if (fallback_has_prefill_claim !== false) {
      const finalBlock = response.content.at(-1);
      const echoed: Anthropic.Beta.BetaContentBlockParam[] =
        finalBlock?.type === "text"
          ? [
              ...response.content.slice(0, -1),
              { ...finalBlock, text: finalBlock.text.trimEnd() }
            ]
          : response.content;
      attempt = {
        ...exactRetry,
        messages: [...request.messages, { role: "assistant", content: echoed }]
      };
    }

    try {
      response = await client.beta.messages.create(attempt);
    } catch (error) {
      // Degrada solo ante un 400 relacionado con la forma. "redemption temporarily
      // unavailable" es transitorio: reintenta de la misma manera dentro de la
      // ventana de cinco minutos del token.
      if (
        !(error instanceof Anthropic.BadRequestError) ||
        error.message.includes("redemption temporarily unavailable")
      ) {
        throw error;
      }
      try {
        response = await client.beta.messages.create(exactRetry);
      } catch (retryError) {
        if (
          !(retryError instanceof Anthropic.BadRequestError) ||
          retryError.message.includes("redemption temporarily unavailable")
        ) {
          throw retryError;
        }
        response = await client.beta.messages.create({ ...request, model: fallbackModel });
      }
    }
  }

  const { stop_reason, model } = response;
  console.log(JSON.stringify({ stop_reason, model }));
  ```

  ```csharp C#
  var client = new AnthropicClient();
  const string beta = "fallback-credit-2026-07-01";

  List<BetaMessageParam> requestMessages =
  [
      new() { Role = Role.User, Content = "Hello, Claude" },
  ];
  MessageCreateParams Request(Model model) => new()
  {
      Model = model,
      MaxTokens = 1024,
      Messages = requestMessages,
      Betas = [beta],
  };
  var response = await client.Beta.Messages.Create(Request(Model.ClaudeFable5));

  if (
      response.StopReason == BetaStopReason.Refusal
      && response.StopDetails is { FallbackCreditToken: string token } details
  )
  {
      var exactBody = Request(Model.ClaudeOpus4_8) with { FallbackCreditToken = token };
      var attempt = exactBody;
      // Prefiere la forma de continuación a menos que la afirmación sea falsa
      if (details.FallbackHasPrefillClaim is not false)
      {
          var echoed = JsonArray.Create(response.RawData["content"])!;
          if (
              echoed is [.., JsonObject lastBlock]
              && lastBlock["type"]?.GetValue<string>() is "text"
              && lastBlock["text"]?.GetValue<string>() is string text
          )
          {
              lastBlock["text"] = text.TrimEnd();
          }
          attempt = exactBody with
          {
              Messages =
              [
                  .. requestMessages,
                  new()
                  {
                      Role = Role.Assistant,
                      Content = new BetaMessageParamContent(
                          JsonSerializer.SerializeToElement(echoed)
                      ),
                  },
              ],
          };
      }
      // Un rechazo transitorio de "canje temporalmente no disponible" se propaga fuera de
      // cada uno de los siguientes filtros catch: reintenta con el token dentro de su ventana de cinco minutos.
      try
      {
          response = await client.Beta.Messages.Create(attempt);
      }
      catch (AnthropicBadRequestException e)
          when (!e.Message.Contains("redemption temporarily unavailable"))
      {
          try
          {
              // Recurre al cuerpo sin cambios, aún con el token
              response = await client.Beta.Messages.Create(exactBody);
          }
          catch (AnthropicBadRequestException retryError)
              when (!retryError.Message.Contains("redemption temporarily unavailable"))
          {
              // El token en sí fue rechazado: descártalo y reintenta sin él.
              response = await client.Beta.Messages.Create(Request(Model.ClaudeOpus4_8));
          }
      }
  }

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

  ```go Go
  ctx := context.Background()
  client := anthropic.NewClient()

  request := anthropic.BetaMessageNewParams{
  	MaxTokens: 1024,
  	Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaFallbackCredit2026_07_01},
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
  	},
  }

  send := func(model anthropic.Model, body anthropic.BetaMessageNewParams) (*anthropic.BetaMessage, error) {
  	body.Model = model
  	return client.Beta.Messages.New(ctx, body)
  }
  // Un 400 no transitorio significa que esta forma de intento o token fue rechazado y
  // debe ejecutarse el siguiente peldaño de la escalera. "redemption temporarily
  // unavailable" es transitorio: muéstralo y reintenta con el token dentro de
  // su ventana de cinco minutos.
  canFallBack := func(err error) bool {
  	apiErr, ok := errors.AsType[*anthropic.Error](err)
  	return ok && apiErr.StatusCode == 400 &&
  		!strings.Contains(apiErr.Error(), "redemption temporarily unavailable")
  }

  response, err := send(anthropic.ModelClaudeFable5, request)
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == anthropic.BetaStopReasonRefusal {
  	details := response.StopDetails
  	if token := details.FallbackCreditToken; token != "" {
  		exactBody := request
  		exactBody.FallbackCreditToken = anthropic.BetaMessageNewParamsFallbackCreditTokenUnion{
  			OfString: anthropic.String(token),
  		}
  		attempt := exactBody
  		// Prefiere la forma de continuación a menos que la afirmación sea falsa
  		if details.FallbackHasPrefillClaim || !details.JSON.FallbackHasPrefillClaim.Valid() {
  			echoed := response.ToParam()
  			if len(echoed.Content) > 0 {
  				if text := echoed.Content[len(echoed.Content)-1].OfText; text != nil {
  					text.Text = strings.TrimRightFunc(text.Text, unicode.IsSpace)
  				}
  			}
  			attempt.Messages = append(slices.Clone(request.Messages), echoed)
  		}
  		response, err = send(anthropic.ModelClaudeOpus4_8, attempt)
  		if err != nil && canFallBack(err) {
  			// Recurre al cuerpo sin cambios, aún con el token
  			response, err = send(anthropic.ModelClaudeOpus4_8, exactBody)
  			if err != nil && canFallBack(err) {
  				// El token en sí fue rechazado: descártalo y reintenta sin él.
  				response, err = send(anthropic.ModelClaudeOpus4_8, request)
  			}
  		}
  		if err != nil {
  			log.Fatal(err)
  		}
  	}
  }

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

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

  MessageCreateParams.Builder request() {
      return MessageCreateParams.builder()
          .maxTokens(1024L)
          .addUserMessage("Hello, Claude")
          .addBeta(AnthropicBeta.FALLBACK_CREDIT_2026_07_01);
  }

  BetaMessage send(Model model, MessageCreateParams.Builder body) {
      return client.beta().messages().create(body.model(model).build());
  }

  void main() {
      BetaMessage response = send(Model.CLAUDE_FABLE_5, request());

      if (response.stopReason().map(BetaStopReason.REFUSAL::equals).orElse(false)
              && response.stopDetails().orElse(null) instanceof BetaRefusalStopDetails details
              && details.fallbackCreditToken().orElse(null) instanceof String creditToken) {
          MessageCreateParams.Builder attempt = request().fallbackCreditToken(creditToken);
          // Prefiere la forma de continuación a menos que la afirmación sea falsa
          if (details.fallbackHasPrefillClaim().orElse(true)) {
              List<BetaContentBlockParam> echoed = new ArrayList<>(
                  response.content().stream().map(BetaContentBlock::toParam).toList());
              if (!echoed.isEmpty() && echoed.getLast().isText()) {
                  var lastText = echoed.removeLast().asText();
                  echoed.addLast(BetaContentBlockParam.ofText(
                      lastText.toBuilder().text(lastText.text().stripTrailing()).build()));
              }
              attempt.addAssistantMessageOfBetaContentBlockParams(echoed);
          }
          try {
              response = send(Model.CLAUDE_OPUS_4_8, attempt);
          } catch (BadRequestException badRequest) {
              // Transitorio: reintenta con el token dentro de su ventana de cinco minutos
              if (badRequest.getMessage().contains("redemption temporarily unavailable")) {
                  throw badRequest;
              }
              try {
                  // Recurre al cuerpo sin cambios, aún con el token
                  response = send(Model.CLAUDE_OPUS_4_8, request().fallbackCreditToken(creditToken));
              } catch (BadRequestException retryBadRequest) {
                  if (retryBadRequest.getMessage().contains("redemption temporarily unavailable")) {
                      throw retryBadRequest;
                  }
                  // El token en sí fue rechazado: descártalo y reintenta sin él.
                  response = send(Model.CLAUDE_OPUS_4_8, request());
              }
          }
      }

      IO.println("""
          {"stop_reason": "%s", "model": "%s"}"""
          .formatted(response.stopReason().orElseThrow(), response.model()));
  }
  ```

  ```php PHP
  $client = new Client();
  $beta = 'fallback-credit-2026-07-01';
  $messages = [['role' => 'user', 'content' => 'Hello, Claude']];

  $send = fn (string $model, array $messages, ?string $token = null) => $client->beta->messages->create(
      maxTokens: 1024,
      messages: $messages,
      model: $model,
      fallbackCreditToken: $token,
      betas: [$beta],
  );
  $response = $send('claude-fable-5', $messages);

  $token = $response->stopReason === 'refusal'
      ? $response->stopDetails?->fallbackCreditToken
      : null;

  if ($token !== null) {
      $attemptMessages = $messages;
      // Prefiere la forma de continuación a menos que la afirmación sea falsa
      if ($response->stopDetails->fallbackHasPrefillClaim !== false) {
          $echoed = $response->content
              |> json_encode(...)
              |> (fn (string $json): array => json_decode($json, associative: true));
          $lastIndex = array_key_last($echoed);
          if ($lastIndex !== null && $echoed[$lastIndex]['type'] === 'text') {
              $echoed[$lastIndex]['text'] = rtrim($echoed[$lastIndex]['text']);
          }
          $attemptMessages[] = ['role' => 'assistant', 'content' => $echoed];
      }
      // Transitorio: reintenta con el token dentro de su ventana de cinco minutos
      $isTransientRedemption = fn (BadRequestException $error): bool =>
          str_contains($error->getMessage(), 'redemption temporarily unavailable');
      try {
          $response = $send('claude-opus-4-8', $attemptMessages, $token);
      } catch (BadRequestException $error) {
          if ($isTransientRedemption($error)) {
              throw $error;
          }
          try {
              // Recurre al cuerpo sin cambios, aún con el token
              $response = $send('claude-opus-4-8', $messages, $token);
          } catch (BadRequestException $retryError) {
              if ($isTransientRedemption($retryError)) {
                  throw $retryError;
              }
              // El token en sí fue rechazado: descártalo y reintenta sin él.
              $response = $send('claude-opus-4-8', $messages);
          }
      }
  }

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

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

  request = {
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}]
  }

  send_message = ->(model, body) do
    client.beta.messages.create(model:, betas: ["fallback-credit-2026-07-01"], **body)
  end

  response = send_message.call("claude-fable-5", request)

  if response in {stop_reason: :refusal,
                  stop_details: {fallback_credit_token: String => credit_token} => details}
    exact_body = request.merge(fallback_credit_token: credit_token)

    # Prefiere la forma de continuación a menos que la afirmación sea falsa
    attempt = if details.fallback_has_prefill_claim != false
      echoed = response.content.map(&:to_h)
      if echoed.last in {type: :text, text: String => final_text}
        echoed[-1] = echoed.last.merge(text: final_text.rstrip)
      end
      exact_body.merge(
        messages: [*request[:messages], {role: "assistant", content: echoed}]
      )
    else
      exact_body
    end

    begin
      response = send_message.call("claude-opus-4-8", attempt)
    rescue Anthropic::Errors::BadRequestError => error
      # Transitorio: reintenta con el token dentro de su ventana de cinco minutos
      raise if error.message.include?("redemption temporarily unavailable")
      begin
        # Recurre al cuerpo sin cambios, aún con el token
        response = send_message.call("claude-opus-4-8", exact_body)
      rescue Anthropic::Errors::BadRequestError => error
        # Transitorio: reintenta con el token dentro de su ventana de cinco minutos
        raise if error.message.include?("redemption temporarily unavailable")
        # El token en sí fue rechazado: descártalo y reintenta sin él.
        response = send_message.call("claude-opus-4-8", request)
      end
    end
  end

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

## Dónde funciona

El crédito de respaldo está en beta en la Claude API, Amazon Bedrock, Claude Platform on AWS, Google Cloud y Microsoft Foundry. Los rechazos en [Message Batches](https://platform.claude.com/docs/es/build-with-claude/batch-processing) no generan tokens de crédito, y el canje solo se aplica a solicitudes directas a la Messages API: un token enviado en una solicitud por lotes se acepta pero se ignora. Un rechazo de Claude Haiku 5.5 no incluye crédito de respaldo, por lo que un reintento posterior paga el costo completo de escribir la caché de prompts del modelo de respaldo.

El modelo del reintento debe ser uno de los destinos de respaldo permitidos del modelo que rechazó. Para Claude Fable 5.1, Claude Fable 5 y Claude Opus 5.5, estos son Claude Opus 4.8 (`claude-opus-4-8`) y Claude Opus 5 (`claude-opus-5`). Para Claude Opus 5, es Claude Opus 4.8. Para Claude Sonnet 5.5, es Claude Sonnet 5 (`claude-sonnet-5`).

<Accordion title="Consultar los destinos de respaldo permitidos de forma programática">
  En la Claude API y Claude Platform en AWS, la lista de destinos se publica como `allowed_fallback_models` en la entrada de cada modelo en la [Models API](https://platform.claude.com/docs/es/api/models/list) cuando se establece el encabezado beta `server-side-fallback-2026-07-01`. La lista aún no es visible solo con el encabezado `fallback-credit-*`. No se expone en Amazon Bedrock, Google Cloud ni Microsoft Foundry.
</Accordion>

## Verificar que el crédito se aplicó

El reembolso es visible en el `usage` del reintento. En comparación con lo que la misma solicitud reportaría sin el token, `cache_creation_input_tokens` es menor y `cache_read_input_tokens` es mayor en la misma cantidad. Un cambio de cero significa que el token fue aceptado pero no había nada que recalcular, por ejemplo porque la caché del modelo de reintento ya estaba caliente.

## Cuando un reintento es rechazado

La mayoría de los reintentos se canjean en el primer intento. Cuando uno no lo hace, la API devuelve un error 400 que te indica qué probar a continuación.

<Steps>
  <Step title="Continuación rechazada: reenvía el cuerpo sin cambios">
    Si el reintento que añade el mensaje de asistente es rechazado con un error 400, reenvía el cuerpo de la solicitud rechazada sin cambios, aún con el token.
  </Step>

  <Step title="Token rechazado: descarta el token">
    Si el cuerpo sin cambios también es rechazado con un error 400 cuyo mensaje menciona `fallback_credit_token`, reintenta sin el token. El crédito se pierde, pero el reintento en sí se procesa.
  </Step>
</Steps>

<Note>
  Si la solicitud rechazada ejecutó herramientas de servidor, un reintento sin token vuelve a ejecutar y vuelve a facturar esas herramientas. En ese caso, muestra el error 400 a quien te llamó en lugar de caer en un reintento sin token.
</Note>

<Accordion title="Si el error dice 'redemption temporarily unavailable'">
  Este rechazo es transitorio, no un veredicto sobre la forma de tu reintento. Reintenta la misma solicitud, con el mismo token, dentro de la ventana de cinco minutos del token. No pases al siguiente paso de la escalera.
</Accordion>

## Referencia

Las siguientes secciones cubren casos límite y las reglas completas de canje. La mayoría de las integraciones no las necesitan.

<Accordion title="Campos que deben coincidir con la solicitud rechazada">
  El canje compara el reintento con la solicitud rechazada. Cada campo que da forma al prompt debe coincidir exactamente. Los campos que no dan forma al prompt pueden cambiar en el reintento.

  | Regla                          | Campos                                                                                                                                                                   |
  | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | Deben coincidir exactamente    | `system`, `messages`, `tools`, `tool_choice`, `thinking` y `cache_control`, además de `output_config`, `mcp_servers`, `context_management` y `container` cuando los usas |
  | Pueden cambiar en el reintento | `model`, `max_tokens`, `stop_sequences`, `temperature`, `top_p`, `top_k`, `stream`, `metadata` y `service_tier`                                                          |

  La forma de continuación (`fallback_has_prefill_claim: true`) es la única excepción a la coincidencia de `messages`: agrega exactamente un mensaje de asistente al final de `messages`.

  No elimines los bloques `thinking` o `redacted_thinking` de turnos anteriores en el reintento, aunque un reintento simple sin token normalmente los elimina. El cuerpo debe coincidir con la solicitud rechazada, y el servidor maneja esos bloques por sí mismo.
</Accordion>

<Accordion title="Los encabezados beta también deben coincidir">
  Envía los mismos encabezados `anthropic-beta` en el reintento que en la solicitud rechazada. Un encabezado beta presente en una de las dos solicitudes pero no en la otra puede hacer fallar la coincidencia incluso cuando los cuerpos son idénticos. El error 400 resultante lleva el mismo mensaje `request body ... does not match` que una diferencia en el cuerpo, por lo que es fácil confundir una diferencia de encabezados con un problema del cuerpo. En particular, no agregues ni elimines encabezados beta según el modelo al que se dirige la solicitud.

  Dos familias de encabezados están exentas de la coincidencia, en beneficio del reintento:

  * **`server-side-fallback-*`:** un reintento debe eliminar el parámetro `fallbacks`, y eliminar este encabezado junto con él no causa una discrepancia.
  * **`fallback-credit-*`:** mantén este encabezado en ambas solicitudes. El reintento lo necesita para canjear el token.

  <Note>
    En los modelos que incluyen la ventana de contexto de 1M tokens de forma predeterminada, como Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Sonnet 5.5 y Claude Sonnet 5, el encabezado beta `context-1m-2025-08-07` no tiene efecto. Para mantener idénticas las dos solicitudes, omite ese encabezado en ambas en lugar de enviarlo en una y no en la otra.
  </Note>
</Accordion>

<Accordion title="Cuando fallback_has_prefill_claim está ausente">
  El campo no tiene valor solo cuando el token tampoco lo tiene, así que mientras tengas un token el campo tiene un valor, excepto en Amazon Bedrock, Google Cloud y Microsoft Foundry, donde todavía puede estar ausente mientras se implementa su compatibilidad con el campo. En ese caso, trata la forma del reintento como desconocida en lugar de como `false` (python: `False`). Prueba primero la forma con el mensaje de asistente añadido y confía en el manejo de rechazos de [Cuando un reintento es rechazado](https://platform.claude.com/docs/es/build-with-claude/fallback-credit#when-a-retry-is-rejected), que recurre al cuerpo sin cambios.
</Accordion>

<Accordion title="Replicar el content de la respuesta rechazada">
  Cuando el token de un rechazo admite la forma de continuación, el `content` de la respuesta contiene solo la salida propia del modelo, y la explicación del rechazo se entrega en `stop_details.explanation`. Por lo tanto, puedes replicar `content` en el mensaje de asistente añadido tal como está.

  Aun así, pueden ser necesarios dos ajustes antes de enviar:

  * Si el bloque final que envías es un bloque `text`, elimina sus espacios en blanco finales.
  * Omite cualquier bloque `tool_use` del lado del cliente que no tenga un `tool_result` correspondiente.

  Si el contenido replicado incluye un bloque `fallback` de un [respaldo del lado del servidor](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#server-side-fallback) anterior, mantén el bloque exactamente donde apareció. Se acepta en cualquier solicitud sin un encabezado beta. La API usa su posición para validar los bloques de pensamiento a su alrededor, por lo que una solicitud que replica bloques de pensamiento de ambos lados de ese límite es rechazada si el bloque se omite o se mueve.
</Accordion>

<Accordion title="Alcance y vigencia del token">
  El token se canjea solo desde la organización y el espacio de trabajo que recibieron el rechazo, incluso en Microsoft Foundry. En Amazon Bedrock y Google Cloud, que no tienen espacios de trabajo, el token está vinculado en su lugar a la identidad del llamador de la plataforma.

  El token expira cinco minutos después del rechazo. Después de eso, envía el reintento sin él. El token también es sin estado: el servidor no almacena nada sobre él, y no hay ningún endpoint para inspeccionarlo o revocarlo.
</Accordion>

<Accordion title="Cuando un token no puede canjearse con ninguna de las dos formas">
  Cuando el rechazo llegó después de que las herramientas de servidor ya se habían ejecutado dentro de la solicitud, el token se canjea solo continuando la respuesta parcial. Esa restricción es lo que evita que las llamadas a herramientas completadas se ejecuten, y se facturen, de nuevo.

  Por lo tanto, una combinación puede dejar el token sin posibilidad de canje con ninguna de las dos formas, cuando se cumplen ambas condiciones siguientes:

  * La solicitud usó `output_config.format` o un `tool_choice` que fuerza el uso de herramientas. Cualquiera de los dos descarta la forma con el mensaje de asistente añadido.
  * El rechazo llegó después de que se ejecutaran herramientas de servidor. Eso descarta el cuerpo sin cambios.

  Si el reintento con el cuerpo sin cambios es rechazado con un error 400 que dice que el token debe canjearse continuando la respuesta parcial, descarta el token. Un reintento sin él se procesa, pero vuelve a ejecutar y vuelve a facturar las herramientas de servidor completadas. Muestra el costo o el error a quien te llamó en lugar de reintentar silenciosamente.
</Accordion>

## Próximos pasos

<CardGroup>
  <Card title="Rechazos y respaldo" icon="shield" href="https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback">
    Detecta rechazos y elige entre el respaldo del lado del servidor, el middleware del SDK y un reintento manual.
  </Card>

  <Card title="Almacenamiento en caché de prompts" icon="bolt" href="https://platform.claude.com/docs/es/build-with-claude/prompt-caching">
    Cómo se facturan las lecturas de caché y las escrituras en caché.
  </Card>

  <Card title="Razones de detención y respaldo" 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">
    El asistente del SDK que aplica el crédito de respaldo automáticamente.
  </Card>
</CardGroup>
