---
title: Kredit fallback
url: https://platform.claude.com/docs/id/build-with-claude/fallback-credit
description: Hindari membayar biaya prompt-cache dua kali saat Anda mencoba ulang permintaan yang ditolak pada model lain.
---

Prompt cache bersifat per-model. Ketika sebuah model menolak permintaan dan Anda mencoba ulang pada model lain, prefiks percakapan yang sudah di-cache untuk model pertama harus ditulis ke dalam cache model baru dari awal. Penulisan cache (cache write) lebih mahal daripada pembacaan cache (cache read). "Fallback credit" (kredit fallback) menghilangkan biaya tambahan tersebut. Penolakan membawa token kredit, Anda mengirimkan kembali token tersebut pada percobaan ulang, dan percobaan ulang ditagih seolah-olah percakapan telah berada pada model baru sejak awal.

Anda memerlukan halaman ini hanya jika Anda membangun percobaan ulang sendiri: melalui HTTP mentah atau dengan logika percobaan ulang kustom. [Fallback sisi server](https://platform.claude.com/docs/id/build-with-claude/refusals-and-fallback#server-side-fallback) dan [middleware SDK](https://platform.claude.com/docs/id/build-with-claude/refusals-and-fallback#client-side-fallback) menerapkan kredit fallback secara otomatis. Jika Anda menggunakan salah satunya, lewati halaman ini.

[Penolakan dan fallback](https://platform.claude.com/docs/id/build-with-claude/refusals-and-fallback) membahas cara mendeteksi penolakan dan memilih pendekatan fallback. [Caching prompt](https://platform.claude.com/docs/id/build-with-claude/prompt-caching) menjelaskan pembacaan cache dan penulisan cache jika istilah-istilah tersebut baru bagi Anda.

## Alur dasar

<Steps>
  <Step title="Ikut serta dengan header beta">
    Kirim permintaan yang mungkin ditolak dengan header `anthropic-beta: fallback-credit-2026-07-01`. Header `server-side-fallback-2026-07-01` juga memberikan field yang sama, kecuali di Amazon Bedrock dan Google Cloud, yang mengembalikan error 400 untuk header tersebut. Header `fallback-credit-2026-06-01` yang lebih lama tetap diterima dan memberikan field yang sama.
  </Step>

  <Step title="Baca dua field dari penolakan">
    Pada penolakan, `stop_details` menyertakan dua field:

    * **`fallback_credit_token`:** string opaque yang merepresentasikan kredit.
    * **`fallback_has_prefill_claim`:** Boolean yang memberi tahu Anda bentuk body percobaan ulang mana yang harus digunakan.

    Keduanya bernilai `null` ketika tidak ada kredit yang tersedia untuk penolakan tersebut.
  </Step>

  <Step title="Bangun percobaan ulang">
    Mulai dari body permintaan yang ditolak. Atur `model` ke model fallback dan tambahkan token sebagai parameter tingkat atas `fallback_credit_token`. Pilih bentuk body dari tabel berikut.
  </Step>

  <Step title="Kirim percobaan ulang dengan header yang sama">
    Kirim percobaan ulang dengan header beta `fallback-credit-2026-07-01` yang sama. Percobaan ulang memerlukan header tersebut untuk menukarkan token.
  </Step>
</Steps>

Field `fallback_has_prefill_claim` memberi tahu Anda apakah percobaan ulang dapat melanjutkan output parsial dari model yang menolak alih-alih memulai dari awal:

| `fallback_has_prefill_claim` | Body percobaan ulang                                                                                                                                                                                                                                                                                                                 |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `true`                       | Body permintaan yang ditolak, tanpa perubahan, ditambah satu pesan asisten yang ditambahkan di akhir yang `content`-nya menyalin `content` dari respons yang ditolak. Model percobaan ulang melanjutkan respons dari titik di mana model yang menolak berhenti, dan panggilan alat server yang telah selesai tidak dieksekusi ulang. |
| `false`                      | Body permintaan yang ditolak, tanpa perubahan.                                                                                                                                                                                                                                                                                       |

## Contoh

Contoh berikut membuat permintaan yang mungkin ditolak dan menukarkan token kredit pada percobaan ulang terhadap Claude Opus 4.8. Ketika upaya percobaan ulang ditolak, contoh ini menurun melalui tangga penolakan: urutan bentuk percobaan ulang yang semakin sederhana yang dibahas di [Ketika percobaan ulang ditolak](https://platform.claude.com/docs/id/build-with-claude/fallback-credit#when-a-retry-is-rejected).

<CodeGroup>
  ```bash cURL
  # Permintaan awal (mungkin ditolak)
  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"}]
    }')

  # Penolakan membawa token kredit sekali pakai di stop_details
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # Coba lagi pada model cadangan dengan token kredit (body yang sama)
    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

  # Lihat contoh SDK untuk tangga penanganan penolakan yang lengkap.
  jq -c '{stop_reason, model}' <<<"${response}"
  ```

  ```bash CLI
  # Permintaan awal (mungkin ditolak)
  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)

  # Penolakan membawa token kredit sekali pakai di stop_details
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # Coba lagi pada model cadangan dengan token kredit
    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

  # Lihat contoh SDK untuk tahapan penanganan penolakan yang lengkap.
  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}
      # Utamakan bentuk continuation kecuali claim bernilai 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:
              # Kembali ke body yang tidak diubah, tetap dengan 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
              # Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
              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
    };

    // Bentuk terkaya lebih dulu, menurun pada setiap penolakan: bentuk
    // continuation (kecuali klaimnya salah), body tak berubah yang masih membawa
    // token, dan terakhir melepaskan 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) {
      // Turunkan hanya pada 400 terkait bentuk. "redemption temporarily
      // unavailable" bersifat sementara: coba lagi dengan cara yang sama dalam
      // jendela lima menit token tersebut.
      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;
      // Utamakan bentuk kelanjutan kecuali klaimnya salah
      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)
                      ),
                  },
              ],
          };
      }
      // Penolakan sementara "redemption temporarily unavailable" diteruskan keluar dari
      // setiap filter catch berikut: coba lagi dengan token dalam jendela lima menitnya.
      try
      {
          response = await client.Beta.Messages.Create(attempt);
      }
      catch (AnthropicBadRequestException e)
          when (!e.Message.Contains("redemption temporarily unavailable"))
      {
          try
          {
              // Kembali ke body yang tidak diubah, tetap dengan token
              response = await client.Beta.Messages.Create(exactBody);
          }
          catch (AnthropicBadRequestException retryError)
              when (!retryError.Message.Contains("redemption temporarily unavailable"))
          {
              // Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
              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 yang tidak sementara berarti bentuk percobaan atau token ini ditolak dan
  // anak tangga berikutnya harus dijalankan. "redemption temporarily
  // unavailable" bersifat sementara: tampilkan dan coba lagi dengan token dalam
  // jendela lima menitnya.
  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
  		// Utamakan bentuk lanjutan kecuali klaimnya salah
  		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) {
  			// Kembali ke body yang tidak diubah, tetap dengan token
  			response, err = send(anthropic.ModelClaudeOpus4_8, exactBody)
  			if err != nil && canFallBack(err) {
  				// Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
  				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);
          // Utamakan bentuk lanjutan kecuali klaimnya salah
          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) {
              // Sementara: coba lagi dengan token dalam jendela lima menitnya
              if (badRequest.getMessage().contains("redemption temporarily unavailable")) {
                  throw badRequest;
              }
              try {
                  // Kembali ke body yang tidak diubah, tetap dengan token
                  response = send(Model.CLAUDE_OPUS_4_8, request().fallbackCreditToken(creditToken));
              } catch (BadRequestException retryBadRequest) {
                  if (retryBadRequest.getMessage().contains("redemption temporarily unavailable")) {
                      throw retryBadRequest;
                  }
                  // Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
                  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;
      // Utamakan bentuk lanjutan kecuali klaimnya salah
      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];
      }
      // Sementara: coba lagi dengan token dalam jendela lima menitnya
      $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 {
              // Kembali ke body yang tidak diubah, tetap dengan token
              $response = $send('claude-opus-4-8', $messages, $token);
          } catch (BadRequestException $retryError) {
              if ($isTransientRedemption($retryError)) {
                  throw $retryError;
              }
              // Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
              $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)

    # Utamakan bentuk lanjutan kecuali klaimnya salah
    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
      # Sementara: coba lagi dengan token dalam jendela lima menitnya
      raise if error.message.include?("redemption temporarily unavailable")
      begin
        # Kembali ke body yang tidak diubah, tetap dengan token
        response = send_message.call("claude-opus-4-8", exact_body)
      rescue Anthropic::Errors::BadRequestError => error
        # Sementara: coba lagi dengan token dalam jendela lima menitnya
        raise if error.message.include?("redemption temporarily unavailable")
        # Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
        response = send_message.call("claude-opus-4-8", request)
      end
    end
  end

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

## Di mana fitur ini berfungsi

Kredit fallback tersedia dalam versi beta di Claude API, Amazon Bedrock, Claude Platform on AWS, Google Cloud, dan Microsoft Foundry. Penolakan dalam [Message Batches](https://platform.claude.com/docs/id/build-with-claude/batch-processing) tidak menghasilkan token kredit, dan penukaran hanya berlaku untuk permintaan Messages API langsung: token yang diteruskan pada permintaan batch diterima tetapi diabaikan. Penolakan Claude Haiku 5.5 tidak membawa kredit fallback, sehingga percobaan ulang setelahnya membayar biaya penuh untuk menulis cache prompt model fallback.

Model percobaan ulang harus merupakan salah satu target fallback yang diizinkan dari model yang menolak. Untuk Claude Fable 5.1, Claude Fable 5, dan Claude Opus 5.5, targetnya adalah Claude Opus 4.8 (`claude-opus-4-8`) dan Claude Opus 5 (`claude-opus-5`). Untuk Claude Opus 5, targetnya adalah Claude Opus 4.8. Untuk Claude Sonnet 5.5, targetnya adalah Claude Sonnet 5 (`claude-sonnet-5`).

<Accordion title="Mencari target fallback yang diizinkan secara terprogram">
  Di Claude API dan Claude Platform on AWS, daftar target dipublikasikan sebagai `allowed_fallback_models` pada entri setiap model di [Models API](https://platform.claude.com/docs/id/api/models/list) ketika header beta `server-side-fallback-2026-07-01` diatur. Daftar ini belum terlihat hanya dengan header `fallback-credit-*` saja. Daftar ini tidak diekspos di Amazon Bedrock, Google Cloud, atau Microsoft Foundry.
</Accordion>

## Memeriksa bahwa kredit telah diterapkan

Pengembalian biaya terlihat di `usage` percobaan ulang. Dibandingkan dengan apa yang akan dilaporkan oleh permintaan yang sama tanpa token, `cache_creation_input_tokens` lebih rendah, dan `cache_read_input_tokens` lebih tinggi dengan jumlah yang sama. Pergeseran nol berarti token dihormati tetapi tidak ada yang perlu dihitung ulang harganya, misalnya karena cache model percobaan ulang sudah hangat.

## Ketika percobaan ulang ditolak

Sebagian besar percobaan ulang berhasil ditukarkan pada upaya pertama. Ketika tidak berhasil, API mengembalikan error 400 yang memberi tahu Anda apa yang harus dicoba selanjutnya.

<Steps>
  <Step title="Kelanjutan ditolak: kirim ulang body tanpa perubahan">
    Jika percobaan ulang yang menambahkan pesan asisten ditolak dengan error 400, kirim ulang body permintaan yang ditolak tanpa perubahan, tetap dengan token.
  </Step>

  <Step title="Token ditolak: hapus token">
    Jika body tanpa perubahan juga ditolak dengan error 400 yang pesannya menyebut `fallback_credit_token`, coba ulang tanpa token. Kredit hangus, tetapi percobaan ulang itu sendiri berhasil.
  </Step>
</Steps>

<Note>
  Jika permintaan yang ditolak mengeksekusi alat server, percobaan ulang tanpa token akan menjalankan ulang dan menagih ulang alat-alat tersebut. Dalam kasus itu, tampilkan error 400 kepada pemanggil Anda alih-alih meneruskan ke percobaan ulang tanpa token.
</Note>

<Accordion title="Jika error menyatakan 'redemption temporarily unavailable'">
  Penolakan ini bersifat sementara, bukan keputusan atas bentuk percobaan ulang Anda. Coba ulang permintaan yang sama, dengan token yang sama, dalam jendela lima menit token tersebut. Jangan berpindah ke langkah berikutnya dalam tangga.
</Accordion>

## Referensi

Bagian-bagian berikut membahas kasus tepi dan aturan penukaran lengkap. Sebagian besar integrasi tidak memerlukannya.

<Accordion title="Field yang harus cocok dengan permintaan yang ditolak">
  Penukaran membandingkan percobaan ulang dengan permintaan yang ditolak. Setiap field yang membentuk prompt harus cocok persis. Field yang tidak membentuk prompt boleh berubah pada percobaan ulang.

  | Aturan                             | Field                                                                                                                                                                                    |
  | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | Harus cocok persis                 | `system`, `messages`, `tools`, `tool_choice`, `thinking`, dan `cache_control`, ditambah `output_config`, `mcp_servers`, `context_management`, dan `container` ketika Anda menggunakannya |
  | Boleh berubah pada percobaan ulang | `model`, `max_tokens`, `stop_sequences`, `temperature`, `top_p`, `top_k`, `stream`, `metadata`, dan `service_tier`                                                                       |

  Bentuk kelanjutan (`fallback_has_prefill_claim: true`) adalah satu-satunya pengecualian terhadap kecocokan `messages`: bentuk ini menambahkan tepat satu pesan asisten di akhir `messages`.

  Jangan menghapus blok `thinking` atau `redacted_thinking` dari giliran sebelumnya pada percobaan ulang, meskipun percobaan ulang biasa tanpa token umumnya menghapusnya. Body harus cocok dengan permintaan yang ditolak, dan server menangani blok-blok tersebut sendiri.
</Accordion>

<Accordion title="Header beta juga harus cocok">
  Kirim header `anthropic-beta` yang sama pada percobaan ulang seperti pada permintaan yang ditolak. Header beta yang ada pada salah satu dari dua permintaan tetapi tidak pada yang lain dapat menggagalkan kecocokan bahkan ketika body-nya identik. Error 400 yang dihasilkan membawa pesan `request body ... does not match` yang sama seperti perbedaan body, sehingga perbedaan header mudah disalahartikan sebagai masalah body. Secara khusus, jangan menambah atau menghapus header beta berdasarkan model mana yang ditargetkan permintaan.

  Dua keluarga header dikecualikan dari kecocokan, demi kepentingan percobaan ulang:

  * **`server-side-fallback-*`:** percobaan ulang harus menghapus parameter `fallbacks`, dan menghapus header ini bersamanya tidak menyebabkan ketidakcocokan.
  * **`fallback-credit-*`:** pertahankan header ini pada kedua permintaan. Percobaan ulang memerlukannya untuk menukarkan token.

  <Note>
    Pada model yang menyertakan "context window" (jendela konteks) 1M token secara default, seperti Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Sonnet 5.5, dan Claude Sonnet 5, header beta `context-1m-2025-08-07` tidak berpengaruh. Agar kedua permintaan tetap identik, hilangkan header tersebut pada keduanya alih-alih mengirimkannya pada satu permintaan dan tidak pada yang lain.
  </Note>
</Accordion>

<Accordion title="Ketika fallback_has_prefill_claim tidak ada">
  Field ini tidak memiliki nilai hanya ketika token juga tidak memiliki nilai, sehingga selama Anda memegang token, field ini memiliki nilai, kecuali di Amazon Bedrock, Google Cloud, dan Microsoft Foundry, di mana field ini masih bisa tidak ada selama dukungan mereka untuk field tersebut diluncurkan. Dalam kasus itu, perlakukan bentuk percobaan ulang sebagai tidak diketahui alih-alih sebagai `false` (python: `False`). Coba bentuk pesan-asisten-yang-ditambahkan terlebih dahulu, dan andalkan penanganan penolakan di [Ketika percobaan ulang ditolak](https://platform.claude.com/docs/id/build-with-claude/fallback-credit#when-a-retry-is-rejected), yang beralih ke body tanpa perubahan.
</Accordion>

<Accordion title="Menyalin content dari respons yang ditolak">
  Ketika token penolakan mendukung bentuk kelanjutan, `content` respons hanya membawa output model itu sendiri, dan penjelasan penolakan disampaikan di `stop_details.explanation`. Oleh karena itu, Anda dapat menyalin `content` ke dalam pesan asisten yang ditambahkan apa adanya.

  Dua penyesuaian mungkin masih diperlukan sebelum mengirim:

  * Jika blok terakhir yang Anda kirim adalah blok `text`, hapus whitespace di akhirnya.
  * Hilangkan blok `tool_use` sisi klien apa pun yang tidak memiliki `tool_result` yang cocok.

  Jika content yang disalin menyertakan blok `fallback` dari [fallback sisi server](https://platform.claude.com/docs/id/build-with-claude/refusals-and-fallback#server-side-fallback) sebelumnya, pertahankan blok tersebut tepat di tempat ia muncul. Blok ini diterima pada permintaan apa pun tanpa header beta. API menggunakan posisinya untuk memvalidasi blok thinking di sekitarnya, sehingga permintaan yang menyalin blok thinking dari kedua sisi batas tersebut akan ditolak jika blok itu dihilangkan atau dipindahkan.
</Accordion>

<Accordion title="Cakupan dan masa berlaku token">
  Token hanya dapat ditukarkan dari organisasi dan workspace yang menerima penolakan, termasuk di Microsoft Foundry. Di Amazon Bedrock dan Google Cloud, yang tidak memiliki workspace, token terikat pada identitas pemanggil platform sebagai gantinya.

  Token kedaluwarsa lima menit setelah penolakan. Setelah itu, kirim percobaan ulang tanpanya. Token juga bersifat stateless: server tidak menyimpan apa pun tentangnya, dan tidak ada endpoint untuk memeriksa atau mencabutnya.
</Accordion>

<Accordion title="Ketika token tidak dapat ditukarkan dengan bentuk mana pun">
  Ketika penolakan tiba setelah alat server sudah dieksekusi dalam permintaan, token hanya dapat ditukarkan dengan melanjutkan respons parsial. Pembatasan itulah yang mencegah panggilan alat yang telah selesai berjalan, dan ditagih, lagi.

  Oleh karena itu, satu kombinasi dapat membuat token tidak dapat ditukarkan dengan bentuk mana pun, ketika kedua hal berikut benar:

  * Permintaan menggunakan `output_config.format` atau `tool_choice` yang memaksa penggunaan alat. Salah satunya mengesampingkan bentuk pesan-asisten-yang-ditambahkan.
  * Penolakan tiba setelah alat server dieksekusi. Itu mengesampingkan body tanpa perubahan.

  Jika percobaan ulang body-tanpa-perubahan ditolak dengan error 400 yang menyatakan token harus ditukarkan dengan melanjutkan respons parsial, buang token tersebut. Percobaan ulang tanpanya berhasil, tetapi akan menjalankan ulang dan menagih ulang alat server yang telah selesai. Tampilkan biaya atau error tersebut kepada pemanggil Anda daripada mencoba ulang secara diam-diam.
</Accordion>

## Langkah selanjutnya

<CardGroup>
  <Card title="Penolakan dan fallback" icon="shield" href="https://platform.claude.com/docs/id/build-with-claude/refusals-and-fallback">
    Deteksi penolakan dan pilih antara fallback sisi server, middleware SDK, dan percobaan ulang manual.
  </Card>

  <Card title="Caching prompt" icon="bolt" href="https://platform.claude.com/docs/id/build-with-claude/prompt-caching">
    Bagaimana pembacaan cache dan penulisan cache ditagih.
  </Card>

  <Card title="Alasan berhenti dan fallback" icon="code" href="https://platform.claude.com/docs/id/build-with-claude/handling-stop-reasons">
    Setiap nilai `stop_reason` dan cara menanganinya.
  </Card>

  <Card title="Middleware SDK" icon="settings" href="https://platform.claude.com/docs/id/cli-sdks-libraries/middleware">
    Helper SDK yang menerapkan kredit fallback secara otomatis.
  </Card>
</CardGroup>
