---
title: Sorties structurées
url: https://platform.claude.com/docs/fr/build-with-claude/structured-outputs
description: Obtenez des résultats JSON validés à partir de workflows d'agents
featureMetadata:
  status: ga
  zdr:
    eligibility: eligible
    note: Excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements).
  supportedModels:
    - claude-fable-5-1
    - claude-mythos-5-1
    - claude-fable-5
    - claude-mythos-5
    - claude-mythos-preview
    - claude-opus-5-5
    - claude-opus-5
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-5-5
    - claude-sonnet-5
    - claude-sonnet-4-6
    - claude-sonnet-4-5-20250929
    - claude-opus-4-5-20251101
    - claude-haiku-5-5
    - claude-haiku-4-5-20251001
  supportedPlatforms:
    Claude API: ga
    Claude Platform on AWS: ga
    Amazon Bedrock:
      availability: ga
      note: Sur Amazon Bedrock, les sorties structurées sont disponibles sur l'intégration héritée [Amazon Bedrock (Opus 4.6 et versions antérieures)](https://platform.claude.com/docs/fr/build-with-claude/claude-on-amazon-bedrock-legacy) pour Claude Opus 4.6, Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Opus 4.5 et Claude Haiku 4.5, et non sur [Claude in Amazon Bedrock](https://platform.claude.com/docs/fr/build-with-claude/claude-in-amazon-bedrock).
    Google Cloud: ga
    Microsoft Foundry: ga
---

Les « structured outputs » (sorties structurées) contraignent les réponses de Claude à suivre un schéma spécifique, garantissant une sortie valide et analysable pour le traitement en aval. Les sorties structurées offrent deux fonctionnalités complémentaires :

* **Sorties JSON** (`output_config.format`) : obtenez la réponse de Claude dans un format JSON spécifique, par exemple pour extraire des données d'images ou de texte, générer des rapports structurés ou formater des réponses d'API. Cette page traite des sorties JSON.
* **« Strict tool use » (utilisation d'outils stricte)** (`strict: true`) : garantissez la validation du schéma sur les noms et les entrées des outils. Consultez [Utilisation d'outils stricte](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/strict-tool-use).

