---
title: Refus et repli
url: https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback
description: Comment les modèles Claude Fable, les modèles Claude Opus, Claude Sonnet 5.5 et Claude Haiku 5.5 renvoient des refus de classificateur et comment relancer les requêtes refusées sur un modèle de repli.
---

Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5 et Claude Haiku 5.5 incluent des « safety classifiers » (classificateurs de sécurité) qui peuvent décliner une requête. Lorsque cela se produit, vous recevez une réponse normale, et non une erreur, avec `stop_reason: "refusal"`. Son champ `stop_details.category` nomme le domaine de politique concerné (voir [À quoi ressemble un refus](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#refusal-response)). Vous pouvez généralement obtenir malgré tout une réponse en envoyant la même requête à un autre modèle Claude. Cette page vous montre comment reconnaître un refus et comment mettre en place cette nouvelle tentative.

Lisez cette page lorsque vous développez sur l'un de ces modèles et souhaitez que les requêtes déclinées basculent automatiquement vers un autre modèle. Elle s'applique également lorsque vous avez vu `"refusal"` dans une réponse et souhaitez savoir quoi faire ensuite.

Pages connexes :

* [Raisons d'arrêt et repli](https://platform.claude.com/docs/fr/build-with-claude/handling-stop-reasons) : la liste complète des valeurs de `stop_reason`.
* [Crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit) : comment éviter de payer deux fois le coût de la mise en cache des prompts lorsque vous construisez vous-même la nouvelle tentative.
* [Middleware SDK](https://platform.claude.com/docs/fr/cli-sdks-libraries/middleware) : l'utilitaire SDK qui encapsule tout cela.
* [Cookbook sur le repli et la facturation](https://platform.claude.com/cookbook/fable-5-fallback-billing-guide) : un exemple complet de bout en bout.

La configuration la plus simple, en bêta sur l'API Claude : définissez `fallbacks` sur `"default"`, et l'API relance une requête déclinée sur le « fallback model » (modèle de repli) qu'Anthropic recommande pour sa catégorie de refus. Pour les catégories sans repli recommandé, le refus est maintenu. Claude Haiku 5.5 ne dispose pas de repli côté serveur ; pour ce modèle, utilisez donc le [repli côté client avec le middleware SDK](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#client-side-fallback) ou [écrivez vous-même la nouvelle tentative](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#manual-retry).

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

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

  ```python Python
  client = Anthropic()

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

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

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

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

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

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

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

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

  fmt.Println(response.Model)
  ```

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

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

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

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

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

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

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

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

  puts response.model
  ```
</CodeGroup>

Les sections suivantes décrivent ce que contient une réponse de refus, quand utiliser le repli côté serveur ou côté client, et comment chacun est facturé.

## À quoi ressemble un refus

Un refus est une réponse HTTP 200 réussie avec `stop_reason: "refusal"` :

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

L'objet `stop_details` explique le refus :

* **`category` :** nomme le domaine de politique qui a déclenché le classificateur.
* **`explanation` :** une description lisible par un humain. Le texte n'est pas stable, affichez-le donc plutôt que de l'analyser.
* **`recommended_model` :** présent uniquement sur les requêtes qui définissent `fallbacks` ([repli côté serveur](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#server-side-fallback), bêta). Il nomme un modèle sur lequel relancer directement lorsque l'API a ignoré la tentative de repli (par exemple, le modèle de repli était soumis à une limite de débit), et vaut `null` sinon. C'est une indication, pas une garantie.
* `category` et `explanation` valent tous deux `null` lorsque le refus ne correspond à aucune catégorie nommée. Ce `null` est une valeur normale et permanente, pas un espace réservé.
* `stop_details` lui-même vaut `null` pour toute raison d'arrêt autre que `refusal`.

| `category`               | Signification                                                                                                                                                                                                                                                                                                                                                                                                                                    | Facturé avant toute sortie |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- |
| `"cyber"`                | La requête pourrait permettre un préjudice cyber, comme le développement de logiciels malveillants ou d'exploits. Un travail de cybersécurité bénin peut également déclencher cette catégorie.                                                                                                                                                                                                                                                   | Non                        |
| `"bio"`                  | La requête pourrait permettre un préjudice biologique, comme des méthodes de laboratoire dangereuses. Un travail bénéfique en sciences de la vie peut également déclencher cette catégorie.                                                                                                                                                                                                                                                      | Oui                        |
| `"frontier_llm"`         | La requête pourrait aider au développement de modèles d'IA concurrents, ce qui est restreint par les [conditions commerciales d'Anthropic](https://www.anthropic.com/legal/commercial-terms). Un travail bénin en apprentissage automatique peut également déclencher cette catégorie.                                                                                                                                                           | Oui                        |
| `"reasoning_extraction"` | La requête demande au modèle de reproduire son raisonnement interne dans le texte de la réponse. Pour obtenir le raisonnement sous une forme structurée, utilisez plutôt la [réflexion adaptative](https://platform.claude.com/docs/fr/build-with-claude/thinking). Voir [Conserver le raisonnement dans les blocs de réflexion](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks). | Oui                        |
| `"general_harms"`        | La requête relève d'un domaine de la politique d'utilisation en dehors des quatre catégories nommées. Un travail bénin peut également déclencher cette catégorie.                                                                                                                                                                                                                                                                                | Non                        |

Un refus peut arriver avant toute sortie, ou en cours de streaming après une sortie partielle. Dans les deux cas, considérez toute sortie partielle comme incomplète et écartez-la.

### Conserver le raisonnement dans les blocs de réflexion

Un refus `reasoning_extraction` provient généralement d'un prompt qui demande au modèle de placer sa réflexion ou son raisonnement dans la sortie, soit mot pour mot, soit dans un format fixe. Exemples courants :

* Une section `<thinking>`, `<reasoning>` ou de brouillon que le modèle remplit avant de répondre
* Un champ `reasoning`, `thinking` ou `trace` dans une sortie JSON ou dans une entrée d'outil
* Les notes privées du modèle, ou un journal continu de son raisonnement
* Un raisonnement demandé mot pour mot ou dans son intégralité

La formulation peut se trouver dans une invite système, une skill ou une description d'outil, vérifiez donc également ces éléments.

Vous pouvez toujours demander à Claude d'expliquer sa réponse. Demandez une brève explication, les éléments justifiant un résultat, ou un résumé des actions qu'il a effectuées. Pour voir comment le modèle a raisonné, définissez `display: "summarized"` et lisez les [blocs `thinking`](https://platform.claude.com/docs/fr/build-with-claude/thinking#summarized-thinking). Cette catégorie n'a pas de modèle de repli recommandé, modifiez donc le prompt plutôt que de relancer la requête. Si une requête reformulée est toujours déclinée, [contactez le support](https://support.claude.com/en/articles/9015913-how-to-get-support) et incluez l'[ID de requête](https://platform.claude.com/docs/fr/api/errors#request-id).

## Facturation des refus

Ces règles de facturation s'appliquent sur toutes les plateformes : l'API Claude, Amazon Bedrock, Claude Platform on AWS, Google Cloud et Microsoft Foundry.

**Refus avant toute sortie :** afin d'entraver les tentatives de contournement à grande échelle des garde-fous d'Anthropic, un refus survenant avant toute sortie est facturé lorsque son `stop_details.category` vaut `"bio"`, `"frontier_llm"` ou `"reasoning_extraction"`. Ce sont les catégories pour lesquelles, en septembre 2026, Anthropic mesure de faibles volumes de faux positifs. Ces refus sont facturés comme toute autre requête, aux tarifs du modèle qui l'a exécutée. Un refus avant toute sortie dans toute autre catégorie, ou avec une catégorie `null`, n'est pas facturé. Dans tous les cas, `content` est vide, et les décomptes de tokens apparaissent dans `usage`. La requête compte tout de même dans vos limites de débit.

**Refus en cours de streaming :** un refus en cours de streaming facture les tokens d'entrée et la sortie déjà diffusée aux tarifs normaux.

**Repli :** lorsque vous utilisez le repli, le refus qui l'a déclenché est facturé, en plus de la requête de repli, lorsqu'il est survenu en cours de streaming ou qu'il appartient à l'une des catégories facturées. Le [crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit) compense l'échec du cache de prompts pour la requête de repli, afin que vous ne payiez pas deux fois la mise en cache de la conversation. Un refus de Claude Haiku 5.5 ne donne droit à aucun crédit de repli ; un repli après un tel refus paie donc le coût complet de l'écriture du cache de prompts du modèle de repli. Pour savoir comment le repli côté serveur rend compte de chaque tentative, consultez [Facturation et limites de débit](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#billing-and-rate-limits).

Les catégories facturées peuvent évoluer à mesure qu'Anthropic continue de mesurer et d'affiner les taux de faux positifs de ses garde-fous. La colonne **Facturé avant toute sortie** du [tableau des catégories de refus](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#refusal-response) répertorie les catégories facturées.

## Choisir une approche de repli

Il existe trois façons de relancer une requête refusée sur un autre modèle. La bonne dépend de l'endroit où vous exécutez votre code et du niveau de contrôle dont vous avez besoin.

| Votre situation                                          | Utilisez                                                                                                                                                                                                                  | Pourquoi                                                                             |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| API Claude, configuration la plus simple                 | [Repli côté serveur](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#server-side-fallback)                                                                                                    | Une requête, une réponse. L'API gère la nouvelle tentative.                          |
| Toute plateforme, avec un SDK Anthropic                  | [Le middleware SDK](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#client-side-fallback)                                                                                                     | Configurez une fois sur le client. Les nouvelles tentatives se font automatiquement. |
| HTTP brut ou logique de nouvelle tentative personnalisée | [Une nouvelle tentative manuelle](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#manual-retry) avec [crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit) | Contrôle total. Le crédit de repli limite le coût.                                   |

Le repli côté serveur et le middleware SDK appliquent le crédit de repli pour vous. Vous n'avez besoin de la page [Crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit) que lorsque vous implémentez vous-même la nouvelle tentative. Claude Haiku 5.5 ne dispose ni du repli côté serveur ni du crédit de repli ; utilisez donc le middleware SDK ou une nouvelle tentative manuelle.

## Repli côté serveur

Le repli côté serveur relance une requête refusée au sein d'un seul appel API. Dans le mode par défaut, lorsque le modèle principal décline et que la catégorie de refus dispose d'un repli recommandé, l'API exécute la même requête sur le modèle qu'Anthropic recommande pour cette catégorie. Vous pouvez à la place [nommer jusqu'à trois modèles de repli de votre choix](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#naming-your-own-fallback-models). Dans les deux cas, vous recevez une seule réponse qui nomme le modèle ayant répondu, de sorte que votre utilisateur obtient une réponse en un seul aller-retour.

<Note>
  Le repli côté serveur est en bêta sur l'API Claude. Le paramètre `fallbacks` n'est pas pris en charge sur l'[API Message Batches](https://platform.claude.com/docs/fr/build-with-claude/batch-processing) (un élément de lot qui l'inclut est renvoyé sous forme de résultat en erreur) et n'est pas disponible sur Amazon Bedrock, Google Cloud ou Microsoft Foundry. Sur ces plateformes, utilisez plutôt le [repli côté client avec le middleware SDK](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#client-side-fallback). Claude Haiku 5.5 ne dispose pas de repli côté serveur : avec `fallbacks: "default"`, une requête déclinée reste déclinée, et une liste de modèles de repli renvoie une erreur 400.
</Note>

### Effectuer la requête

Définissez le paramètre `fallbacks` sur la chaîne `"default"` et envoyez l'en-tête bêta `server-side-fallback-2026-07-01`. L'API applique alors le routage par défaut défini côté serveur pour le modèle demandé, qui sélectionne un modèle de repli recommandé en fonction de la catégorie de refus signalée par le classificateur, de sorte que les requêtes refusées sont servies sans que vous ayez à maintenir une liste de modèles au fil de l'évolution des recommandations.

Le routage par défaut ne déclenche jamais le [rejet préalable pour image surdimensionnée](https://platform.claude.com/docs/fr/build-with-claude/vision-coordinates#oversized-image-error) pour des modèles que vous n'avez pas choisis : un modèle routé qui redimensionnerait une image marquée `"oversized_image": "error"` est plutôt retiré du routage, de sorte qu'une image marquée n'est jamais servie redimensionnée.

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: server-side-fallback-2026-07-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-fable-5",
      "max_tokens": 1024,
      "fallbacks": "default",
      "messages": [{"role": "user", "content": "Hello, Claude"}]
    }' |
    jq -c '{
      stop_reason,
      model,
      # A fallback_message entry in usage.iterations means a fallback model ran;
      # pair it with stop_reason to confirm the fallback served the response.
      served_by_fallback: (
        any(.usage.iterations[]?; .type == "fallback_message")
        and .stop_reason != "refusal"
      )
    }'
  ```

  ```bash CLI
  ant beta:messages create \
    --model claude-fable-5 \
    --max-tokens 1024 \
    --message '{"role":"user","content":"Hello, Claude"}' \
    --fallbacks default \
    --beta server-side-fallback-2026-07-01 \
    --format json |
    jq -c '{
      stop_reason,
      model,
      # A fallback_message entry in usage.iterations means a fallback model ran;
      # pair it with stop_reason to confirm the fallback served the response.
      served_by_fallback: (
        any(.usage.iterations[]?; .type == "fallback_message")
        and .stop_reason != "refusal"
      )
    }'
  ```

  ```python Python
  client = Anthropic()

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

  # Une entrée fallback_message dans usage.iterations signifie qu'un modèle de repli a été exécuté ;
  # associez-la à stop_reason pour confirmer que le repli a fourni la réponse.
  fallback_ran = any(
      iteration.type == "fallback_message"
      for iteration in response.usage.iterations or []
  )
  served_by_fallback = fallback_ran and response.stop_reason != "refusal"

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

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

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

  // Une entrée fallback_message dans usage.iterations signifie qu'un modèle de repli a été exécuté ;
  // associez-la à stop_reason pour confirmer que le repli a fourni la réponse.
  const { stop_reason, model, usage } = response;
  const servedByFallback =
    (usage.iterations ?? []).some((entry) => entry.type === "fallback_message") &&
    stop_reason !== "refusal";

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

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

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

  // Une entrée fallback_message dans usage.iterations signifie qu'un modèle de repli a été exécuté ;
  // associez-la à stop_reason pour confirmer que le repli a servi la réponse.
  bool fallbackRan = (response.Usage.Iterations ?? []).Any(iteration =>
      iteration.TryPickBetaFallbackMessageIterationUsage(out _)
  );
  bool servedByFallback =
      fallbackRan && response.StopReason?.Value() != BetaStopReason.Refusal;

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

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

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

  // Une entrée fallback_message dans usage.iterations signifie qu'un modèle de repli a été exécuté ;
  // associez-la à stop_reason pour confirmer que le repli a fourni la réponse.
  fallbackRan := slices.ContainsFunc(
  	response.Usage.Iterations,
  	func(iteration anthropic.BetaIterationsUsageItemUnion) bool {
  		_, isFallback := iteration.AsAny().(anthropic.BetaFallbackMessageIterationUsage)
  		return isFallback
  	},
  )
  servedByFallback := fallbackRan && response.StopReason != anthropic.BetaStopReasonRefusal

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

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

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

  // Une entrée d'usage fallback_message signifie qu'un modèle de repli a produit la
  // réponse ; un stop_reason de refus signifie qu'aucun modèle ne l'a servie.
  List<BetaUsage.Iteration> iterations =
      response.usage().iterations().orElse(List.of());
  boolean servedByFallback =
      iterations.stream().anyMatch(BetaUsage.Iteration::isFallbackMessage)
          && response.stopReason().filter(BetaStopReason.REFUSAL::equals).isEmpty();

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

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

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

  // Une entrée fallback_message dans usage.iterations signifie qu'un modèle de repli a été exécuté ;
  // associez-la à stop_reason pour confirmer que le repli a fourni la réponse.
  $iterations = $response->usage->iterations ?? [];
  $servedByFallback = array_any($iterations, fn($entry) => $entry->type === 'fallback_message')
      && $response->stopReason !== 'refusal';

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

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

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

  # Une entrée fallback_message dans usage.iterations signifie qu'un modèle de repli a été exécuté ;
  # associez-la à stop_reason pour confirmer que le repli a servi la réponse.
  iterations = response.usage.iterations || []
  served_by_fallback = iterations.any? { it.type == :fallback_message } &&
    response.stop_reason != :refusal

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

Anthropic définit des garde-fous pour chaque modèle individuellement et pour chaque catégorie de politique, en fonction des capacités du modèle : selon la catégorie, une requête signalée peut se replier sur un modèle moins performant ou être déclinée. Le mode `"default"` encode pour vous ces recommandations par modèle et par catégorie, de sorte qu'une requête refusée est relancée sur le modèle qu'Anthropic recommande pour cette catégorie. Les replis sont visibles dans tous les cas : la réponse nomme le modèle qui l'a servie, et le bloc de contenu `fallback` marque le passage de relais.

Le routage est appliqué côté serveur et n'est pas publié par modèle sur l'[API Models](https://platform.claude.com/docs/fr/api/models/list). Pour voir quel modèle a servi une requête refusée, vérifiez le champ `model` de premier niveau de la réponse et recherchez une entrée `fallback_message` dans `usage.iterations`, comme le font les exemples de cette page.

Seul un refus du classificateur de sécurité déclenche le repli. Une limite de débit, une surcharge ou une erreur serveur sur le modèle demandé vous est renvoyée telle quelle.

<Note>
  L'en-tête bêta doit porter exactement la date `2026-07-01`, qui prend en charge à la fois `"default"` et la forme à liste explicite, ou `2026-06-01`, qui n'accepte que la forme à liste explicite. Avec toute autre valeur `server-side-fallback-*`, le paramètre `fallbacks` est rejeté avec une erreur 400. Si vous avez développé avec une préversion antérieure de cette fonctionnalité, mettez à jour ensemble l'en-tête bêta et les formats de requête et de réponse vers ceux de cette page.
</Note>

### Nommer vos propres modèles de repli

Au lieu du routage par défaut, vous pouvez définir `fallbacks` sur une liste de trois modèles au maximum. Lorsque le modèle demandé décline, l'API exécute le modèle suivant de la chaîne sur la même requête. Utilisez cette forme lorsque vous souhaitez contrôler exactement quels modèles servent les requêtes refusées, par exemple pour épingler un modèle que votre application a qualifié.

Les modèles de repli nommés comptent pour la [vérification d'image surdimensionnée](https://platform.claude.com/docs/fr/build-with-claude/vision-coordinates#oversized-image-error) : une requête dont le bloc image définit `"oversized_image": "error"` est vérifiée au préalable par rapport au modèle demandé et à chaque repli nommé, est rejetée si l'un d'eux redimensionnerait cette image, et la cible de redimensionnement indiquée dans le rejet convient à tous.

Les lignes surlignées sont la seule différence avec la requête à routage par défaut.

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

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

  ```python Python
  client = Anthropic()

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

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

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

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

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

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

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

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

  fmt.Println(response.Model)
  ```

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

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

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

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

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

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

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

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

  puts response.model
  ```
</CodeGroup>

Quelques règles s'appliquent à la liste `fallbacks` :

* Les entrées sont essayées dans l'ordre. Chacune doit être distincte des autres entrées et du modèle demandé.
* Chaque entrée doit faire partie des cibles autorisées du modèle demandé. Lorsque l'en-tête bêta est défini, cette liste est publiée sous la forme `allowed_fallback_models` dans l'entrée du modèle de l'[API Models](https://platform.claude.com/docs/fr/api/models/list).
* Chaque entrée désigne un `model` et peut remplacer `max_tokens`, `thinking`, `output_config` et `speed` pour cette tentative uniquement.
* La requête doit être valide en tant que requête directe vers chaque modèle nommé. Si un modèle de repli ne prend pas en charge une fonctionnalité utilisée par la requête, l'API rejette la requête en amont. Avec l'en-tête `2026-07-01`, `thinking: {"type": "between_tools"}` fait exception : lorsqu'une requête Claude Sonnet 5.5 bascule vers Claude Sonnet 5, la tentative de repli s'exécute avec `thinking: {"type": "disabled"}` et `display: "omitted"`. Avec `2026-06-01`, définissez `thinking` sur l'entrée Claude Sonnet 5, sinon la requête est rejetée en amont.
* Comme pour le mode par défaut, seul un refus d'un classificateur de sécurité déclenche le repli. Une limite de débit, une surcharge ou une erreur serveur sur le modèle demandé vous est renvoyée telle quelle.
* Si un modèle de repli a atteint sa limite de débit ou est surchargé, la tentative de repli n'est pas effectuée et le refus précédent est renvoyé à la place. Le champ `stop_details.recommended_model` du refus indique alors un modèle à relancer directement. Dimensionnez les limites de débit du modèle de repli en fonction du volume de refus attendu, sinon les replis se transforment en refus sous charge.

La réponse a la même forme dans les deux modes : le modèle qui a servi le tour apparaît dans le champ `model` de premier niveau, un bloc de contenu `fallback` marque le passage de relais, et `usage.iterations` enregistre chaque tentative.

### Ce que contient la réponse

La réponse ressemble à n'importe quel autre message, avec deux ajouts :

* Le champ `model` de premier niveau indique le modèle qui a produit le message renvoyé, qu'il s'agisse du modèle demandé ou d'un repli.

* Un bloc de contenu `fallback` marque chaque point dans `content` où la sortie d'un modèle cède la place au suivant : `{"type": "fallback", "from": {"model": ...}, "to": {"model": ...}}`.

  * `from.model` reprend la chaîne de modèle que vous avez envoyée lorsque l'étape qui décline est le modèle demandé.
  * `to.model` est toujours l'identifiant résolu du modèle qui poursuit.

Lors d'un refus avant toute sortie, le bloc `fallback` est le premier bloc de contenu. Par exemple, lorsque le routage par défaut sélectionne Claude Opus 4.8 pour la catégorie du refus :

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

Le tableau `usage.iterations` enregistre chaque tentative. Un modèle qui a décliné apparaît comme une entrée `message` ordinaire, et le modèle qui a servi le tour apparaît comme une entrée `fallback_message`. Si tous les modèles de la chaîne déclinent, la réponse est le refus du dernier modèle, avec une entrée `message` pour chaque étape précédente et une entrée `fallback_message` pour la dernière.

Le [routage persistant](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#sticky-routing) peut envoyer un tour ultérieur directement au modèle de repli. Un tel tour ne porte aucun bloc de contenu `fallback`, car aucun modèle n'a décliné ce tour. Identifiez-le par l'entrée `fallback_message` dans `usage.iterations`, l'absence d'entrée `message` pour le modèle demandé, et le champ `model` de la réponse.

### Poursuivre la conversation

Au tour suivant, renvoyez le contenu de l'assistant tel que vous l'avez reçu. Après un repli en cours de sortie, `content` peut inclure des types de blocs que le modèle ayant décliné a produits avant le passage de relais. Le tableau suivant indique lesquels conserver et lesquels supprimer lorsque vous renvoyez le tour.

| Type de bloc                                                                         | Au tour suivant                                                                                                                                                                                                                                                     |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fallback`                                                                           | Conservez-le exactement là où il apparaissait. L'API utilise sa position pour valider les blocs de réflexion qui l'entourent, de sorte qu'une requête qui renvoie des blocs de réflexion des deux côtés de la frontière est rejetée si le bloc est omis ou déplacé. |
| `text`                                                                               | Conserver.                                                                                                                                                                                                                                                          |
| Tout bloc après le dernier bloc `fallback`                                           | Conserver.                                                                                                                                                                                                                                                          |
| `thinking`, `redacted_thinking` ou `connector_text` avant le dernier bloc `fallback` | Supprimer.                                                                                                                                                                                                                                                          |
| `tool_use` côté client avant le dernier bloc `fallback`                              | Supprimer.                                                                                                                                                                                                                                                          |
| `server_tool_use` avant le dernier bloc `fallback`                                   | Conserver lorsqu'il est associé à son résultat. Supprimer lorsqu'il n'a pas de résultat correspondant.                                                                                                                                                              |

<Note>
  Un bloc `connector_text` porte un texte de narration que certaines réponses utilisant des outils incluent entre les appels d'outils.
</Note>

### Streaming

Sur une requête en streaming, la nouvelle tentative se produit sur le même flux, et rien de ce que vous avez déjà reçu n'est invalidé. Ce que vous voyez dépend du moment où le refus se produit.

**Lorsque le refus se produit avant toute sortie :**

* `message_start` nomme le modèle de repli, et le bloc `fallback` est le premier bloc de contenu.
* Comme `message_start` attend que la tentative de repli démarre, le délai jusqu'au premier octet inclut la tentative déclinée.

**Lorsque le refus se produit en cours de sortie :**

* Le bloc de contenu ouvert se ferme, et le bloc `fallback` (une paire ordinaire `content_block_start` et `content_block_stop` sans deltas) marque la frontière.
* Le modèle de repli poursuit à partir de la sortie partielle. Seuls les blocs `text` de la sortie partielle sont transmis au modèle de repli comme contexte. Les autres types de blocs restent dans `content`.
* `message_start` a déjà nommé le modèle demandé, lisez donc le modèle servant dans le champ `to.model` du bloc `fallback` et dans l'entrée `fallback_message` du `usage.iterations` du dernier `message_delta`.

### Réponses sans streaming

Sur une requête sans streaming, un refus en cours de sortie se comporte différemment : la réponse omet la sortie partielle du modèle ayant décliné, et le modèle de repli répond à partir de zéro. Le résultat ressemble à un refus avant toute sortie, avec le bloc `fallback` en premier. La tentative déclinée et ses tokens de sortie apparaissent toujours dans `usage.iterations`.

<Note>
  **Refus pendant l'utilisation d'outils :** le travail d'outil terminé ne bloque pas le repli. Lorsqu'un refus se déclenche après que des outils serveur (par exemple, la recherche web ou l'exécution de code) ont fini de s'exécuter au sein d'une requête, la tentative de repli se poursuit : les résultats d'outils terminés sont reportés, et le modèle de repli peut continuer à invoquer des outils serveur. Le seul cas qui ne relance pas est un refus en streaming qui se déclenche alors qu'un bloc d'utilisation d'outil de n'importe quel type (un outil client, un outil serveur ou un appel d'outil MCP) est encore ouvert sur le flux : ce refus est renvoyé directement, et si l'en-tête `fallback-credit-2026-07-01` est défini, il porte tout de même un jeton de crédit utilisable en poursuivant la réponse partielle. Les requêtes sans streaming ne sont pas affectées ; l'API efface le travail partiel et relance avant de répondre.
</Note>

### Facturation et limites de débit

Chaque tentative suit les règles de la section [Facturation des refus](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#how-refusals-are-billed), aux tarifs du modèle qui l'a exécutée. Une tentative qui a décliné avant de produire la moindre sortie n'est facturée que si sa catégorie de refus est facturée, et ses tokens sont indiqués dans son entrée `usage.iterations` dans tous les cas. Chaque tentative ayant produit une sortie, y compris une tentative qui a décliné en cours de réponse, est facturée séparément. Le tableau `usage.iterations` est le relevé par tentative de ce qui vous est facturé. Les décomptes `usage` de premier niveau décrivent uniquement la tentative qui a produit le message renvoyé. Les tokens de modèles différents ne sont jamais additionnés dans un même champ.

Chaque tentative exécutée, y compris une tentative qui a décliné, compte dans les limites de débit de son propre modèle.

### Routage persistant

Après qu'une conversation s'est repliée, l'API enregistre quel modèle l'a servie. Les requêtes ultérieures pour cette conversation qui incluent `fallbacks` vont directement à ce modèle de repli, sans exécuter le modèle demandé. Cela évite de payer à chaque tour une tentative qui serait de manière prévisible déclinée à nouveau.

Quelques propriétés de la décision de routage :

* Elle est conservée pendant environ 1 heure et est limitée à votre organisation.
* Elle est stockée sous forme de hachage de contenu du préfixe de conversation plus le modèle qui l'a servie. Le contenu des messages lui-même n'est pas stocké.
* Elle est fournie au mieux, votre code doit donc gérer le fait que le modèle demandé soit réessayé à tout moment.

Le routage persistant s'applique aux requêtes avec et sans streaming. Sur une requête en streaming, la décision de routage est prise avant l'ouverture du flux, de sorte que le champ `model` de l'événement `message_start` porte déjà l'identifiant du modèle de repli.

## Repli côté client avec le middleware SDK

Le SDK inclut un middleware de repli en cas de refus. Vous le configurez une seule fois sur le client avec votre liste de modèles de repli. Les appels effectués via `client.beta.messages` (csharp, go: `client.Beta.Messages`; java: `client.beta().messages()`; php: `$client->beta->messages`) relancent alors automatiquement les requêtes refusées, sur n'importe quelle plateforme. Le middleware envoie également l'en-tête bêta `fallback-credit-2026-07-01` sur chaque requête qu'il traite, de sorte que les nouvelles tentatives sont retarifées sans configuration par requête. Un refus de Claude Haiku 5.5 ne donne droit à aucun crédit de repli ; une nouvelle tentative après un tel refus paie donc le coût complet de l'écriture du cache de prompts du modèle de repli.

### Configuration

Passez `BetaRefusalFallbackMiddleware` (typescript: `betaRefusalFallbackMiddleware`; go: `betafallback.BetaRefusalFallbackMiddleware`; csharp: `BetaRefusalFallbackHandler`; java: `BetaRefusalFallbackInterceptor`; php: `RefusalFallbackMiddleware`) au constructeur du client, et partagez une seule instance de `BetaFallbackState` entre les requêtes d'une conversation.

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    Le middleware de repli en cas de refus fait partie du SDK Claude ; une requête cURL ne peut donc pas l'utiliser. Pour effectuer un repli au sein d'une seule requête, utilisez le [repli côté serveur](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#server-side-fallback). Pour écrire vous-même la nouvelle tentative, consultez [Crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit).
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    La CLI `ant` n'inclut pas le middleware de repli en cas de refus. Pour effectuer un repli au sein d'une seule requête, utilisez le [repli côté serveur](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#server-side-fallback). Pour écrire vous-même la nouvelle tentative, consultez [Crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit).
  </CodeGroupItem>

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

  # En cas de refus, le middleware réessaie sur le modèle de repli indiqué et
  # envoie automatiquement l'en-tête bêta fallback-credit à chaque requête qu'il traite.
  client = Anthropic(
      middleware=[BetaRefusalFallbackMiddleware([{"model": "claude-opus-4-8"}])],
  )

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

  # Streaming : en cas de refus, le middleware réessaie sur le modèle de repli et
  # raccorde ses événements au flux ouvert.
  with (
      state,
      client.beta.messages.stream(
          max_tokens=1024,
          model="claude-fable-5",
          messages=[{"role": "user", "content": "Hello, Claude"}],
      ) as stream,
  ):
      for text in stream.text_stream:
          print(text, end="", flush=True)
      final_message = stream.get_final_message()
  print(f"\nserved by: {final_message.model}")

  # Sans streaming : réutiliser l'état maintient la conversation épinglée.
  with state:
      message = client.beta.messages.create(
          max_tokens=1024,
          model="claude-fable-5",
          messages=[{"role": "user", "content": "Hello, Claude"}],
      )
  print(f"served by: {message.model}")
  ```

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

  // En cas de refus, le middleware réessaie sur le modèle de repli indiqué et
  // envoie automatiquement l'en-tête bêta fallback-credit à chaque requête qu'il traite.
  const client = new Anthropic({
    middleware: [betaRefusalFallbackMiddleware([{ model: "claude-opus-4-8" }])]
  });

  // Partagez un seul état sur toute la conversation afin que les requêtes suivantes restent
  // épinglées au modèle qui a accepté.
  const fallbackState = new BetaFallbackState();

  // Streaming : en cas de refus, le middleware réessaie sur le modèle de repli et
  // insère ses événements dans le flux ouvert.
  const stream = client.beta.messages
    .stream(
      {
        max_tokens: 1024,
        model: "claude-fable-5",
        messages: [{ role: "user", content: "Hello, Claude" }]
      },
      { fallbackState }
    )
    .on("text", (text) => process.stdout.write(text));

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

  // Sans streaming : réutiliser l'état maintient la conversation épinglée.
  const message = await client.beta.messages.create(
    {
      max_tokens: 1024,
      model: "claude-fable-5",
      messages: [{ role: "user", content: "Hello, Claude" }]
    },
    { fallbackState }
  );
  console.log("served by:", message.model);
  ```

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

  // En cas de refus, le gestionnaire réessaie sur le modèle de repli indiqué et
  // envoie automatiquement l'en-tête bêta fallback-credit à chaque requête qu'il traite.
  AnthropicClient client = new()
  {
      Handlers =
      [
          new BetaRefusalFallbackHandler { Fallbacks = [new(Messages::Model.ClaudeOpus4_8)] },
      ],
  };

  // Épingle les requêtes de suivi partageant cet état au modèle qui a accepté.
  BetaFallbackState fallbackState = BetaFallbackState.Create();

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

  // Streaming : si le flux se termine par un refus, le gestionnaire raccorde les événements
  // du modèle de repli au flux encore ouvert.
  BetaMessageContentAggregator aggregator = new();
  using (fallbackState.Use())
  {
      var responseUpdates = client.Beta.Messages.CreateStreaming(parameters);
      await foreach (BetaRawMessageStreamEvent rawEvent in responseUpdates.CollectAsync(aggregator))
      {
          if (
              rawEvent.TryPickContentBlockDelta(out var deltaEvent)
              && deltaEvent.Delta.TryPickText(out var textDelta)
          )
          {
              Console.Write(textDelta.Text);
          }
      }
  }
  BetaMessage streamedMessage = aggregator.Message();
  Console.WriteLine($"\nserved by: {streamedMessage.Model.Raw()}");

  // Hors streaming : réutiliser l'état maintient la conversation épinglée au modèle qui a accepté.
  using (fallbackState.Use())
  {
      BetaMessage message = await client.Beta.Messages.Create(parameters);
      Console.WriteLine($"served by: {message.Model.Raw()}");
  }
  ```

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

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

  	// Le middleware réessaie une requête refusée sur chaque modèle de repli
  	// tour à tour, et inscrit automatiquement les requêtes à la bêta fallback-credit.
  	client := anthropic.NewClient(
  		option.WithMiddleware(betafallback.BetaRefusalFallbackMiddleware(
  			[]anthropic.BetaFallbackParam{{Model: anthropic.ModelClaudeOpus4_8}},
  		)),
  	)

  	// Un état par conversation : les requêtes qui le partagent restent liées au
  	// modèle qui a accepté, donc un suivi ne réinterroge jamais un modèle ayant refusé.
  	state := &betafallback.BetaFallbackState{}
  	conversation := betafallback.WithBetaFallbackState(state)

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

  	// Streaming : en cas de refus, le middleware réessaie sur place, en greffant les
  	// événements du modèle de repli sur le flux ouvert en un seul message continu.
  	stream := client.Beta.Messages.NewStreaming(ctx, params, conversation)
  	defer stream.Close()
  	var streamed anthropic.BetaMessage
  	for stream.Next() {
  		event := stream.Current()
  		if err := streamed.Accumulate(event); err != nil {
  			panic(err)
  		}
  		switch eventVariant := event.AsAny().(type) {
  		case anthropic.BetaRawContentBlockDeltaEvent:
  			if textDelta, ok := eventVariant.Delta.AsAny().(anthropic.BetaTextDelta); ok {
  				fmt.Print(textDelta.Text)
  			}
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  	fmt.Println("\nserved by:", streamed.Model)

  	// Sans streaming : l'état partagé lie ce suivi au modèle qui
  	// a servi le tour en streaming.
  	message, err := client.Beta.Messages.New(ctx, params, conversation)
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println("served by:", message.Model)
  }
  ```

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

  void main() {
      // L'intercepteur relance les requêtes refusées sur le modèle de repli. Il ajoute automatiquement
      // l'en-tête bêta fallback-credit à chaque requête qu'il traite.
      AnthropicClient client = AnthropicOkHttpClient.builder()
          .fromEnv()
          .addInterceptor(BetaRefusalFallbackInterceptor.builder()
              .addFallback(Model.CLAUDE_OPUS_4_8)
              .build())
          .build();

      // Partagez un même état entre les requêtes pour que les suivis restent épinglés au modèle qui a accepté.
      BetaFallbackState state = BetaFallbackState.create();

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

      // Streaming : en cas de refus, les événements du modèle de repli sont greffés sur le flux ouvert.
      BetaMessageAccumulator accumulator = BetaMessageAccumulator.create();
      try (StreamResponse<BetaRawMessageStreamEvent> streamResponse = client.beta()
              .messages()
              .createStreaming(params, RequestOptions.builder().fallbackState(state).build())) {
          streamResponse.stream()
              .peek(accumulator::accumulate)
              .forEach(event -> event.contentBlockDelta()
                  .flatMap(deltaEvent -> deltaEvent.delta().text())
                  .ifPresent(textDelta -> IO.print(textDelta.text())));
      }
      IO.println("\nserved by: " + accumulator.message().model().asString());

      // Sans streaming : réutiliser le même état maintient la conversation épinglée.
      BetaMessage message = client.beta()
          .messages()
          .create(params, RequestOptions.builder().fallbackState(state).build());
      IO.println("served by: " + message.model().asString());
  }
  ```

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

  // Configurez la chaîne de repli une seule fois. En cas de refus, le middleware relance la
  // requête le long de la chaîne et envoie l'en-tête bêta fallback-credit pour vous.
  $client = new Client(
      requestOptions: [
          'middleware' => [new RefusalFallbackMiddleware([['model' => 'claude-opus-4-8']])],
      ],
  );

  // Partagez un seul état sur toute la conversation afin que les requêtes suivantes restent
  // épinglées au modèle qui a accepté.
  $state = new BetaFallbackState();

  // Streaming : en cas de refus, le middleware greffe les événements du modèle de repli
  // sur le flux encore ouvert. Le modèle de l'accumulateur est le modèle qui sert la requête.
  $stream = $client->beta->messages->createStream(
      model: 'claude-fable-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      requestOptions: ['fallbackState' => $state],
  );
  $accumulator = MessageAccumulator::forBetaMessages();
  foreach ($stream as $event) {
      $accumulator->accumulate($event);
      if ($event instanceof BetaRawContentBlockDeltaEvent
          && $event->delta instanceof BetaTextDelta) {
          echo $event->delta->text;
      }
  }
  echo "\nserved by: {$accumulator->message()->model}\n";

  // Sans streaming : même middleware. Réutiliser l'état maintient la conversation
  // épinglée au modèle qui a accepté.
  $message = $client->beta->messages->create(
      model: 'claude-fable-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      requestOptions: ['fallbackState' => $state],
  );
  echo "served by: {$message->model}\n";
  ```

  ```ruby Ruby
  # En cas de refus, le middleware relance la requête en descendant la chaîne de repli.
  # Il envoie l'en-tête bêta fallback-credit sur chaque requête qu'il traite.
  client = Anthropic::Client.new(
    middleware: [Anthropic::BetaRefusalFallbackMiddleware.new([{model: "claude-opus-4-8"}])]
  )

  # Partagez un seul état sur toute la conversation afin que les requêtes suivantes restent
  # épinglées au modèle qui a accepté.
  state = Anthropic::BetaFallbackState.new

  # Streaming : en cas de refus, le middleware greffe les événements du modèle de repli
  # sur le flux encore ouvert.
  stream = client.beta.messages.stream(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    request_options: {fallback_state: state}
  )
  stream.text.each { print it }
  puts "\nserved by: #{stream.accumulated_message.model}"

  # Sans streaming : réutiliser l'état maintient la conversation épinglée au modèle qui a accepté.
  message = client.beta.messages.create(
    model: "claude-fable-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    request_options: {fallback_state: state}
  )
  puts "served by: #{message.model}"
  ```
</CodeGroup>

### Comportement

* Les nouvelles tentatives parcourent votre liste de replis dans l'ordre. Un modèle de repli qui refuse lui-même transmet la requête à l'entrée suivante.
* Lorsque tous les modèles de la liste ont décliné, le middleware renvoie le refus final (la réponse de refus du dernier modèle) plutôt que de lever une erreur.
* Les blocs de réflexion de Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, Claude Haiku 5.5 ou Claude Fable 5 sont transmis sans modification. Chaque nouvelle tentative renvoie le corps de votre requête d'origine, et les seuls blocs que le middleware retire de l'historique de conversation lors des requêtes ultérieures sont les blocs de frontière `fallback` qu'il a lui-même ajoutés. Le modèle de repli ne peut pas lire les blocs de Claude Fable 5.1, que seuls [Claude Fable 5.1 et Claude Mythos 5.1 lisent](https://platform.claude.com/docs/fr/build-with-claude/thinking#preserved-for-model) ; l'API les supprime donc. L'API supprime également les blocs de Claude Opus 5.5 pour tous les modèles de repli sauf Claude Fable 5.1 et Claude Mythos 5.1 (voir [Changer de modèle en cours de conversation](https://platform.claude.com/docs/fr/build-with-claude/preserved-thinking#switching-models)). Elle supprime aussi les blocs de Claude Sonnet 5.5, pour tous les modèles de repli sauf Claude Opus 5.5 sur l'API Claude et Google Cloud. Claude Opus 5.5 et Claude Sonnet 5.5 lisent les blocs de Claude Haiku 5.5 sur l'API Claude et Google Cloud, et l'API les supprime pour un modèle de repli qui ne peut pas les lire.
* Les réponses servies via le middleware incluent un bloc de contenu `fallback` à chaque limite entre modèles, comme les réponses du repli côté serveur. Le middleware gère ces blocs pour vous lors des requêtes ultérieures.
* Le modèle qui a accepté est enregistré dans `BetaFallbackState`, de sorte que les requêtes suivantes qui partagent cet état restent épinglées sur lui plutôt que de solliciter à nouveau un modèle qui a refusé.

<Note>
  Le middleware et le paramètre `fallbacks` côté serveur font le même travail. Configurez l'un ou l'autre, jamais les deux sur la même requête. Pour envoyer une requête `fallbacks` côté serveur depuis une application qui installe le middleware, utilisez une instance de client distincte sans celui-ci.
</Note>

## Écrire vous-même la nouvelle tentative

En HTTP brut ou avec une logique de nouvelle tentative personnalisée, implémentez le modèle que le middleware encapsule :

<Steps>
  <Step title="Détecter le refus">
    Vérifiez si la réponse contient `stop_reason: "refusal"`.
  </Step>

  <Step title="Renvoyer sur un modèle de repli">
    Envoyez la même requête avec `model` défini sur un modèle de repli, comme Claude Opus 4.8. Si la requête refusée envoyait `thinking: {"type": "between_tools"}`, modifiez d'abord `thinking` : seul Claude Sonnet 5.5 accepte cette valeur, omettez donc `thinking` ou définissez une valeur acceptée par le modèle de repli. Le [repli côté serveur](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#server-side-fallback) avec l'en-tête `2026-07-01` effectue cette modification pour vous lorsqu'il bascule vers Claude Sonnet 5. Un autre modèle peut normalement servir une requête que Claude Fable 5.1 ou Claude Fable 5 décline. La façon dont vous gérez l'historique de conversation dépend de l'utilisation ou non d'un [crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit) :

    * **Sans utiliser de crédit :** vous pouvez laisser en place les blocs `thinking` et `redacted_thinking` antérieurs ou les retirer pour économiser des tokens d'entrée. Le modèle de repli ne peut normalement pas les utiliser dans un cas comme dans l'autre : seuls Claude Fable 5.1 et Claude Mythos 5.1 [lisent les blocs de Claude Fable 5.1](https://platform.claude.com/docs/fr/build-with-claude/thinking#preserved-for-model), et seuls ces deux modèles ainsi que Claude Fable 5 et Claude Mythos 5 lisent les blocs de Claude Fable 5 ; pour tout autre modèle de repli, l'API les supprime donc. L'API supprime également les blocs de Claude Opus 5.5 pour tous les modèles de repli sauf Claude Fable 5.1 et Claude Mythos 5.1 (voir [Changer de modèle en cours de conversation](https://platform.claude.com/docs/fr/build-with-claude/preserved-thinking#switching-models)). Elle supprime aussi les blocs de Claude Sonnet 5.5, pour tous les modèles de repli sauf Claude Opus 5.5 sur l'API Claude et Google Cloud. Une nouvelle tentative après un refus de Claude Haiku 5.5 relève toujours de ce cas. Claude Opus 5.5 et Claude Sonnet 5.5 lisent les blocs de Claude Haiku 5.5 sur l'API Claude et Google Cloud ; laissez donc ces blocs en place lorsque vous basculez vers l'un de ces modèles sur ces plateformes. Pour un modèle de repli qui ne peut pas les lire, l'API les supprime sans les facturer.
    * **En utilisant un crédit :** envoyez le corps sans modification, car l'utilisation du crédit exige une correspondance exacte. Le serveur gère les blocs de réflexion du modèle précédent lors de l'utilisation d'un crédit, ne les supprimez donc pas (consultez [Champs qui doivent correspondre à la requête refusée](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit#reference)).
  </Step>

  <Step title="Rester sur le modèle de repli">
    Pour les conversations à plusieurs tours, continuez à utiliser le modèle de repli pour les tours suivants plutôt que de revenir au modèle initial.
  </Step>
</Steps>

Une nouvelle tentative manuelle écrit le cache de prompts du modèle de repli à partir de zéro, ce qui coûte plus cher que la lecture d'un cache existant. Le [crédit de repli](https://platform.claude.com/docs/fr/build-with-claude/fallback-credit) rembourse ce coût ; utilisez-le pour chaque nouvelle tentative que vous implémentez vous-même. Un refus de Claude Haiku 5.5 ne donne droit à aucun crédit de repli ; une nouvelle tentative après un tel refus paie donc le coût complet de l'écriture du cache de prompts du modèle de repli.

## Refus dans les Message Batches

Une requête refusée dans un [Message Batch](https://platform.claude.com/docs/fr/build-with-claude/batch-processing) revient avec `result.type: "succeeded"` et `stop_reason: "refusal"`. Les résultats de lot portent le même objet `stop_details` que les réponses synchrones, vous pouvez donc détecter les refus via `stop_reason` ou `stop_details.type`. Une différence : les refus de lot ne génèrent pas de crédits de repli, de sorte que `stop_details` sur un résultat de lot n'inclut jamais de `fallback_credit_token`.

Le repli côté serveur n'est pas disponible pour les lots (une requête de lot qui inclut `fallbacks` produit un résultat en erreur par élément). Pour relancer les éléments de lot refusés :

1. Collectez les éléments refusés dans les résultats.
2. Laissez en place les blocs de réflexion dans les historiques à plusieurs tours, ou retirez-les pour économiser des tokens d'entrée. Pour un modèle de repli qui ne peut pas les lire, l'API les supprime sans les facturer, comme pour [une nouvelle tentative manuelle](https://platform.claude.com/docs/fr/build-with-claude/refusals-and-fallback#manual-retry).
3. Si un élément refusé envoyait `thinking: {"type": "between_tools"}`, omettez `thinking` ou définissez une valeur acceptée par le modèle de repli. Seul Claude Sonnet 5.5 accepte `between_tools`.
4. Soumettez-les à nouveau sur un modèle de repli, sous forme de nouveau lot ou de requêtes directes.

## Pièges courants

* **Relancez sur un modèle différent.** Renvoyer une requête refusée au même modèle entraîne généralement un autre refus. Dirigez la nouvelle tentative vers le modèle de repli.
* **Budgétez les nouvelles tentatives par requête, et non par tour ou par session.** Un seul tour peut produire plusieurs refus, par exemple un agent plus ses sous-agents.
* **Configurez le repli sur chaque chemin de requête.** Les gestionnaires de nouvelle tentative, les branches de récupération d'erreur et les workers en arrière-plan en ont tous besoin. Un gestionnaire qui réémet une requête sans repli perd la protection précisément sur les requêtes les plus susceptibles d'en avoir besoin.
* **Donnez aux appels de sous-agents leur propre repli.** Le paramètre `fallbacks` ne se propage pas aux appels de modèle effectués depuis l'intérieur de l'exécution d'outils.
* **Faites du repli une propriété de la requête, et non d'un état ambiant.** Un indicateur partagé, une valeur de configuration en cache ou une bascule globale peut se désynchroniser et laisser silencieusement une requête sans protection. Lorsque vous ne pouvez pas confirmer que le repli est actif, configurez-le plutôt que de supposer qu'il est activé.
* **Instrumentez les refus comme un signal à part entière.** Un refus est un HTTP 200, de sorte qu'une surveillance fondée sur les taux d'erreur ou les réponses 5xx ne le voit jamais. Émettez un événement par refus et un par réponse servie par repli (l'entrée `fallback_message` dans `usage.iterations` marque cette dernière), puis alertez sur l'écart entre les deux décomptes.
* **Branchez sur `stop_reason` ou `stop_details.type`, et non sur `content` ou les champs internes de `stop_details`.** L'objet `stop_details` est toujours présent sur un refus, mais ses champs `category` et `explanation` peuvent valoir `null`. Vérifiez directement que `stop_reason` est égal à `"refusal"`.

## Étapes suivantes

<CardGroup>
  <Card title="Crédit de repli" icon="scales" href="https://platform.claude.com/docs/fr/build-with-claude/fallback-credit">
    Évitez de payer deux fois le coût de la mise en cache des prompts lorsque vous construisez vous-même la nouvelle tentative.
  </Card>

  <Card title="Raisons d'arrêt et repli" icon="code" href="https://platform.claude.com/docs/fr/build-with-claude/handling-stop-reasons">
    Chaque valeur de `stop_reason` et comment la gérer.
  </Card>

  <Card title="Middleware SDK" icon="settings" href="https://platform.claude.com/docs/fr/cli-sdks-libraries/middleware">
    Comment fonctionne le middleware SDK, y compris l'utilitaire de repli sur refus.
  </Card>

  <Card title="Guide de migration" icon="arrow-right" href="https://platform.claude.com/docs/fr/models/fable-5-1/migration-guide">
    Migrez une application existante vers Claude Fable 5.1.
  </Card>
</CardGroup>
