---
title: フォールバッククレジット
url: https://platform.claude.com/docs/ja/build-with-claude/fallback-credit
description: 拒否されたリクエストを別のモデルで再試行する際に、プロンプトキャッシュのコストを二重に支払うことを回避します。
---

プロンプトキャッシュはモデルごとに保持されます。あるモデルがリクエストを拒否し、別のモデルで再試行する場合、最初のモデル向けにすでにキャッシュされていた会話のプレフィックスは、新しいモデルのキャッシュにゼロから書き込む必要があります。キャッシュ書き込みはキャッシュ読み取りよりもコストがかかります。「fallback credit」（フォールバッククレジット）はその追加コストを取り除きます。拒否レスポンスにはクレジットトークンが含まれており、再試行時にそのトークンをそのまま送り返すと、再試行は会話が最初から新しいモデル上で行われていたかのように課金されます。

このページが必要になるのは、生のHTTPやカスタムの再試行ロジックを使って、再試行を自分で構築する場合のみです。[サーバーサイドフォールバック](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback#server-side-fallback)と[SDKミドルウェア](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback#client-side-fallback)はフォールバッククレジットを自動的に適用します。いずれかを使用している場合は、このページを読み飛ばしてください。

[拒否とフォールバック](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback)では、拒否の検出とフォールバック手法の選択について説明しています。キャッシュ読み取りやキャッシュ書き込みという用語に馴染みがない場合は、[プロンプトキャッシング](https://platform.claude.com/docs/ja/build-with-claude/prompt-caching)で説明しています。

## 基本的な流れ

<Steps>
  <Step title="ベータヘッダーでオプトインする">
    拒否される可能性のあるリクエストを、`anthropic-beta: fallback-credit-2026-07-01` ヘッダー付きで送信します。`server-side-fallback-2026-07-01` ヘッダーでも同じフィールドが付与されますが、Amazon BedrockとGoogle Cloudではこのヘッダーに対して400エラーが返されます。以前の `fallback-credit-2026-06-01` ヘッダーも引き続き受け付けられ、同じフィールドが付与されます。
  </Step>

  <Step title="拒否から2つのフィールドを読み取る">
    拒否時、`stop_details` には2つのフィールドが含まれます。

    * **`fallback_credit_token`:** クレジットを表す不透明な文字列です。
    * **`fallback_has_prefill_claim`:** どちらの再試行ボディ形式を使用すべきかを示すブール値です。

    その拒否に対してクレジットが利用できない場合、両方とも `null` になります。
  </Step>

  <Step title="再試行を構築する">
    拒否されたリクエストボディから始めます。`model` をフォールバックモデルに設定し、トークンをトップレベルの `fallback_credit_token` パラメータとして追加します。以下の表からボディ形式を選択してください。
  </Step>

  <Step title="同じヘッダーを付けて再試行を送信する">
    同じ `fallback-credit-2026-07-01` ベータヘッダーを付けて再試行を送信します。トークンを引き換えるには、再試行にこのヘッダーが必要です。
  </Step>
</Steps>

`fallback_has_prefill_claim` フィールドは、再試行が最初からやり直すのではなく、拒否したモデルの部分的な出力を継続できるかどうかを示します。

| `fallback_has_prefill_claim` | 再試行ボディ                                                                                                                                             |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `true`                       | 拒否されたリクエストボディをそのまま使用し、拒否レスポンスの `content` をそのまま反映した `content` を持つアシスタントメッセージを1つ末尾に追加します。再試行モデルは拒否したモデルが停止した箇所からレスポンスを継続し、完了済みのサーバーツール呼び出しは再実行されません。 |
| `false`                      | 拒否されたリクエストボディをそのまま使用します。                                                                                                                           |

## 例

以下の例では、拒否される可能性のあるリクエストを行い、Claude Opus 4.8 に対する再試行でクレジットトークンを引き換えます。再試行が拒否された場合、この例は拒否ラダー、すなわち[再試行が拒否された場合](https://platform.claude.com/docs/ja/build-with-claude/fallback-credit#when-a-retry-is-rejected)で説明する、段階的に単純になっていく再試行形式の順序に沿って段階的に処理を進めます。

<CodeGroup>
  ```bash cURL
  # 最初のリクエスト(拒否される可能性あり)
  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"}]
    }')

  # 拒否時は stop_details に1回限りのクレジットトークンが含まれる
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # クレジットトークンを付けてフォールバックモデルで再試行(同じボディ)
    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

  # 拒否処理の全段階については SDK のサンプルを参照。
  jq -c '{stop_reason, model}' <<<"${response}"
  ```

  ```bash CLI
  # 最初のリクエスト(拒否される可能性あり)
  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)

  # 拒否時はstop_detailsに1回限りのクレジットトークンが含まれます
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # クレジットトークンを使ってフォールバックモデルで再試行
    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

  # 拒否処理の全段階についてはSDKのサンプルを参照してください。
  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}
      # claimが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:
              # トークンを保持したまま、変更なしのボディにフォールバックする
              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
              # トークン自体が拒否された場合: トークンを破棄し、トークンなしで再試行する。
              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
    };

    // 最もリッチな形式から始め、拒否されるたびに段階的に縮小します: 継続の
    // 形式（主張が偽でない限り）、トークンを保持したままの変更なしのボディ、
    // そして最後にトークンを放棄します。
    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) {
      // 形式に関連する400の場合のみ縮小します。"redemption temporarily
      // unavailable"は一時的なものです: 代わりにトークンの
      // 5分間のウィンドウ内で同じ方法で再試行してください。
      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;
      // クレームが偽でない限り、継続形式を優先します
      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)
                      ),
                  },
              ],
          };
      }
      // 一時的な「引き換えは一時的に利用不可」という拒否は、
      // 以下の各catchフィルターから伝播します。5分間の有効期間内にトークンを使って再試行します。
      try
      {
          response = await client.Beta.Messages.Create(attempt);
      }
      catch (AnthropicBadRequestException e)
          when (!e.Message.Contains("redemption temporarily unavailable"))
      {
          try
          {
              // 変更されていないボディにフォールバックします（トークンは保持したまま）
              response = await client.Beta.Messages.Create(exactBody);
          }
          catch (AnthropicBadRequestException retryError)
              when (!retryError.Message.Contains("redemption temporarily unavailable"))
          {
              // トークン自体が拒否されました。トークンを破棄し、トークンなしで再試行します。
              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)
  }
  // 一時的でない400は、この試行の形式またはトークンが拒否されたことを意味し、
  // ラダーの次の段を実行すべきです。"redemption temporarily
  // unavailable"は一時的なものです。これを表示し、トークンの
  // 5分間の有効期間内にトークン付きで再試行します。
  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
  		// 主張が偽でない限り、継続の形式を優先します
  		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) {
  			// 変更なしのボディにフォールバックします（トークンは付けたまま）
  			response, err = send(anthropic.ModelClaudeOpus4_8, exactBody)
  			if err != nil && canFallBack(err) {
  				// トークン自体が拒否されました。トークンを放棄し、トークンなしで再試行します。
  				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);
          // 主張が偽でない限り、継続の形式を優先します
          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) {
              // 一時的なエラー: 5分間の有効期間内にトークンを使って再試行します
              if (badRequest.getMessage().contains("redemption temporarily unavailable")) {
                  throw badRequest;
              }
              try {
                  // 変更なしの本文にフォールバックします（トークンは引き続き付与）
                  response = send(Model.CLAUDE_OPUS_4_8, request().fallbackCreditToken(creditToken));
              } catch (BadRequestException retryBadRequest) {
                  if (retryBadRequest.getMessage().contains("redemption temporarily unavailable")) {
                      throw retryBadRequest;
                  }
                  // トークン自体が拒否されました: トークンを破棄し、トークンなしで再試行します。
                  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;
      // 主張が偽でない限り、継続の形式を優先する
      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];
      }
      // 一時的なエラー: 5分間の有効期間内にトークンを使って再試行する
      $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 {
              // 変更なしのボディにフォールバックする（トークンは引き続き付与）
              $response = $send('claude-opus-4-8', $messages, $token);
          } catch (BadRequestException $retryError) {
              if ($isTransientRedemption($retryError)) {
                  throw $retryError;
              }
              // トークン自体が拒否された: トークンを破棄し、トークンなしで再試行する。
              $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)

    # 主張が偽でない限り、継続形式を優先する
    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
      # 一時的なエラー: 5分間の有効期間内にトークンを使って再試行する
      raise if error.message.include?("redemption temporarily unavailable")
      begin
        # 変更なしの本文にフォールバックする（トークンは引き続き付与）
        response = send_message.call("claude-opus-4-8", exact_body)
      rescue Anthropic::Errors::BadRequestError => error
        # 一時的なエラー: 5分間の有効期間内にトークンを使って再試行する
        raise if error.message.include?("redemption temporarily unavailable")
        # トークン自体が拒否された: トークンを破棄し、トークンなしで再試行する。
        response = send_message.call("claude-opus-4-8", request)
      end
    end
  end

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

## 利用できる環境

フォールバッククレジットは、Claude API、Amazon Bedrock、Claude Platform on AWS、Google Cloud、Microsoft Foundryでベータ版として提供されています。[Message Batches](https://platform.claude.com/docs/ja/build-with-claude/batch-processing)での拒否ではクレジットトークンは発行されず、引き換えは直接のMessages APIリクエストにのみ適用されます。バッチリクエストで渡されたトークンは受け付けられますが、無視されます。Claude Haiku 5.5の拒否にはフォールバッククレジットが含まれないため、その後の再試行ではフォールバックモデルのプロンプトキャッシュ書き込みの全コストを支払うことになります。

再試行モデルは、拒否したモデルで許可されているフォールバック先のいずれかである必要があります。Claude Fable 5.1、Claude Fable 5、Claude Opus 5.5の場合、フォールバック先はClaude Opus 4.8（`claude-opus-4-8`）とClaude Opus 5（`claude-opus-5`）です。Claude Opus 5の場合はClaude Opus 4.8です。Claude Sonnet 5.5の場合はClaude Sonnet 5（`claude-sonnet-5`）です。

<Accordion title="許可されたフォールバック先をプログラムで調べる">
  Claude API および Claude Platform on AWS では、`server-side-fallback-2026-07-01` ベータヘッダーが設定されている場合、[Models API](https://platform.claude.com/docs/ja/api/models/list) の各モデルのエントリに `allowed_fallback_models` としてフォールバック先のリストが公開されます。このリストは `fallback-credit-*` ヘッダー単独ではまだ表示されません。Amazon Bedrock、Google Cloud、Microsoft Foundry では公開されていません。
</Accordion>

## クレジットが適用されたことを確認する

払い戻しは再試行の `usage` で確認できます。トークンなしで同じリクエストを送った場合の報告値と比較すると、`cache_creation_input_tokens` が少なくなり、`cache_read_input_tokens` が同じ量だけ多くなります。差がゼロの場合は、トークンは受理されたものの、再計算すべきものがなかったことを意味します。たとえば、再試行モデルのキャッシュがすでにウォーム状態だった場合などです。

## 再試行が拒否された場合

ほとんどの再試行は最初の試行で引き換えに成功します。成功しなかった場合、API は次に何を試すべきかを示す 400 エラーを返します。

<Steps>
  <Step title="継続が拒否された場合: 変更なしのボディを再送信する">
    アシスタントメッセージを追加した再試行が 400 エラーで拒否された場合は、拒否されたリクエストボディを変更せずに、トークンを付けたまま再送信します。
  </Step>

  <Step title="トークンが拒否された場合: トークンを外す">
    変更なしのボディも、メッセージに `fallback_credit_token` の名前が含まれる 400 エラーで拒否された場合は、トークンなしで再試行します。クレジットは失われますが、再試行自体は通ります。
  </Step>
</Steps>

<Note>
  拒否されたリクエストでサーバーツールが実行されていた場合、トークンなしの再試行ではそれらのツールが再実行され、再課金されます。その場合は、トークンなしの再試行に進むのではなく、400 エラーを呼び出し元に伝えてください。
</Note>

<Accordion title="エラーに 'redemption temporarily unavailable' と表示された場合">
  この拒否は一時的なものであり、再試行形式に対する判定ではありません。トークンの5分間の有効期間内に、同じトークンを付けて同じリクエストを再試行してください。ラダーの次のステップには進まないでください。
</Accordion>

## リファレンス

以下のセクションでは、エッジケースと完全な引き換えルールについて説明します。ほとんどの統合では必要ありません。

<Accordion title="拒否されたリクエストと一致しなければならないフィールド">
  引き換えでは、再試行が拒否されたリクエストと比較されます。プロンプトを形成するすべてのフィールドは完全に一致する必要があります。プロンプトを形成しないフィールドは再試行時に変更できます。

  | ルール          | フィールド                                                                                                                                            |
  | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
  | 完全に一致する必要がある | `system`、`messages`、`tools`、`tool_choice`、`thinking`、`cache_control`、および使用している場合は `output_config`、`mcp_servers`、`context_management`、`container` |
  | 再試行時に変更可能    | `model`、`max_tokens`、`stop_sequences`、`temperature`、`top_p`、`top_k`、`stream`、`metadata`、`service_tier`                                           |

  継続形式（`fallback_has_prefill_claim: true`）は `messages` の一致に対する唯一の例外です。この形式では `messages` の末尾にアシスタントメッセージをちょうど1つ追加します。

  トークンなしの通常の再試行では通常それらを削除しますが、再試行時に以前のターンの `thinking` ブロックや `redacted_thinking` ブロックを削除しないでください。ボディは拒否されたリクエストと一致する必要があり、それらのブロックはサーバー側で処理されます。
</Accordion>

<Accordion title="ベータヘッダーも一致する必要がある">
  再試行では、拒否されたリクエストと同じ `anthropic-beta` ヘッダーを送信してください。2つのリクエストの一方にのみ存在するベータヘッダーがあると、ボディが同一であっても一致に失敗する可能性があります。その結果生じる 400 エラーには、ボディの差異の場合と同じ `request body ... does not match` メッセージが含まれるため、ヘッダーの差異をボディの問題と誤解しやすくなります。特に、リクエストの対象モデルに応じてベータヘッダーを追加したり削除したりしないでください。

  再試行のために、2つのヘッダーファミリーは一致の対象から除外されています。

  * **`server-side-fallback-*`:** 再試行では `fallbacks` パラメータを削除する必要があり、それに伴ってこのヘッダーを削除しても不一致にはなりません。
  * **`fallback-credit-*`:** このヘッダーは両方のリクエストに付けたままにしてください。トークンを引き換えるには再試行にこのヘッダーが必要です。

  <Note>
    Claude Fable 5.1、Claude Fable 5、Claude Opus 5.5、Claude Opus 5、Claude Opus 4.8、Claude Sonnet 5.5、Claude Sonnet 5など、デフォルトで100万トークンのコンテキストウィンドウを含むモデルでは、`context-1m-2025-08-07` ベータヘッダーは効果がありません。2つのリクエストを同一に保つため、一方にのみ送信するのではなく、両方でこのヘッダーを省略してください。
  </Note>
</Accordion>

<Accordion title="fallback_has_prefill_claim が存在しない場合">
  このフィールドに値がないのはトークンにも値がない場合のみであるため、トークンを保持している間はこのフィールドに値があります。ただし、Amazon Bedrock、Google Cloud、Microsoft Foundryでは、このフィールドのサポートが展開中であるため、まだ存在しない場合があります。その場合は、再試行の形式を `false` (python: `False`) としてではなく、不明として扱ってください。まずアシスタントメッセージを追加する形式を試し、変更していないボディにフォールバックする[再試行が拒否された場合](https://platform.claude.com/docs/ja/build-with-claude/fallback-credit#when-a-retry-is-rejected)の拒否処理に頼ってください。
</Accordion>

<Accordion title="拒否レスポンスの content をそのまま反映する">
  拒否のトークンが継続形式をサポートしている場合、レスポンスの `content` にはモデル自身の出力のみが含まれ、拒否の説明は `stop_details.explanation` で提供されます。したがって、`content` をそのまま追加のアシスタントメッセージに反映できます。

  送信前に、なお2つの調整が必要になる場合があります。

  * 送信する最後のブロックが `text` ブロックの場合は、末尾の空白を削除してください。
  * 対応する `tool_result` のないクライアントサイドの `tool_use` ブロックは省略してください。

  反映する content に、以前の[サーバーサイドフォールバック](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback#server-side-fallback)による `fallback` ブロックが含まれている場合は、そのブロックを元の位置にそのまま残してください。このブロックはベータヘッダーなしでもあらゆるリクエストで受け付けられます。API はその位置を使って周囲の thinking ブロックを検証するため、その境界の両側の thinking ブロックを反映するリクエストは、このブロックが省略または移動されていると拒否されます。
</Accordion>

<Accordion title="トークンのスコープと有効期間">
  トークンは、Microsoft Foundry 上を含め、拒否を受け取った組織およびワークスペースからのみ引き換えできます。ワークスペースを持たない Amazon Bedrock と Google Cloud では、トークンは代わりにプラットフォームの呼び出し元 ID に紐付けられます。

  トークンは拒否から5分後に期限切れになります。それ以降は、トークンなしで再試行を送信してください。また、トークンはステートレスです。サーバーはトークンについて何も保存せず、トークンを検査または取り消すためのエンドポイントもありません。
</Accordion>

<Accordion title="どちらの形式でもトークンを引き換えられない場合">
  リクエスト内でサーバーツールがすでに実行された後に拒否が発生した場合、トークンは部分的なレスポンスを継続することによってのみ引き換えできます。この制限により、完了済みのツール呼び出しが再度実行され、再度課金されることが防止されます。

  したがって、以下の両方が当てはまる場合、どちらの形式でもトークンを引き換えられなくなる組み合わせが1つ存在します。

  * リクエストが `output_config.format` またはツール使用を強制する `tool_choice` を使用していた。いずれもアシスタントメッセージ追加形式を使用できなくします。
  * サーバーツールが実行された後に拒否が発生した。これにより変更なしのボディが使用できなくなります。

  変更なしボディの再試行が、トークンは部分的なレスポンスを継続することで引き換える必要があるという 400 エラーで拒否された場合は、トークンを破棄してください。トークンなしの再試行は通りますが、完了済みのサーバーツールが再実行され、再課金されます。黙って再試行するのではなく、コストまたはエラーを呼び出し元に伝えてください。
</Accordion>

## 次のステップ

<CardGroup>
  <Card title="拒否とフォールバック" icon="shield" href="https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback">
    拒否を検出し、サーバーサイドフォールバック、SDKミドルウェア、手動再試行のいずれかを選択します。
  </Card>

  <Card title="プロンプトキャッシング" icon="bolt" href="https://platform.claude.com/docs/ja/build-with-claude/prompt-caching">
    キャッシュ読み取りとキャッシュ書き込みの課金方法。
  </Card>

  <Card title="停止理由とフォールバック" icon="code" href="https://platform.claude.com/docs/ja/build-with-claude/handling-stop-reasons">
    すべての `stop_reason` の値とその処理方法。
  </Card>

  <Card title="SDKミドルウェア" icon="settings" href="https://platform.claude.com/docs/ja/cli-sdks-libraries/middleware">
    フォールバッククレジットを自動的に適用する SDK ヘルパー。
  </Card>
</CardGroup>