Vous pouvez utiliser ces fonctionnalités indépendamment ou [ensemble dans la même requête](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#using-both-features-together).

## Pourquoi utiliser les sorties structurées

Sans sorties structurées, Claude peut générer des réponses JSON mal formées ou des entrées d'outils invalides qui cassent vos applications. Même avec un prompting soigné, vous pouvez rencontrer :

* Des erreurs d'analyse dues à une syntaxe JSON invalide
* Des champs obligatoires manquants
* Des types de données incohérents
* Des violations de schéma nécessitant une gestion des erreurs et des nouvelles tentatives

Les sorties structurées garantissent des réponses conformes au schéma grâce au décodage contraint :

* **Toujours valides :** plus d'erreurs `JSON.parse()`
* **Sûreté de typage :** types de champs et champs obligatoires garantis
* **Fiables :** aucune nouvelle tentative nécessaire pour les violations de schéma

## Fonctionnement

<Steps>
  <Step title="Définissez votre schéma">
    Décrivez la structure souhaitée sous forme de schéma JSON ou de type dans votre langage. Le schéma suit JSON Schema, avec certaines [limitations](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#json-schema-limitations).
  </Step>

  <Step title="Envoyez-le dans output_config.format">
    La requête transporte le schéma dans `output_config.format` avec `type: "json_schema"`. Les helpers du SDK le définissent pour vous.
  </Step>

  <Step title="Lisez la réponse">
    Claude renvoie un JSON valide correspondant à votre schéma dans le bloc de contenu texte de la réponse. Les helpers du SDK l'analysent dans votre type.
  </Step>
</Steps>

## Utilisation

<Tabs>
  <Tab title="cURL">
    ```bash
    curl https://api.anthropic.com/v1/messages \
      -H "content-type: application/json" \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -d '{
        "model": "claude-opus-5-5",
        "max_tokens": 1024,
        "messages": [
          {
            "role": "user",
            "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
          }
        ],
        "output_config": {
          "format": {
            "type": "json_schema",
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"},
                "plan_interest": {"type": "string"},
                "demo_requested": {"type": "boolean"}
              },
              "required": ["name", "email", "plan_interest", "demo_requested"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```

    Claude renvoie un JSON comme celui-ci dans le bloc de contenu texte de la réponse :

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create \
      --transform 'content.#(type=="text").text|@fromstr' \
      --format jsonl <<'YAML'
    model: claude-opus-5-5
    max_tokens: 1024
    messages:
      - role: user
        content: >-
          Extract the key information from this email: John Smith
          (john@example.com) is interested in our Enterprise plan and wants
          to schedule a demo for next Tuesday at 2pm.
    output_config:
      format:
        type: json_schema
        schema:
          type: object
          properties:
            name: {type: string}
            email: {type: string}
            plan_interest: {type: string}
            demo_requested: {type: boolean}
          required: [name, email, plan_interest, demo_requested]
          additionalProperties: false
    YAML
    ```

    Pour n'afficher que certaines propriétés de la sortie JSON, passez un [chemin GJSON](https://platform.claude.com/docs/fr/cli-sdks-libraries/cli/using#transform-output-with-gjson) à `--transform`.

    L'exemple ci-dessus produit :

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Python">
    ```python
    from pydantic import BaseModel
    # ...


    class ContactInfo(BaseModel):
        name: str
        email: str
        plan_interest: str
        demo_requested: bool


    client = Anthropic()

    response = client.messages.parse(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": (
                    "Extract the key information from this email: "
                    "John Smith (john@example.com) is interested in our Enterprise plan "
                    "and wants to schedule a demo for next Tuesday at 2pm."
                ),
            }
        ],
        output_format=ContactInfo,
    )

    print(response.parsed_output)
    ```

    Passez un modèle [Pydantic](https://docs.pydantic.dev/) à `client.messages.parse()`, la méthode recommandée, en tant que `output_format`. Le SDK transforme le schéma du modèle, l'envoie en tant que `output_config.format`, valide la réponse et renvoie le modèle analysé dans `parsed_output`.

    L'exemple ci-dessus produit :

    ```text Output wrap
    name='John Smith' email='john@example.com' plan_interest='Enterprise' demo_requested=True
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { z } from "zod";
    import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

    const ContactInfoSchema = z.object({
      name: z.string(),
      email: z.string(),
      plan_interest: z.string(),
      demo_requested: z.boolean()
    });

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content:
            "Extract the key information from this email: " +
            "John Smith (john@example.com) is interested in our Enterprise plan " +
            "and wants to schedule a demo for next Tuesday at 2pm."
        }
      ],
      output_config: { format: zodOutputFormat(ContactInfoSchema) }
    });

    // Analysé et validé automatiquement.
    console.log(response.parsed_output);
    ```

    Enveloppez un schéma [Zod](https://zod.dev/) dans `zodOutputFormat()` et passez-le à `client.messages.parse()` en tant que `output_config.format`. `zodOutputFormat()` transforme le schéma, et `parse()` valide la réponse et renvoie le résultat analysé dans `parsed_output`, typé pour correspondre au schéma.

    L'exemple ci-dessus produit :

    ```javascript Output
    {
      name: 'John Smith',
      email: 'john@example.com',
      plan_interest: 'Enterprise',
      demo_requested: true
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp
    using Anthropic;
    using Anthropic.Models.Messages;
    using Anthropic.Services;

    var client = new AnthropicClient();

    var response = await client.Messages.Create<ContactInfo>(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = [new() {
            Role = Role.User,
            Content = "Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm."
        }],
    });

    if (response.Content.Select(block => block.Parsed()).OfType<ContactInfo>().FirstOrDefault() is { } contact)
    {
        Console.WriteLine(contact);
    }

    public record ContactInfo
    {
        public string Name { get; set; } = "";
        public string Email { get; set; } = "";
        public string PlanInterest { get; set; } = "";
        public bool DemoRequested { get; set; }
    }
    ```

    Passez une classe ou un record C# simple à la surcharge générique `Create<T>()`. Le SDK dérive un schéma JSON à partir du type, le transforme et analyse chaque bloc de texte pour le reconvertir dans ce type.

    L'exemple ci-dessus produit :

    ```text Output wrap
    ContactInfo { Name = John Smith, Email = john@example.com, PlanInterest = Enterprise, DemoRequested = True }
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

    func main() {
    	client := anthropic.NewClient()

    	var contact ContactInfo
    	_, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.BetaMessageParam{
    			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(
    				"Extract the key information from this email: " +
    					"John Smith (john@example.com) is interested in our Enterprise plan " +
    					"and wants to schedule a demo for next Tuesday at 2pm.",
    			)),
    		},
    		OutputConfig: anthropic.BetaOutputConfigParam{
    			Format: anthropic.BetaJSONOutputFormatParam{Schema: &contact},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}

    	fmt.Printf("%#v\n", contact)
    }
    ```

    <Note>
      Le passage d'une struct comme schéma est en bêta en Go, c'est pourquoi cet exemple utilise `client.Beta.Messages.New`. Pour utiliser la version GA `client.Messages.New`, passez un schéma JSON comme indiqué dans [Utiliser un schéma JSON brut](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#using-a-raw-json-schema).
    </Note>

    Le SDK génère et transforme le schéma JSON à partir de la struct et désérialise la réponse dans celle-ci.

    L'exemple ci-dessus produit :

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```
  </Tab>

  <Tab title="Java">
    ```java
    static class ContactInfo {
        public String name;
        public String email;
        public String plan_interest;
        public boolean demo_requested;
    }

    void main() throws Exception {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        StructuredMessageCreateParams<ContactInfo> params = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .addUserMessage("Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm.")
            .outputConfig(ContactInfo.class)
            .build();

        StructuredMessage<ContactInfo> response = client.messages().create(params);
        ContactInfo contact = response.content().stream()
            .flatMap(block -> block.text().stream())
            .findFirst().orElseThrow().text();
        IO.println(new ObjectMapper().writeValueAsString(contact));
    }
    ```

    Lorsque vous passez une classe Java à `outputConfig()`, le SDK en dérive un schéma JSON et valide ce schéma. Le builder produit ensuite un `StructuredMessageCreateParams<T>`. Plutôt que de transformer le schéma, la validation locale du SDK rejette les [contraintes non prises en charge par l'API](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#json-schema-limitations). Lisez le résultat analysé avec `response.content().stream().flatMap(block -> block.text().stream()).findFirst().orElseThrow().text()`.

    L'exemple ci-dessus produit :

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```

    <Note>
      Déclarez vos classes de schéma comme classes de premier niveau ou classes imbriquées `static`. Cette exigence provient de la bibliothèque Jackson Databind (`com.fasterxml.jackson.databind`), que le SDK utilise pour désérialiser les réponses JSON en instances de vos classes et qui ne peut pas instancier les classes internes non statiques.
    </Note>

    <Accordion title="Effacement des types génériques">
      Java conserve les informations de type générique pour les champs dans les métadonnées de la classe, mais l'effacement des types génériques s'applique dans d'autres portées. Alors qu'un schéma JSON peut être dérivé d'un champ `BookList.books` de type `List<Book>`, un schéma JSON valide ne peut pas être dérivé d'une variable locale de ce même type.

      Si une erreur se produit lors de la conversion d'une réponse JSON en instance de classe Java, le message d'erreur inclut la réponse JSON pour faciliter le diagnostic. Si votre réponse JSON peut contenir des informations sensibles, évitez de la journaliser directement, ou assurez-vous de masquer tout détail sensible du message d'erreur.
    </Accordion>

    <Accordion title="Validation locale du schéma">
      Les sorties structurées prennent en charge un [sous-ensemble du langage JSON Schema](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#json-schema-limitations). Le SDK génère automatiquement des schémas à partir des classes pour s'aligner sur ce sous-ensemble. La méthode `outputConfig(Class<T>)` effectue une vérification de validation sur le schéma dérivé de la classe spécifiée.

      Points clés :

      * La **validation locale** s'effectue sans envoyer de requêtes au modèle d'IA distant.
      * La **validation distante** est également effectuée par le modèle d'IA à la réception du schéma JSON.
      * **Compatibilité des versions :** la validation locale peut échouer alors que la validation distante réussit si la version du SDK est obsolète.
      * **Désactivation de la validation locale :** passez `JsonSchemaLocalValidation.NO` si vous rencontrez des problèmes de compatibilité :

      ```java
      import com.anthropic.core.JsonSchemaLocalValidation;
      // ...

      static class BookList {
          public List<String> books;
      }

      void main() {
          StructuredMessageCreateParams<BookList> createParams = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(2048)
              .outputConfig(BookList.class, JsonSchemaLocalValidation.NO)
              .addUserMessage("List some famous late twentieth century novels.")
              .build();
      }
      ```
    </Accordion>

    <Accordion title="Streaming">
      Les sorties structurées fonctionnent également avec le streaming. Comme les réponses arrivent sous forme d'événements de flux, vous devez accumuler la réponse complète avant de désérialiser le JSON.

      Utilisez `MessageAccumulator` pour collecter les chaînes JSON du flux. Une fois accumulées, appelez `MessageAccumulator.message(Class<T>)` pour convertir le `Message` accumulé en `StructuredMessage`, qui désérialise automatiquement le JSON dans votre classe Java.
    </Accordion>

    <Accordion title="Propriétés du schéma JSON">
      Lorsque le SDK dérive un schéma JSON de vos classes Java, il inclut par défaut toutes les propriétés représentées par des champs `public` ou des méthodes getter `public` et exclut les champs et méthodes getter non `public`.

      Vous pouvez contrôler la visibilité avec des annotations :

      * `@JsonIgnore` exclut un champ ou une méthode getter `public`
      * `@JsonProperty` inclut un champ ou une méthode getter non `public`

      Si vous définissez des champs `private` avec des méthodes getter `public`, le SDK dérive le nom de la propriété à partir du getter (par exemple, le champ `private` `myValue` avec la méthode `public` `getMyValue()` produit une propriété `"myValue"`). Pour utiliser un nom de getter non conventionnel, annotez la méthode avec `@JsonProperty`.

      Chaque classe doit définir au moins une propriété pour le schéma JSON. Une erreur de validation se produit si aucun champ ni aucune méthode getter ne peut produire de propriétés de schéma, par exemple lorsque :

      * Il n'y a aucun champ ni aucune méthode getter dans la classe
      * Tous les membres `public` sont annotés avec `@JsonIgnore`
      * Tous les membres non `public` sont dépourvus d'annotations `@JsonProperty`
      * Un champ utilise un type `Map`, ce qui produit un champ `"properties"` vide
    </Accordion>

    <Accordion title="Composition et héritage">
      Vos classes Java peuvent utiliser la composition et l'héritage pour partager une structure lors de la définition de schémas JSON. Chaque modèle affecte différemment la structure de sortie.

      La **composition** produit une sortie JSON imbriquée. Dérivation d'un schéma à partir de la classe `Composed` qui compose `A` et `B` :

      ```java
      static class A {
          public String a;
      }

      static class B {
          public String b;
      }

      static class Composed {
          public A composedA;
          public B composedB;
      }
      ```

      La sortie JSON a cette structure imbriquée :

      ```json
      {
        "composedA": { "a": "hello" },
        "composedB": { "b": "world" }
      }
      ```

      L'**héritage** produit une sortie JSON plate. Dérivation d'un schéma à partir de la classe `Derived` qui étend `Base` :

      ```java
      static class Base {
          public String a;
      }

      static class Derived extends Base {
          public String b;
      }
      ```

      La sortie JSON a cette structure plate :

      ```json
      {
        "a": "hello",
        "b": "world"
      }
      ```
    </Accordion>

    <Accordion title="Annotations (Jackson et Swagger)">
      Vous pouvez utiliser les annotations Jackson Databind pour enrichir le schéma JSON dérivé de vos classes Java :

      ```java
      import com.fasterxml.jackson.annotation.JsonClassDescription;
      import com.fasterxml.jackson.annotation.JsonIgnore;
      import com.fasterxml.jackson.annotation.JsonPropertyDescription;

      static class Person {

        @JsonPropertyDescription("The first name and surname of the person")
        public String name;

        public int birthYear;

        @JsonPropertyDescription("The year the person died, or 'present' if the person is living.")
        public String deathYear;
      }

      @JsonClassDescription("The details of one published book")
      static class Book {

        public String title;
        public Person author;

        @JsonPropertyDescription("The year in which the book was first published.")
        public int publicationYear;

        @JsonIgnore
        public String genre;
      }

      static class BookList {
        public List<Book> books;
      }
      ```

      Résumé des annotations :

      * `@JsonClassDescription` : ajoute une description à une classe
      * `@JsonPropertyDescription` : ajoute une description à un champ ou une méthode getter
      * `@JsonIgnore` : exclut un champ ou getter `public` du schéma
      * `@JsonProperty` : inclut un champ ou getter non `public` dans le schéma

      Si vous utilisez `@JsonProperty(required = false)`, le SDK ignore la valeur `false`. Les schémas dérivés de classes marquent toujours toutes les propriétés comme obligatoires.

      Vous pouvez également utiliser les annotations `@Schema` et `@ArraySchema` de Swagger Core (OpenAPI 3) pour des contraintes spécifiques aux types :

      ```java
      import io.swagger.v3.oas.annotations.media.ArraySchema;
      import io.swagger.v3.oas.annotations.media.Schema;

      static class Article {

        @ArraySchema(minItems = 1)
        public List<String> authors;

        public String title;

        @Schema(format = "date")
        public String publicationDate;

        public int pageCount;
      }
      ```

      La validation locale vérifie que vous n'avez utilisé aucun mot-clé de contrainte non pris en charge, mais les valeurs des contraintes ne sont pas validées localement. Par exemple, une valeur `"format"` non prise en charge peut passer la validation locale mais provoquer une erreur distante.

      Si vous utilisez à la fois des annotations Jackson et Swagger pour définir le même champ de schéma, l'annotation Jackson est prioritaire.
    </Accordion>
  </Tab>

  <Tab title="PHP">
    ```php
    use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
    use Anthropic\Lib\Contracts\StructuredOutputModel;

    $client = new Client();

    class ContactInfo implements StructuredOutputModel
    {
        use StructuredOutputModelTrait;

        public string $name;
        public string $email;
        public string $plan_interest;
        public bool $demo_requested;
    }

    $message = $client->messages->create(
        maxTokens: 1024,
        messages: [
            [
                'role' => 'user',
                'content' => 'Extract the key information from this email: '
                    . 'John Smith (john@example.com) is interested in our Enterprise plan '
                    . 'and wants to schedule a demo for next Tuesday at 2pm.',
            ],
        ],
        model: 'claude-opus-5-5',
        outputConfig: ['format' => ContactInfo::class],
    );

    $contact = $message->parsedOutput();
    if ($contact instanceof ContactInfo) {
        var_dump($contact);
    }
    ```

    Définissez une classe qui implémente `StructuredOutputModel` et utilise `StructuredOutputModelTrait`, et passez son nom dans `outputConfig: ['format' => MyClass::class]`. Le SDK dérive un schéma JSON à partir des types de propriétés PHP 8 de la classe, le transforme et renvoie une instance typée depuis `$message->parsedOutput()`.

    `parsedOutput()` renvoie l'instance de votre modèle en cas de succès, ou `null` (ou un tableau d'erreurs) si l'analyse échoue. Utilisez `instanceof` pour affiner le type avant d'accéder aux champs.

    L'exemple ci-dessus produit :

    ```text Output wrap
    object(ContactInfo)#42 (4) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
      ["demo_requested"]=>
      bool(true)
    }
    ```

    <Accordion title="Inférence de type">
      Le SDK fait correspondre les types de propriétés natifs PHP 8 à JSON Schema :

      | Type PHP                                    | JSON Schema                       |
      | ------------------------------------------- | --------------------------------- |
      | `string`                                    | `"string"`                        |
      | `int`                                       | `"integer"`                       |
      | `float`                                     | `"number"`                        |
      | `bool`                                      | `"boolean"`                       |
      | `array`                                     | `"array"` (voir la note suivante) |
      | `?type` (nullable)                          | Champ optionnel                   |
      | Classe implémentant `StructuredOutputModel` | Objet imbriqué                    |

      Pour les propriétés `array`, le SDK ajoute un schéma `items` uniquement lorsque le type d'élément est un `StructuredOutputModel` imbriqué, déclaré avec `#[Constrained(itemClass: MyModel::class)]` ou un docblock `/** @var MyModel[] */`. Les tableaux de scalaires (`string[]`, `int[]`) émettent un `{"type":"array"}` non contraint.

      Toutes les propriétés non nullables deviennent des champs obligatoires.
    </Accordion>

    <Accordion title="Contraintes avec l'attribut #[Constrained]">
      Ajoutez des contraintes avec l'attribut `#[Constrained]` :

      ```php
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      class Address implements StructuredOutputModel { use StructuredOutputModelTrait; public string $street; }

      class Profile implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          #[Constrained(description: 'Age in years', minimum: 0, maximum: 150)]
          public int $age;

          #[Constrained(format: 'email')]
          public string $email;

          #[Constrained(itemClass: Address::class, minItems: 1)]
          public array $addresses;
      }
      ```

      **Contraintes appliquées par l'API** (envoyées dans le schéma) : `description`, `format`, `const`, `itemClass`, `minItems` (0 ou 1 uniquement).

      **Contraintes validées par le SDK** (retirées du schéma transmis, ajoutées à la description et validées par rapport à la réponse) : `minimum`, `maximum`, `multipleOf`, `minLength`, `maxLength`.
    </Accordion>
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

    class ContactInfo < Anthropic::BaseModel
      required :name, String
      required :email, String
      required :plan_interest, String
      required :demo_requested, Anthropic::Boolean
    end

    message = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [{
        role: "user",
        content: "Extract the key information from this email: " \
          "John Smith (john@example.com) is interested in our Enterprise plan " \
          "and wants to schedule a demo for next Tuesday at 2pm."
      }],
      output_config: {format: ContactInfo}
    )

    contact = message.parsed_output
    puts contact
    ```

    Définissez une classe qui étend `Anthropic::BaseModel` et passez-la à `messages.create()` sous la forme `output_config: {format: Model}`. Le SDK dérive un schéma JSON à partir de la classe, le transforme et affecte le résultat analysé à l'attribut `parsed_output` de la réponse sous forme d'objet Ruby typé.

    L'exemple ci-dessus produit :

    ```text Output wrap
    {name: "John Smith", email: "john@example.com", plan_interest: "Enterprise", demo_requested: true}
    ```

    <Accordion title="Fonctionnalités avancées des modèles">
      Le SDK Ruby prend en charge des fonctionnalités supplémentaires de définition de modèles pour des schémas plus riches :

      * **Mot-clé `doc:` :** ajoutez des descriptions aux champs pour une sortie de schéma plus informative
      * **`Anthropic::ArrayOf[T]` :** tableaux typés. Passez les contraintes au niveau du tableau (`min_items:`, `max_items:`) comme mots-clés sur `required`/`optional`, et non sur `ArrayOf` lui-même
      * **`Anthropic::EnumOf[:a, :b]` :** champs enum avec des valeurs contraintes
      * **`Anthropic::UnionOf[T1, T2]` :** types union mappés vers `anyOf`

      ```ruby
      class FamousNumber < Anthropic::BaseModel
        required :value, Float
        optional :reason, String, doc: "why is this number mathematically significant?"
      end

      class Output < Anthropic::BaseModel
        required :numbers, Anthropic::ArrayOf[FamousNumber], min_items: 3, max_items: 5
      end

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages: [{role: "user", content: "give me some famous numbers"}],
        output_config: {format: Output}
      )

      message.parsed_output
      # => #<Output numbers=[#<FamousNumber value=3.14159... reason="Pi est...">...]>
      ```
    </Accordion>
  </Tab>
</Tabs>

## Fonctionnement de la transformation par le SDK

La plupart des helpers du SDK transforment les schémas qui utilisent des fonctionnalités non prises en charge. Les étapes de transformation :

1. **Suppression des contraintes non prises en charge** (par exemple, `minimum`, `maximum`, `minLength`, `maxLength`)
2. **Mise à jour des descriptions** en ajoutant chaque contrainte non prise en charge à la description du champ (par exemple, `{minimum: 100}`)
3. **Ajout de `additionalProperties: false`** à tous les objets
4. **Filtrage des formats de chaîne** pour ne conserver que la liste prise en charge
5. **Validation des réponses** par rapport à votre schéma d'origine et à toutes ses contraintes, si le helper valide les réponses

Claude reçoit un schéma simplifié, mais un helper qui valide les réponses applique toujours chaque contrainte dans votre code.

**Exemple :** un champ avec `minimum: 100` devient un simple entier dans le schéma envoyé, et le SDK ajoute `{minimum: 100}` à la description du champ. Un helper qui valide les réponses vérifie toujours la réponse par rapport à `minimum: 100`.

## Utiliser un schéma JSON brut

Pour utiliser un schéma JSON provenant d'un fichier, d'une spécification OpenAPI ou d'un code qui le construit à l'exécution, passez-le dans `output_config.format`.

<Tabs>
  <Tab title="cURL">
    ```bash
    curl https://api.anthropic.com/v1/messages \
      -H "content-type: application/json" \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -d '{
        "model": "claude-opus-5-5",
        "max_tokens": 1024,
        "messages": [
          {
            "role": "user",
            "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
          }
        ],
        "output_config": {
          "format": {
            "type": "json_schema",
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"},
                "plan_interest": {"type": "string"},
                "demo_requested": {"type": "boolean"}
              },
              "required": ["name", "email", "plan_interest", "demo_requested"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```

    Claude renvoie un JSON comme celui-ci dans le bloc de contenu texte de la réponse :

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create \
      --transform 'content.#(type=="text").text|@fromstr' \
      --format jsonl <<'YAML'
    model: claude-opus-5-5
    max_tokens: 1024
    messages:
      - role: user
        content: >-
          Extract the key information from this email: John Smith
          (john@example.com) is interested in our Enterprise plan and wants
          to schedule a demo for next Tuesday at 2pm.
    output_config:
      format:
        type: json_schema
        schema:
          type: object
          properties:
            name: {type: string}
            email: {type: string}
            plan_interest: {type: string}
            demo_requested: {type: boolean}
          required: [name, email, plan_interest, demo_requested]
          additionalProperties: false
    YAML
    ```

    L'exemple ci-dessus produit :

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Python">
    ```python
    client = anthropic.Anthropic()

    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": (
                    "Extract the key information from this email: "
                    "John Smith (john@example.com) is interested in our Enterprise plan "
                    "and wants to schedule a demo for next Tuesday at 2pm."
                ),
            }
        ],
        output_config={
            "format": {
                "type": "json_schema",
                "schema": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "email": {"type": "string"},
                        "plan_interest": {"type": "string"},
                        "demo_requested": {"type": "boolean"},
                    },
                    "required": ["name", "email", "plan_interest", "demo_requested"],
                    "additionalProperties": False,
                },
            }
        },
    )
    text = next(block.text for block in response.content if block.type == "text")
    contact = json.loads(text)
    print(contact)
    ```

    L'exemple ci-dessus produit :

    ```text Output wrap
    {'name': 'John Smith', 'email': 'john@example.com', 'plan_interest': 'Enterprise', 'demo_requested': True}
    ```

    Pour déplacer les [contraintes non prises en charge par l'API](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#json-schema-limitations) dans les descriptions des champs, faites passer le schéma par `transform_schema()` du package `anthropic` avant de l'envoyer, et modifiez le résultat si nécessaire. `transform_schema()` accepte également un modèle Pydantic. Contrairement à `client.messages.parse()`, elle renvoie le schéma transformé au lieu de l'envoyer.
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { jsonSchemaOutputFormat } from "@anthropic-ai/sdk/helpers/json-schema";

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"
        }
      ],
      output_config: {
        format: jsonSchemaOutputFormat({
          type: "object",
          properties: {
            name: { type: "string" },
            email: { type: "string" },
            planInterest: { type: "string" }
          },
          required: ["name", "email", "planInterest"],
          additionalProperties: false
        } as const)
      }
    });

    // response.parsed_output est typé comme { name: string; email: string; planInterest: string } | null.
    console.log(response.parsed_output);
    ```

    Utilisez `jsonSchemaOutputFormat()` pour passer un schéma JSON simple à `parse()`, sans installer Zod. L'API renvoie une réponse conforme au schéma, et si vous déclarez le schéma avec `as const`, le type de `parsed_output` est automatiquement inféré par TypeScript en fonction de votre schéma. Pour un schéma importé ou généré, `parsed_output` est typé comme `unknown`.

    Par défaut, le helper [transforme le schéma](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#how-sdk-transformation-works) de la même manière que `zodOutputFormat()`. Passez `{ transform: false }` comme second argument pour l'envoyer sans modification. Contrairement à `zodOutputFormat()`, il ne valide pas la réponse, il ne détectera donc pas les violations des [contraintes non prises en charge par l'API](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#json-schema-limitations), telles que `minimum`.

    L'exemple ci-dessus produit :

    ```javascript Output
    { name: 'John Smith', email: 'john@example.com', planInterest: 'Pro' }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp
    using System.Text.Json;
    using Anthropic;
    using Anthropic.Models.Messages;

    var client = new AnthropicClient();

    var response = await client.Messages.Create(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = [new() {
            Role = Role.User,
            Content = "Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm."
        }],
        OutputConfig = new OutputConfig
        {
            Format = new JsonOutputFormat
            {
                Schema = new Dictionary<string, JsonElement>
                {
                    ["type"] = JsonSerializer.SerializeToElement("object"),
                    ["properties"] = JsonSerializer.SerializeToElement(new
                    {
                        name = new { type = "string" },
                        email = new { type = "string" },
                        plan_interest = new { type = "string" },
                        demo_requested = new { type = "boolean" },
                    }),
                    ["required"] = JsonSerializer.SerializeToElement(
                        new[] { "name", "email", "plan_interest", "demo_requested" }),
                    ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                },
            },
        },
    });

    if (response.Content.Select(b => b.Value).OfType<TextBlock>().FirstOrDefault() is { } textBlock)
    {
        // Le JSON est garanti conforme au schéma.
        var contact = JsonSerializer.Deserialize<JsonElement>(textBlock.Text);
        Console.WriteLine(contact);
    }
    ```

    L'exemple ci-dessus produit :

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

    func main() {
    	client := anthropic.NewClient()

    	message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.MessageParam{
    			anthropic.NewUserMessage(anthropic.NewTextBlock(
    				"Extract the key information from this email: " +
    					"John Smith (john@example.com) is interested in our Enterprise plan " +
    					"and wants to schedule a demo for next Tuesday at 2pm.",
    			)),
    		},
    		OutputConfig: anthropic.OutputConfigParam{
    			Format: anthropic.JSONOutputFormatParam{
    				Schema: map[string]any{
    					"type": "object",
    					"properties": map[string]any{
    						"name":           map[string]string{"type": "string"},
    						"email":          map[string]string{"type": "string"},
    						"plan_interest":  map[string]string{"type": "string"},
    						"demo_requested": map[string]string{"type": "boolean"},
    					},
    					"required":             []string{"name", "email", "plan_interest", "demo_requested"},
    					"additionalProperties": false,
    				},
    			},
    		},
    	})

    	for _, block := range message.Content {
    		switch variant := block.AsAny().(type) {
    		case anthropic.TextBlock:
    			var contact ContactInfo
    			json.Unmarshal([]byte(variant.Text), &contact)
    			fmt.Printf("%#v\n", contact)
    		}
    	}
    }
    ```

    L'exemple ci-dessus produit :

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```

    Pour que le SDK transforme un schéma sous forme de map, faites-le passer par `BetaJSONSchemaOutputFormat()` sur l'API bêta.
  </Tab>

  <Tab title="Java">
    ```java
    import com.anthropic.core.JsonValue;
    import com.anthropic.models.messages.JsonOutputFormat;
    // ...
    import com.anthropic.models.messages.OutputConfig;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;

    void main() throws Exception {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        JsonOutputFormat.Schema schema = JsonOutputFormat.Schema.builder()
            .putAdditionalProperty("type", JsonValue.from("object"))
            .putAdditionalProperty("properties", JsonValue.from(Map.of(
                "name", Map.of("type", "string"),
                "email", Map.of("type", "string"),
                "plan_interest", Map.of("type", "string"))))
            .putAdditionalProperty("required", JsonValue.from(
                List.of("name", "email", "plan_interest")))
            .putAdditionalProperty("additionalProperties", JsonValue.from(false))
            .build();

        OutputConfig outputConfig = OutputConfig.builder()
            .format(JsonOutputFormat.builder().schema(schema).build())
            .build();

        MessageCreateParams createParams = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .outputConfig(outputConfig)
            .addUserMessage(
                "John Smith (john@example.com) is interested in our Enterprise plan.")
            .build();

        String json = client.messages().create(createParams).content().stream()
            .flatMap(contentBlock -> contentBlock.text().stream())
            .findFirst()
            .orElseThrow()
            .text();

        JsonNode contact = new ObjectMapper().readTree(json);
        IO.println(contact);
    }
    ```

    L'exemple ci-dessus produit :

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise"}
    ```

    Pour un exemple plus complet qui construit un schéma imbriqué avec des tableaux et des descriptions, consultez [`StructuredOutputsRawExample.java`](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/StructuredOutputsRawExample.java) dans le dépôt du SDK.
  </Tab>

  <Tab title="PHP">
    ```php
    use Anthropic\Messages\OutputConfig;
    use Anthropic\Messages\JSONOutputFormat;

    $client = new Client();

    $message = $client->messages->create(
        maxTokens: 1024,
        messages: [
            [
                'role' => 'user',
                'content' => 'Extract the key information from this email: '
                    . 'John Smith (john@example.com) is interested in our Enterprise plan.',
            ],
        ],
        model: 'claude-opus-5-5',
        outputConfig: OutputConfig::with(format: JSONOutputFormat::with(schema: [
            'type' => 'object',
            'properties' => [
                'name' => ['type' => 'string'],
                'email' => ['type' => 'string'],
                'plan_interest' => ['type' => 'string'],
            ],
            'required' => ['name', 'email', 'plan_interest'],
            'additionalProperties' => false,
        ])),
    );

    $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
    $contact = json_decode($textBlock->text, associative: true);
    var_dump($contact);
    ```

    Passez le schéma sous forme de tableau associatif à `OutputConfig::with()`, et décodez la réponse avec `json_decode()`.

    L'exemple ci-dessus produit :

    ```text Output wrap
    array(3) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
    }
    ```
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

    response = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: "Extract the key information from this email: " \
            "John Smith (john@example.com) is interested in our Enterprise plan " \
            "and wants to schedule a demo for next Tuesday at 2pm."
        }
      ],
      output_config: {
        format: {
          type: "json_schema",
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              email: { type: "string" },
              plan_interest: { type: "string" },
              demo_requested: { type: "boolean" }
            },
            required: ["name", "email", "plan_interest", "demo_requested"],
            additionalProperties: false
          }
        }
      }
    )

    text = response.content.find { it.type == :text }.text
    contact = JSON.parse(text)
    p contact
    ```

    L'exemple ci-dessus produit :

    ```text Output wrap
    {"name" => "John Smith", "email" => "john@example.com", "plan_interest" => "Enterprise", "demo_requested" => true}
    ```
  </Tab>
</Tabs>

## Cas d'utilisation courants

<AccordionGroup>
  <Accordion title="Extraction de données">
    Extrayez des données structurées à partir de texte non structuré :

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 4096,
          "messages": [
            {
              "role": "user",
              "content": "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Total: $500.00"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "invoice_number": {"type": "string"},
                  "date": {"type": "string"},
                  "total_amount": {"type": "number"},
                  "line_items": {
                    "type": "array",
                    "items": {"type": "object", "additionalProperties": false}
                  },
                  "customer_name": {"type": "string"}
                },
                "required": ["invoice_number", "date", "total_amount", "line_items", "customer_name"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text|@fromstr' \
        --format jsonl <<'YAML'
      model: claude-opus-5-5
      max_tokens: 4096
      messages:
        - role: user
          content: "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Total: $500.00"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              invoice_number: {type: string}
              date: {type: string}
              total_amount: {type: number}
              line_items:
                type: array
                items: {type: object, additionalProperties: false}
              customer_name: {type: string}
            required: [invoice_number, date, total_amount, line_items, customer_name]
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel


      class Invoice(BaseModel):
          invoice_number: str
          date: str
          total_amount: float
          line_items: list[dict]
          customer_name: str


      client = anthropic.Anthropic()
      invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=4096,
          output_format=Invoice,
          messages=[
              {"role": "user", "content": f"Extract invoice data from: {invoice_text}"}
          ],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const InvoiceSchema = z.object({
        invoice_number: z.string(),
        date: z.string(),
        total_amount: z.number(),
        line_items: z.array(z.record(z.string(), z.any())),
        customer_name: z.string()
      });

      const invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: { format: zodOutputFormat(InvoiceSchema) },
        messages: [{ role: "user", content: `Extract invoice data from: ${invoiceText}` }]
      });
      console.log(response.parsed_output);
      ```

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

      string invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 4096,
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          invoice_number = new { type = "string" },
                          date = new { type = "string" },
                          total_amount = new { type = "number" },
                          line_items = new
                          {
                              type = "array",
                              items = new
                              {
                                  type = "object",
                                  additionalProperties = false,
                              },
                          },
                          customer_name = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "invoice_number", "date", "total_amount", "line_items", "customer_name" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
          Messages = [new() { Role = Role.User, Content = $"Extract invoice data from: {invoiceText}" }]
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

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

      invoiceText := "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      schema := map[string]any{
      	"type":                 "object",
      	"additionalProperties": false,
      	"properties": map[string]any{
      		"invoice_number": map[string]any{"type": "string"},
      		"date":           map[string]any{"type": "string"},
      		"total_amount":   map[string]any{"type": "number"},
      		"line_items": map[string]any{
      			"type": "array",
      			"items": map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"description": map[string]any{"type": "string"},
      					"quantity":    map[string]any{"type": "number"},
      					"unit_price":  map[string]any{"type": "number"},
      				},
      				"required": []string{"description", "quantity", "unit_price"},
      			},
      		},
      		"customer_name": map[string]any{"type": "string"},
      	},
      	"required": []string{"invoice_number", "date", "total_amount", "line_items", "customer_name"},
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 4096,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Extract invoice data from: %s", invoiceText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class LineItem {
          @JsonProperty("description")
          public String description;

          @JsonProperty("quantity")
          public int quantity;

          @JsonProperty("unit_price")
          public double unitPrice;
      }

      static class Invoice {
          @JsonProperty("invoice_number")
          public String invoiceNumber;

          @JsonProperty("date")
          public String date;

          @JsonProperty("total_amount")
          public double totalAmount;

          @JsonProperty("line_items")
          public List<LineItem> lineItems;

          @JsonProperty("customer_name")
          public String customerName;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          String invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

          StructuredMessageCreateParams<Invoice> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(4096L)
              .outputConfig(Invoice.class)
              .addUserMessage("Extract invoice data from: " + invoiceText)
              .build();

          StructuredMessage<Invoice> response = client.messages().create(params);
          Invoice invoice = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(invoice.invoiceNumber + ": $" + invoice.totalAmount);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Invoice implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $invoice_number;
          public string $date;
          public float $total_amount;
          public array $line_items;
          public string $customer_name;
      }

      $invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

      $message = $client->messages->create(
          maxTokens: 4096,
          messages: [
              ['role' => 'user', 'content' => "Extract invoice data from: $invoiceText"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Invoice::class],
      );

      $invoice = $message->parsedOutput();
      if ($invoice instanceof Invoice) {
          echo "Invoice {$invoice->invoice_number}: \${$invoice->total_amount}\n";
      }
      ```

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

      class LineItem < Anthropic::BaseModel
        required :description, String
        required :amount, Float
      end

      class Invoice < Anthropic::BaseModel
        required :invoice_number, String
        required :date, String
        required :total_amount, Float
        required :line_items, Anthropic::ArrayOf[LineItem]
        required :customer_name, String
      end

      invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: {format: Invoice},
        messages: [
          {role: "user", content: "Extract invoice data from: #{invoice_text}"}
        ]
      )

      invoice = message.parsed_output
      puts "Invoice #{invoice.invoice_number}: $#{invoice.total_amount}"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Classification">
    Classez du contenu avec des catégories structurées :

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Classify this feedback: Great product, fast shipping!"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "category": {"type": "string"},
                  "confidence": {"type": "number"},
                  "tags": {"type": "array", "items": {"type": "string"}},
                  "sentiment": {"type": "string"}
                },
                "required": ["category", "confidence", "tags", "sentiment"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text|@fromstr' \
        --format jsonl <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      messages:
        - role: user
          content: "Classify this feedback: Great product, fast shipping!"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              category:
                type: string
              confidence:
                type: number
              tags:
                type: array
                items:
                  type: string
              sentiment:
                type: string
            required:
              - category
              - confidence
              - tags
              - sentiment
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class Classification(BaseModel):
          category: str
          confidence: float
          tags: list[str]
          sentiment: str


      feedback_text = "Great product, but the delivery was slow."
      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=Classification,
          messages=[{"role": "user", "content": f"Classify this feedback: {feedback_text}"}],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const ClassificationSchema = z.object({
        category: z.string(),
        confidence: z.number(),
        tags: z.array(z.string()),
        sentiment: z.string()
      });

      const feedbackText = "Great product, but the delivery was slow.";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(ClassificationSchema) },
        messages: [{ role: "user", content: `Classify this feedback: ${feedbackText}` }]
      });

      console.log(response.parsed_output);
      ```

      ```csharp C#
      string feedbackText = "Great product, fast shipping!";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = $"Classify this feedback: {feedbackText}" }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          category = new { type = "string" },
                          confidence = new { type = "number" },
                          tags = new { type = "array", items = new { type = "string" } },
                          sentiment = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "category", "confidence", "tags", "sentiment" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      feedbackText := "Great product, fast shipping!"

      schema := map[string]any{
      	"type": "object",
      	"properties": map[string]any{
      		"category":   map[string]any{"type": "string"},
      		"confidence": map[string]any{"type": "number"},
      		"tags":       map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
      		"sentiment":  map[string]any{"type": "string"},
      	},
      	"required":             []string{"category", "confidence", "tags", "sentiment"},
      	"additionalProperties": false,
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Classify this feedback: %s", feedbackText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		var result map[string]any
      		json.Unmarshal([]byte(variant.Text), &result)
      		fmt.Println(result)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class Classification {
          @JsonProperty("category")
          public String category;

          @JsonProperty("confidence")
          public double confidence;

          @JsonProperty("tags")
          public List<String> tags;

          @JsonProperty("sentiment")
          public String sentiment;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          String feedbackText = "Great product, fast shipping!";

          StructuredMessageCreateParams<Classification> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(Classification.class)
              .addUserMessage("Classify this feedback: " + feedbackText)
              .build();

          StructuredMessage<Classification> response = client.messages().create(params);
          Classification result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.category + " (" + result.confidence + ")");
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Classification implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $category;
          public float $confidence;
          public array $tags;
          public string $sentiment;
      }

      $feedbackText = "Great product, fast shipping!";

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => "Classify this feedback: {$feedbackText}"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Classification::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof Classification) {
          echo "{$result->category} ({$result->confidence}): {$result->sentiment}\n";
      }
      ```

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

      class Classification < Anthropic::BaseModel
        required :category, String
        required :confidence, Float
        required :tags, Anthropic::ArrayOf[String]
        required :sentiment, String
      end

      feedback_text = "Great product, fast shipping!"

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: {format: Classification},
        messages: [
          {role: "user", content: "Classify this feedback: #{feedback_text}"}
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Formatage de réponses d'API">
    Générez des réponses prêtes pour une API :

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Process this request: ..."
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "status": {"type": "string"},
                  "data": {"type": "object", "additionalProperties": false},
                  "errors": {
                    "type": "array",
                    "items": {"type": "object", "additionalProperties": false}
                  },
                  "metadata": {"type": "object", "additionalProperties": false}
                },
                "required": ["status", "data", "metadata"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text' \
        --raw-output <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              status:
                type: string
              data:
                type: object
                additionalProperties: false
              errors:
                type: array
                items:
                  type: object
                  additionalProperties: false
              metadata:
                type: object
                additionalProperties: false
            required:
              - status
              - data
              - metadata
            additionalProperties: false
      messages:
        - role: user
          content: "Process this request: ..."
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class APIResponse(BaseModel):
          status: str
          data: dict
          errors: list[dict] | None
          metadata: dict


      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=APIResponse,
          messages=[{"role": "user", "content": "Process this request: ..."}],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const APIResponseSchema = z.object({
        status: z.string(),
        data: z.record(z.string(), z.any()),
        errors: z.array(z.record(z.string(), z.any())).optional(),
        metadata: z.record(z.string(), z.any())
      });

      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(APIResponseSchema) },
        messages: [{ role: "user", content: "Process this request..." }]
      });

      console.log(response.parsed_output);
      ```

      ```csharp C#
      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = "Process this request: ..." }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          status = new { type = "string" },
                          data = new { type = "object", additionalProperties = false },
                          errors = new
                          {
                              type = "array",
                              items = new { type = "object", additionalProperties = false },
                          },
                          metadata = new { type = "object", additionalProperties = false },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "status", "data", "metadata" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

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

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"status": map[string]any{
      						"type": "string",
      					},
      					"data": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      					},
      					"errors": map[string]any{
      						"type": "array",
      						"items": map[string]any{
      							"type":                 "object",
      							"additionalProperties": false,
      						},
      					},
      					"metadata": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      					},
      				},
      				"required": []string{"status", "data", "metadata"},
      			},
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock("Process this request: ...")),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class APIData {
          @JsonProperty("message")
          public String message;

          @JsonProperty("resource_id")
          public String resourceId;
      }

      static class APIError {
          @JsonProperty("code")
          public String code;

          @JsonProperty("message")
          public String message;
      }

      static class APIMetadata {
          @JsonProperty("request_id")
          public String requestId;

          @JsonProperty("timestamp")
          public String timestamp;
      }

      static class APIResponse {
          @JsonProperty("status")
          public String status;

          @JsonProperty("data")
          public APIData data;

          @JsonProperty("errors")
          public List<APIError> errors;

          @JsonProperty("metadata")
          public APIMetadata metadata;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          StructuredMessageCreateParams<APIResponse> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(APIResponse.class)
              .addUserMessage("Process this request: ...")
              .build();

          StructuredMessage<APIResponse> response = client.messages().create(params);
          APIResponse result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.status);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Payload implements StructuredOutputModel { use StructuredOutputModelTrait; public string $message; }

      class APIError implements StructuredOutputModel { use StructuredOutputModelTrait; public string $code; public string $detail; }

      class Metadata implements StructuredOutputModel { use StructuredOutputModelTrait; public string $request_id; }

      class APIResponse implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $status;
          public Payload $data;
          #[Constrained(itemClass: APIError::class)]
          public ?array $errors;
          public Metadata $metadata;
      }

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => 'Process this request: ...']
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => APIResponse::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof APIResponse) {
          echo "{$result->status}: {$result->data->message}\n";
      }
      ```

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

      class Payload < Anthropic::BaseModel
        required :message, String
      end

      class APIError < Anthropic::BaseModel
        required :code, String
        required :detail, String
      end

      class Metadata < Anthropic::BaseModel
        required :request_id, String
      end

      class APIResponse < Anthropic::BaseModel
        required :status, String
        required :data, Payload
        optional :errors, Anthropic::ArrayOf[APIError]
        required :metadata, Metadata
      end

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: {format: APIResponse},
        messages: [
          {role: "user", content: "Process this request: ..."}
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## Utilisation d'outils stricte

Pour imposer la conformité à JSON Schema sur les entrées d'outils avec un échantillonnage contraint par grammaire, consultez [Utilisation d'outils stricte](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/strict-tool-use).

### Utiliser les deux fonctionnalités ensemble

Les sorties JSON et l'utilisation d'outils stricte résolvent des problèmes différents et fonctionnent ensemble :

* Les **sorties JSON** contrôlent le format de réponse de Claude (ce que Claude dit)
* L'**utilisation d'outils stricte** valide les paramètres des outils (comment Claude appelle vos fonctions)

Combinées, elles permettent à Claude d'appeler des outils avec des paramètres garantis valides ET de renvoyer des réponses JSON structurées. Cela est utile pour les workflows agentiques où vous avez besoin à la fois d'appels d'outils fiables et de sorties finales structurées.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": "Help me plan a trip to Paris departing May 15, 2026"
        }
      ],
      "output_config": {
        "format": {
          "type": "json_schema",
          "schema": {
            "type": "object",
            "properties": {
              "summary": {"type": "string"},
              "next_steps": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["summary", "next_steps"],
            "additionalProperties": false
          }
        }
      },
      "tools": [
        {
          "name": "search_flights",
          "strict": true,
          "input_schema": {
            "type": "object",
            "properties": {
              "destination": {"type": "string"},
              "date": {"type": "string", "format": "date"}
            },
            "required": ["destination", "date"],
            "additionalProperties": false
          }
        }
      ]
    }'
  ```

  ```bash CLI
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: Help me plan a trip to Paris departing May 15, 2026
  # Sorties JSON : format de réponse structuré
  output_config:
    format:
      type: json_schema
      schema:
        type: object
        properties:
          summary:
            type: string
          next_steps:
            type: array
            items:
              type: string
        required: [summary, next_steps]
        additionalProperties: false
  # Utilisation d'outils stricte : paramètres d'outils garantis
  tools:
    - name: search_flights
      strict: true
      input_schema:
        type: object
        properties:
          destination:
            type: string
          date:
            type: string
            format: date
        required: [destination, date]
        additionalProperties: false
  YAML
  ```

  ```python Python
  response = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": "Help me plan a trip to Paris departing May 15, 2026",
          }
      ],
      # Sorties JSON : format de réponse structuré
      output_config={
          "format": {
              "type": "json_schema",
              "schema": {
                  "type": "object",
                  "properties": {
                      "summary": {"type": "string"},
                      "next_steps": {"type": "array", "items": {"type": "string"}},
                  },
                  "required": ["summary", "next_steps"],
                  "additionalProperties": False,
              },
          }
      },
      # Utilisation d'outils stricte : paramètres d'outils garantis
      tools=[
          {
              "name": "search_flights",
              "strict": True,
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "destination": {"type": "string"},
                      "date": {"type": "string", "format": "date"},
                  },
                  "required": ["destination", "date"],
                  "additionalProperties": False,
              },
          }
      ],
  )

  print(response)
  ```

  ```typescript TypeScript
  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }],
    // Sorties JSON : format de réponse structuré
    output_config: {
      format: {
        type: "json_schema",
        schema: {
          type: "object",
          properties: {
            summary: { type: "string" },
            next_steps: { type: "array", items: { type: "string" } }
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    // Utilisation d'outils stricte : paramètres d'outil garantis
    tools: [
      {
        name: "search_flights",
        description: "Search for available flights to a destination on a specific date",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: { type: "string" },
            date: { type: "string", format: "date" }
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  });

  // Claude peut d'abord appeler l'outil (tool_use) ou répondre en JSON (texte)
  console.log("Stop reason:", response.stop_reason);
  for (const block of response.content) {
    switch (block.type) {
      case "tool_use":
        console.log(`Tool call: ${block.name}(${JSON.stringify(block.input)})`);
        break;
      case "text":
        console.log("Response:", block.text);
        break;
    }
  }
  ```

  ```csharp C#
  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Help me plan a trip to Paris departing May 15, 2026" }],
      // Sorties JSON : format de réponse structuré
      OutputConfig = new OutputConfig
      {
          Format = new JsonOutputFormat
          {
              Schema = new Dictionary<string, JsonElement>
              {
                  ["type"] = JsonSerializer.SerializeToElement("object"),
                  ["properties"] = JsonSerializer.SerializeToElement(new
                  {
                      summary = new { type = "string" },
                      next_steps = new { type = "array", items = new { type = "string" } },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "summary", "next_steps" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              },
          },
      },
      // Utilisation d'outils stricte : paramètres d'outils garantis
      Tools =
      [
          new Tool
          {
              Name = "search_flights",
              Strict = true,
              InputSchema = new InputSchema(new Dictionary<string, JsonElement>
              {
                  ["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>
                  {
                      ["destination"] = new { type = "string" },
                      ["date"] = new { type = "string", format = "date" },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "destination", "date" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              }),
          }
      ],
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
  ```

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

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Help me plan a trip to Paris departing May 15, 2026")),
  	},
  	// Sorties JSON : format de réponse structuré
  	OutputConfig: anthropic.OutputConfigParam{
  		Format: anthropic.JSONOutputFormatParam{
  			Schema: map[string]any{
  				"type":                 "object",
  				"additionalProperties": false,
  				"properties": map[string]any{
  					"summary":    map[string]any{"type": "string"},
  					"next_steps": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
  				},
  				"required": []string{"summary", "next_steps"},
  			},
  		},
  	},
  	// Utilisation d'outils stricte : paramètres d'outils garantis
  	Tools: []anthropic.ToolUnionParam{
  		{OfTool: &anthropic.ToolParam{
  			Name:   "search_flights",
  			Strict: anthropic.Bool(true),
  			InputSchema: anthropic.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"destination": map[string]any{"type": "string"},
  					"date":        map[string]any{"type": "string", "format": "date"},
  				},
  				Required: []string{"destination", "date"},
  				ExtraFields: map[string]any{
  					"additionalProperties": false,
  				},
  			}}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response.Content)
  ```

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

  // Sorties JSON : format de réponse structuré
  JsonOutputFormat.Schema outputSchema = JsonOutputFormat.Schema.builder()
      .putAdditionalProperty("type", JsonValue.from("object"))
      .putAdditionalProperty("properties", JsonValue.from(Map.of(
          "summary", Map.of("type", "string"),
          "next_steps", Map.of("type", "array", "items", Map.of("type", "string"))
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("summary", "next_steps")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  // Utilisation d'outils stricte : paramètres d'outils garantis
  InputSchema toolSchema = InputSchema.builder()
      .properties(JsonValue.from(Map.of(
          "destination", Map.of("type", "string"),
          "date", Map.of("type", "string", "format", "date")
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("destination", "date")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Help me plan a trip to Paris departing May 15, 2026")
      .outputConfig(OutputConfig.builder()
          .format(JsonOutputFormat.builder().schema(outputSchema).build())
          .build())
      .addTool(Tool.builder()
          .name("search_flights")
          .description("Search for available flights to a destination on a specific date")
          .strict(true)
          .inputSchema(toolSchema)
          .build())
      .build();

  Message response = client.messages().create(params);
  IO.println(response);
  ```

  ```php PHP
  use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
  use Anthropic\Lib\Contracts\StructuredOutputModel;
  use Anthropic\Messages\ToolUseBlock;

  $client = new Client();

  class TripPlan implements StructuredOutputModel
  {
      use StructuredOutputModelTrait;

      public string $summary;
      public array $next_steps;
  }

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          ['role' => 'user', 'content' => 'Help me plan a trip to Paris departing May 15, 2026']
      ],
      model: 'claude-opus-5-5',
      // Sorties JSON : format de réponse structuré
      outputConfig: ['format' => TripPlan::class],
      // Utilisation d'outils stricte : paramètres d'outil garantis
      tools: [
          [
              'name' => 'search_flights',
              'strict' => true,
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'destination' => ['type' => 'string'],
                      'date' => ['type' => 'string', 'format' => 'date']
                  ],
                  'required' => ['destination', 'date'],
                  'additionalProperties' => false
              ]
          ]
      ],
  );

  // Claude peut d'abord appeler l'outil (tool_use) ou répondre en JSON (texte)
  $plan = $message->parsedOutput();
  if ($plan instanceof TripPlan) {
      echo $plan->summary, "\n";
  } elseif ($toolUse = array_find($message->content, fn($block) => $block instanceof ToolUseBlock)) {
      echo "Tool call: {$toolUse->name}(", json_encode($toolUse->input), ")\n";
  }
  ```

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

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [
      {role: "user", content: "Help me plan a trip to Paris departing May 15, 2026"}
    ],
    # Sorties JSON : format de réponse structuré.
    output_config: {
      format: {
        type: :json_schema,
        schema: {
          type: "object",
          properties: {
            summary: {type: "string"},
            next_steps: {type: "array", items: {type: "string"}}
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    # Utilisation d'outils stricte : paramètres d'outils garantis.
    tools: [
      {
        name: "search_flights",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: {type: "string"},
            date: {type: "string", format: "date"}
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  )
  puts message
  ```
</CodeGroup>

## Considérations importantes

### Compilation et mise en cache des grammaires

Les sorties structurées utilisent un échantillonnage contraint avec des artefacts de grammaire compilés. Cela introduit certaines caractéristiques de performance à connaître :

* **Latence de la première requête :** la première fois que vous utilisez un schéma spécifique, il y a une latence supplémentaire pendant la compilation de la grammaire

* **Mise en cache automatique :** les grammaires compilées sont mises en cache pendant 24 heures à compter de la dernière utilisation, ce qui rend les requêtes suivantes beaucoup plus rapides

* **Invalidation du cache :** le cache est invalidé si vous modifiez :

  * La structure du schéma JSON
  * L'ensemble des outils de votre requête (lorsque vous utilisez à la fois les sorties structurées et l'utilisation d'outils)
  * La modification des seuls champs `name` ou `description` n'invalide pas le cache

### Modification du prompt et coûts en tokens

Lorsque vous utilisez les sorties structurées, Claude reçoit automatiquement une invite système supplémentaire expliquant le format de sortie attendu. Cela signifie que :

* Votre nombre de tokens d'entrée est légèrement plus élevé
* Le prompt injecté vous coûte des tokens comme toute autre invite système
* La modification du paramètre `output_config.format` invalidera tout [cache de prompts](https://platform.claude.com/docs/fr/build-with-claude/prompt-caching) pour ce fil de conversation

### Limitations de JSON Schema

Les sorties structurées prennent en charge le JSON Schema standard avec certaines limitations. Les sorties JSON et l'utilisation d'outils stricte partagent ces limitations.

<Accordion title="Fonctionnalités prises en charge">
  * Tous les types de base : object, array, string, integer, number, boolean, null
  * `enum` (chaînes, nombres, booléens ou nulls uniquement - pas de types complexes). Pour une mise en garde concernant la casse, consultez [Sorties invalides](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#invalid-outputs).
  * `const`
  * `anyOf` et `allOf` (avec limitations - `allOf` avec `$ref` non pris en charge)
  * `$ref`, `$def` et `definitions` (`$ref` externe non pris en charge)
  * Propriété `default` pour tous les types pris en charge
  * `required` et `additionalProperties` (doit être défini à `false` pour les objets)
  * Formats de chaînes : `date-time`, `time`, `date`, `duration`, `email`, `hostname`, `uri`, `ipv4`, `ipv6`, `uuid`
  * `minItems` pour les tableaux (seules les valeurs 0 et 1 sont prises en charge)
</Accordion>

<Accordion title="Non pris en charge">
  * Schémas récursifs
  * Types complexes dans les enums
  * `$ref` externe (par exemple, `'$ref': 'http://...'`)
  * Contraintes numériques (telles que `minimum`, `maximum`, `multipleOf`)
  * Contraintes de chaînes (`minLength`, `maxLength`)
  * Contraintes de tableaux au-delà de `minItems` de 0 ou 1
  * `additionalProperties` défini à autre chose que `false`

  Si vous utilisez une fonctionnalité non prise en charge, vous recevrez une erreur 400 avec des détails.
</Accordion>

<Accordion title="Prise en charge des motifs (regex)">
  **Fonctionnalités regex prises en charge :**

  * Correspondance complète (`^...$`) et correspondance partielle
  * Quantificateurs : `*`, `+`, `?`, cas simples de `{n,m}`
  * Classes de caractères : `[]`, `.`, `\d`, `\w`, `\s`
  * Groupes : `(...)`

  **NON pris en charge :**

  * Références arrière vers des groupes (par exemple, `\1`, `\2`)
  * Assertions lookahead/lookbehind (par exemple, `(?=...)`, `(?!...)`)
  * Limites de mots : `\b`, `\B`
  * Quantificateurs `{n,m}` complexes avec de grandes plages

  Les motifs regex simples fonctionnent bien. Les motifs complexes peuvent entraîner des erreurs 400.
</Accordion>

<Tip>
  Les helpers du SDK peuvent transformer les schémas comportant des [contraintes non prises en charge par l'API](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#json-schema-limitations). Consultez [Fonctionnement de la transformation par le SDK](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#how-sdk-transformation-works).
</Tip>

### Ordre des propriétés

Lorsque vous utilisez les sorties structurées, les propriétés des objets conservent l'ordre défini dans votre schéma, avec une mise en garde importante : **les propriétés obligatoires apparaissent en premier, suivies des propriétés optionnelles**.

Par exemple, avec ce schéma :

```json
{
  "type": "object",
  "properties": {
    "notes": { "type": "string" },
    "name": { "type": "string" },
    "email": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}
```

La sortie ordonnera les propriétés comme suit :

1. `name` (obligatoire, dans l'ordre du schéma)
2. `email` (obligatoire, dans l'ordre du schéma)
3. `notes` (optionnelle, dans l'ordre du schéma)
4. `age` (optionnelle, dans l'ordre du schéma)

Cela signifie que la sortie pourrait ressembler à ceci :

```json
{
  "name": "John Smith",
  "email": "john@example.com",
  "notes": "Interested in enterprise plan",
  "age": 35
}
```

Si l'ordre des propriétés dans la sortie est important pour votre application, marquez toutes les propriétés comme obligatoires, ou tenez compte de ce réordonnancement dans votre logique d'analyse.

### Sorties invalides

Bien que les sorties structurées garantissent la conformité au schéma dans la plupart des cas, il existe des scénarios où la sortie peut ne pas correspondre à votre schéma :

**Refus** (`stop_reason: "refusal"`)

Claude conserve ses propriétés de sécurité et d'utilité même lorsqu'il utilise les sorties structurées. Si Claude refuse une requête pour des raisons de sécurité :

* La réponse a `stop_reason: "refusal"`
* Vous recevrez un code de statut 200
* Les tokens générés vous seront facturés
* La sortie peut ne pas correspondre à votre schéma car le message de refus est prioritaire sur les contraintes du schéma

Une propriété qui demande la réflexion du modèle ou un raisonnement étape par étape peut entraîner un refus `reasoning_extraction`. Demandez plutôt une courte explication. Consultez [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).

**Limite de tokens atteinte** (`stop_reason: "max_tokens"`)

Si la réponse est tronquée parce que la limite `max_tokens` a été atteinte :

* La réponse a `stop_reason: "max_tokens"`
* La sortie peut être incomplète et ne pas correspondre à votre schéma
* Réessayez avec une valeur `max_tokens` plus élevée pour obtenir la sortie structurée complète

**Casse des valeurs enum**

Les sorties structurées ne garantissent pas la casse des valeurs `enum` et `const` de type chaîne : Claude peut renvoyer une valeur qui ne diffère de votre schéma que par la casse, généralement sur la première lettre d'un mot suivant une espace. Par exemple, avec ce schéma :

```json
{
  "type": "string",
  "enum": ["Conversation Topic 1", "Conversation Topic 2", "Conversation topic 3"]
}
```

La sortie peut contenir `"Conversation Topic 3"` (« T » majuscule) même si cette valeur exacte ne figure pas dans l'enum. La réponse se termine normalement, sans erreur ni `stop_reason` particulier. Cela s'applique aussi bien aux sorties JSON qu'à l'utilisation d'outils stricte. Comparez les valeurs enum sans tenir compte de la casse, et évitez les valeurs enum qui ne diffèrent que par la casse.

### Limites de complexité des schémas

Les sorties structurées fonctionnent en compilant vos schémas JSON en une grammaire qui contraint la sortie de Claude. Les schémas plus complexes produisent des grammaires plus volumineuses qui prennent plus de temps à compiler. Pour se protéger contre des temps de compilation excessifs, l'API impose plusieurs limites de complexité.

#### Limites explicites

Les limites suivantes s'appliquent à toutes les requêtes avec `output_config.format` ou `strict: true` :

| Limite                      | Valeur | Description                                                                                                                                                                                                                                  |
| --------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Outils stricts par requête  | 20     | Nombre maximal d'outils avec `strict: true`. Les outils non stricts ne comptent pas dans cette limite.                                                                                                                                       |
| Paramètres optionnels       | 24     | Total des paramètres optionnels sur l'ensemble des schémas d'outils stricts et des schémas de sortie JSON. Chaque paramètre non listé dans `required` compte dans cette limite.                                                              |
| Paramètres avec types union | 16     | Total des paramètres qui utilisent `anyOf` ou des tableaux de types (par exemple, `"type": ["string", "null"]`) sur l'ensemble des schémas stricts. Ceux-ci sont particulièrement coûteux car ils créent un coût de compilation exponentiel. |

<Note>
  Ces limites s'appliquent au total combiné de tous les schémas stricts d'une même requête. Par exemple, si vous avez 4 outils stricts avec 6 paramètres optionnels chacun, vous atteindrez la limite de 24 paramètres même si aucun outil ne semble complexe individuellement.
</Note>

#### Limites internes supplémentaires

Au-delà des limites explicites du tableau précédent, il existe des limites internes supplémentaires sur la taille de la grammaire compilée. Ces limites existent parce que la complexité d'un schéma ne se réduit pas à une seule dimension : des fonctionnalités comme les paramètres optionnels, les types union, les objets imbriqués et le nombre d'outils interagissent entre elles de manières qui peuvent rendre la grammaire compilée disproportionnément volumineuse.

Lorsque ces limites sont dépassées, vous recevrez une erreur 400 avec le message « Schema is too complex for compilation. » Ces erreurs signifient que la complexité combinée de vos schémas dépasse ce qui peut être compilé efficacement, même si chaque limite individuelle du tableau précédent est respectée. En dernier recours, l'API impose également un **délai d'expiration de compilation de 180 secondes**. Les schémas qui passent toutes les vérifications explicites mais produisent des grammaires compilées très volumineuses peuvent atteindre ce délai.

#### Conseils pour réduire la complexité des schémas

Si vous atteignez les limites de complexité, essayez ces stratégies dans l'ordre :

1. **Ne marquez comme stricts que les outils critiques.** Si vous avez de nombreux outils, réservez ce mode aux outils pour lesquels les violations de schéma causent de réels problèmes, et fiez-vous à l'adhérence naturelle de Claude pour les outils plus simples.

2. **Réduisez les paramètres optionnels.** Rendez les paramètres `required` lorsque c'est possible. Chaque paramètre optionnel double approximativement une partie de l'espace d'états de la grammaire. Si un paramètre a toujours une valeur par défaut raisonnable, envisagez de le rendre obligatoire et de demander à Claude de fournir explicitement cette valeur par défaut.

3. **Simplifiez les structures imbriquées.** Les objets profondément imbriqués avec des champs optionnels aggravent la complexité. Aplatissez les structures lorsque c'est possible.

4. **Répartissez sur plusieurs requêtes.** Si vous avez de nombreux outils stricts, envisagez de les répartir sur des requêtes ou des sous-agents distincts.

Pour les problèmes persistants avec des schémas valides, [contactez le support](https://support.claude.com/en/articles/9015913-how-to-get-support) avec la définition de votre schéma.

## Migration depuis la bêta

Le paramètre `output_format` a été déplacé vers `output_config.format`, et les en-têtes bêta ne sont plus nécessaires. Le paramètre `output_format` est déprécié et sera supprimé à l'avenir. Pour l'utiliser malgré tout, ajoutez l'en-tête bêta `structured-outputs-2025-11-13`. Sans celui-ci, l'API renvoie une erreur 400.

Le SDK Python (v1.0 et versions ultérieures) n'accepte pas `output_format={...}` sur `client.beta.messages.create()` ou `count_tokens()` et lève une `TypeError`. Utilisez plutôt `output_config`. Consultez [Utiliser un schéma JSON brut](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#using-a-raw-json-schema) pour la forme mise à jour de l'API.

## Conservation des données

Les prompts et les réponses sont traités avec ZDR lors de l'utilisation des sorties structurées. Cependant, le schéma JSON lui-même est temporairement mis en cache jusqu'à 24 heures après la dernière utilisation à des fins d'optimisation. Aucune donnée de prompt ou de réponse n'est conservée au-delà de la réponse de l'API.

Les sorties structurées sont éligibles HIPAA, mais **les PHI ne doivent pas être incluses dans les définitions de schémas JSON**. L'API compile les schémas JSON en grammaires qui sont mises en cache séparément du contenu des messages, et ces schémas mis en cache ne bénéficient pas des mêmes protections PHI que les prompts et les réponses. N'incluez pas de PHI dans les noms de propriétés de schéma, les valeurs `enum`, les valeurs `const` ou les expressions régulières `pattern`. Les PHI ne doivent apparaître que dans le contenu des messages (prompts et réponses), où elles sont protégées par les garanties HIPAA.

Pour l'éligibilité ZDR et HIPAA de toutes les fonctionnalités, consultez [API et conservation des données](https://platform.claude.com/docs/fr/manage-claude/api-and-data-retention).

## Compatibilité des fonctionnalités

**Fonctionne avec :**

* **[Traitement par lots](https://platform.claude.com/docs/fr/build-with-claude/batch-processing) :** traitez des sorties structurées à grande échelle avec une remise de 50 %
* **[Comptage de tokens](https://platform.claude.com/docs/fr/build-with-claude/token-counting) :** comptez les tokens sans compilation
* **[Streaming](https://platform.claude.com/docs/fr/build-with-claude/streaming) :** diffusez les sorties structurées en streaming comme des réponses normales
* **[Utilisation combinée](https://platform.claude.com/docs/fr/build-with-claude/structured-outputs#using-both-features-together) :** utilisez les sorties JSON (`output_config.format`) et l'utilisation d'outils stricte (`strict: true`) ensemble dans la même requête

**Incompatible avec :**

* **[Citations](https://platform.claude.com/docs/fr/build-with-claude/citations) :** les citations nécessitent d'entrelacer des blocs de citation avec du texte, ce qui entre en conflit avec les contraintes strictes de schéma JSON. Renvoie une erreur 400 si les citations sont activées avec `output_config.format`.
* **Préremplissage de message :** incompatible avec les sorties JSON

<Tip>
  **Portée de la grammaire :** les grammaires s'appliquent uniquement à la sortie directe de Claude, et non aux appels d'utilisation d'outils, aux résultats d'outils ni aux balises de réflexion (lors de l'utilisation de la [réflexion](https://platform.claude.com/docs/fr/build-with-claude/thinking)). L'état de la grammaire est réinitialisé entre les sections, ce qui permet à Claude de réfléchir librement tout en produisant une sortie structurée dans la réponse finale.
</Tip>

## Étapes suivantes

<CardGroup cols={2}>
  <Card title="Citations" icon="book-bookmark" href="https://platform.claude.com/docs/fr/build-with-claude/citations">
    Demandez à Claude de citer ses sources lorsqu'il répond à des questions sur les documents fournis.
  </Card>

  <Card title="Utilisation d'outils stricte" icon="check" href="https://platform.claude.com/docs/fr/agents-and-tools/tool-use/strict-tool-use">
    Imposez la conformité à JSON Schema sur les entrées d'outils de Claude avec un échantillonnage contraint par grammaire.
  </Card>

  <Card title="Utilisation d'outils avec Claude" icon="wrench" href="https://platform.claude.com/docs/fr/agents-and-tools/tool-use/overview">
    Connectez Claude à des outils et API externes. Découvrez où les outils s'exécutent et comment fonctionne la boucle agentique.
  </Card>

  <Card title="Tarification" icon="calculator" href="https://platform.claude.com/docs/fr/about-claude/pricing">
    Découvrez la structure tarifaire d'Anthropic pour les modèles et les fonctionnalités.
  </Card>
</CardGroup>
