---
title: Output strutturati
url: https://platform.claude.com/docs/it/build-with-claude/structured-outputs
description: Ottieni risultati JSON validati dai flussi di lavoro degli agenti
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: Su Amazon Bedrock, gli output strutturati sono disponibili sull'integrazione legacy [Amazon Bedrock (Opus 4.6 e precedenti)](https://platform.claude.com/docs/it/build-with-claude/claude-on-amazon-bedrock-legacy) per Claude Opus 4.6, Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Opus 4.5 e Claude Haiku 4.5, e non su [Claude in Amazon Bedrock](https://platform.claude.com/docs/it/build-with-claude/claude-in-amazon-bedrock).
    Google Cloud: ga
    Microsoft Foundry: ga
---

Gli "structured outputs" (output strutturati) vincolano le risposte di Claude a seguire uno schema specifico, garantendo un output valido e analizzabile per l'elaborazione a valle. Gli output strutturati forniscono due funzionalità complementari:

* **Output JSON** (`output_config.format`): ottieni la risposta di Claude in un formato JSON specifico, ad esempio per estrarre dati da immagini o testo, generare report strutturati o formattare risposte API. Questa pagina tratta gli output JSON.
* **"Strict tool use" (uso rigoroso degli strumenti)** (`strict: true`): garantisci la convalida dello schema sui nomi e sugli input degli strumenti. Consulta [Uso rigoroso degli strumenti](https://platform.claude.com/docs/it/agents-and-tools/tool-use/strict-tool-use).

Puoi usare queste funzionalità in modo indipendente oppure [insieme nella stessa richiesta](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#using-both-features-together).

## Perché usare gli output strutturati

Senza output strutturati, Claude può generare risposte JSON malformate o input di strumenti non validi che compromettono le tue applicazioni. Anche con un prompting accurato, potresti incontrare:

* Errori di parsing dovuti a sintassi JSON non valida
* Campi obbligatori mancanti
* Tipi di dati incoerenti
* Violazioni dello schema che richiedono gestione degli errori e nuovi tentativi

Gli output strutturati garantiscono risposte conformi allo schema tramite decodifica vincolata:

* **Sempre validi:** niente più errori di `JSON.parse()`
* **Type safe:** tipi di campo e campi obbligatori garantiti
* **Affidabili:** nessun nuovo tentativo necessario per violazioni dello schema

## Come funziona

<Steps>
  <Step title="Definisci il tuo schema">
    Descrivi la struttura che desideri come schema JSON o come tipo nel tuo linguaggio. Lo schema segue JSON Schema, con alcune [limitazioni](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#json-schema-limitations).
  </Step>

  <Step title="Invialo in output_config.format">
    La richiesta trasporta lo schema in `output_config.format` con `type: "json_schema"`. Gli helper dell'SDK lo impostano per te.
  </Step>

  <Step title="Leggi la risposta">
    Claude restituisce JSON valido che corrisponde al tuo schema nel blocco di contenuto testuale della risposta. Gli helper dell'SDK lo analizzano e lo convertono nel tuo tipo.
  </Step>
</Steps>

## Utilizzo

<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 restituisce JSON come questo nel blocco di contenuto testuale della risposta:

    ```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
    ```

    Per stampare solo alcune proprietà dell'output JSON, passa un [percorso GJSON](https://platform.claude.com/docs/it/cli-sdks-libraries/cli/using#transform-output-with-gjson) a `--transform`.

    L'esempio sopra produce:

    ```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)
    ```

    Passa un modello [Pydantic](https://docs.pydantic.dev/) a `client.messages.parse()`, il metodo consigliato, come `output_format`. L'SDK trasforma lo schema del modello, lo invia come `output_config.format`, convalida la risposta e restituisce il modello analizzato in `parsed_output`.

    L'esempio sopra produce:

    ```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) }
    });

    // Analizzato e convalidato automaticamente
    console.log(response.parsed_output);
    ```

    Racchiudi uno schema [Zod](https://zod.dev/) in `zodOutputFormat()` e passalo a `client.messages.parse()` come `output_config.format`. `zodOutputFormat()` trasforma lo schema, e `parse()` convalida la risposta e restituisce il risultato analizzato in `parsed_output`, tipizzato in modo da corrispondere allo schema.

    L'esempio sopra produce:

    ```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; }
    }
    ```

    Passa una semplice classe o record C# all'overload generico `Create<T>()`. L'SDK deriva uno schema JSON dal tipo, lo trasforma e analizza ogni blocco di testo riconvertendolo in quel tipo.

    L'esempio sopra produce:

    ```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>
      Passare una struct come schema è in beta in Go, quindi questo esempio usa `client.Beta.Messages.New`. Per usare la versione GA `client.Messages.New`, passa uno schema JSON come mostrato in [Usare uno schema JSON grezzo](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#using-a-raw-json-schema).
    </Note>

    L'SDK genera e trasforma lo schema JSON dalla struct ed esegue l'unmarshal della risposta al suo interno.

    L'esempio sopra produce:

    ```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));
    }
    ```

    Quando passi una classe Java a `outputConfig()`, l'SDK ne deriva uno schema JSON e convalida lo schema. Il builder produce quindi un `StructuredMessageCreateParams<T>`. Invece di trasformare lo schema, la convalida locale dell'SDK rifiuta i [vincoli non supportati dall'API](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#json-schema-limitations). Leggi il risultato analizzato con `response.content().stream().flatMap(block -> block.text().stream()).findFirst().orElseThrow().text()`.

    L'esempio sopra produce:

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

    <Note>
      Dichiara le tue classi di schema come classi di primo livello o classi annidate `static`. Questo requisito deriva dalla libreria Jackson Databind (`com.fasterxml.jackson.databind`), che l'SDK usa per deserializzare le risposte JSON in istanze delle tue classi e che non può istanziare classi interne non statiche.
    </Note>

    <Accordion title="Cancellazione dei tipi generici">
      Java conserva le informazioni sui tipi generici per i campi nei metadati della classe, ma la cancellazione dei tipi generici si applica in altri ambiti. Mentre uno schema JSON può essere derivato da un campo `BookList.books` con tipo `List<Book>`, uno schema JSON valido non può essere derivato da una variabile locale dello stesso tipo.

      Se si verifica un errore durante la conversione di una risposta JSON in un'istanza di classe Java, il messaggio di errore include la risposta JSON per facilitare la diagnosi. Se la tua risposta JSON può contenere informazioni sensibili, evita di registrarla direttamente nei log, oppure assicurati di oscurare eventuali dettagli sensibili dal messaggio di errore.
    </Accordion>

    <Accordion title="Validazione locale dello schema">
      Gli output strutturati supportano un [sottoinsieme del linguaggio JSON Schema](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#json-schema-limitations). L'SDK genera automaticamente gli schemi dalle classi in modo da allinearsi a questo sottoinsieme. Il metodo `outputConfig(Class<T>)` esegue un controllo di validazione sullo schema derivato dalla classe specificata.

      Punti chiave:

      * La **validazione locale** avviene senza inviare richieste al modello di IA remoto.
      * La **validazione remota** viene eseguita anche dal modello di IA alla ricezione dello schema JSON.
      * **Compatibilità delle versioni:** la validazione locale può fallire mentre quella remota ha successo se la versione dell'SDK è obsoleta.
      * **Disabilitare la validazione locale:** passa `JsonSchemaLocalValidation.NO` se riscontri problemi di 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">
      Gli output strutturati funzionano anche con lo streaming. Poiché le risposte arrivano in eventi di stream, devi accumulare la risposta completa prima di deserializzare il JSON.

      Usa `MessageAccumulator` per raccogliere le stringhe JSON dallo stream. Una volta accumulate, chiama `MessageAccumulator.message(Class<T>)` per convertire il `Message` accumulato in uno `StructuredMessage`, che deserializza automaticamente il JSON nella tua classe Java.
    </Accordion>

    <Accordion title="Proprietà dello schema JSON">
      Quando l'SDK deriva uno schema JSON dalle tue classi Java, include per impostazione predefinita tutte le proprietà rappresentate da campi `public` o metodi getter `public` ed esclude i campi e i metodi getter non `public`.

      Puoi controllare la visibilità con le annotazioni:

      * `@JsonIgnore` esclude un campo o metodo getter `public`
      * `@JsonProperty` include un campo o metodo getter non `public`

      Se definisci campi `private` con metodi getter `public`, l'SDK deriva il nome della proprietà dal getter (ad esempio, il campo `private` `myValue` con il metodo `public` `getMyValue()` produce una proprietà `"myValue"`). Per usare un nome di getter non convenzionale, annota il metodo con `@JsonProperty`.

      Ogni classe deve definire almeno una proprietà per lo schema JSON. Si verifica un errore di validazione se nessun campo o metodo getter può produrre proprietà dello schema, ad esempio quando:

      * Non ci sono campi o metodi getter nella classe
      * Tutti i membri `public` sono annotati con `@JsonIgnore`
      * Tutti i membri non `public` sono privi di annotazioni `@JsonProperty`
      * Un campo usa un tipo `Map`, che produce un campo `"properties"` vuoto
    </Accordion>

    <Accordion title="Composizione ed ereditarietà">
      Le tue classi Java possono usare composizione ed ereditarietà per condividere la struttura nella definizione degli schemi JSON. Ogni pattern influisce in modo diverso sulla struttura dell'output.

      La **composizione** produce un output JSON annidato. Derivando uno schema dalla classe `Composed` che compone `A` e `B`:

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

      static class B {
          public String b;
      }

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

      L'output JSON ha questa struttura annidata:

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

      L'**ereditarietà** produce un output JSON piatto. Derivando uno schema dalla classe `Derived` che estende `Base`:

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

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

      L'output JSON ha questa struttura piatta:

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

    <Accordion title="Annotazioni (Jackson e Swagger)">
      Puoi usare le annotazioni Jackson Databind per arricchire lo schema JSON derivato dalle tue classi 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;
      }
      ```

      Riepilogo delle annotazioni:

      * `@JsonClassDescription`: aggiunge una descrizione a una classe
      * `@JsonPropertyDescription`: aggiunge una descrizione a un campo o metodo getter
      * `@JsonIgnore`: esclude un campo o getter `public` dallo schema
      * `@JsonProperty`: include un campo o getter non `public` nello schema

      Se usi `@JsonProperty(required = false)`, l'SDK ignora il valore `false`. Gli schemi derivati da classi contrassegnano sempre tutte le proprietà come obbligatorie.

      Puoi anche usare le annotazioni `@Schema` e `@ArraySchema` di Swagger Core (OpenAPI 3) per vincoli specifici del tipo:

      ```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 validazione locale verifica che tu non abbia usato parole chiave di vincolo non supportate, ma i valori dei vincoli non vengono validati localmente. Ad esempio, un valore `"format"` non supportato può superare la validazione locale ma causare un errore remoto.

      Se usi sia le annotazioni Jackson che quelle Swagger per impostare lo stesso campo dello schema, l'annotazione Jackson ha la precedenza.
    </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);
    }
    ```

    Definisci una classe che implementa `StructuredOutputModel` e usa `StructuredOutputModelTrait`, e passa il suo nome in `outputConfig: ['format' => MyClass::class]`. L'SDK deriva uno schema JSON dai tipi delle proprietà PHP 8 della classe, lo trasforma e restituisce un'istanza tipizzata da `$message->parsedOutput()`.

    `parsedOutput()` restituisce l'istanza del tuo modello in caso di successo, oppure `null` (o un array di errori) se il parsing fallisce. Usa `instanceof` per restringere il tipo prima di accedere ai campi.

    L'esempio sopra produce:

    ```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="Inferenza dei tipi">
      L'SDK mappa i tipi di proprietà nativi di PHP 8 su JSON Schema:

      | Tipo PHP                                      | JSON Schema                       |
      | --------------------------------------------- | --------------------------------- |
      | `string`                                      | `"string"`                        |
      | `int`                                         | `"integer"`                       |
      | `float`                                       | `"number"`                        |
      | `bool`                                        | `"boolean"`                       |
      | `array`                                       | `"array"` (vedi la nota seguente) |
      | `?type` (nullable)                            | Campo opzionale                   |
      | Classe che implementa `StructuredOutputModel` | Oggetto annidato                  |

      Per le proprietà `array`, l'SDK aggiunge uno schema `items` solo quando il tipo degli elementi è un `StructuredOutputModel` annidato, dichiarato con `#[Constrained(itemClass: MyModel::class)]` o con un docblock `/** @var MyModel[] */`. Gli array di scalari (`string[]`, `int[]`) emettono un `{"type":"array"}` non vincolato.

      Tutte le proprietà non nullable diventano campi obbligatori.
    </Accordion>

    <Accordion title="Vincoli con l'attributo #[Constrained]">
      Aggiungi vincoli con l'attributo `#[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;
      }
      ```

      **Vincoli applicati dall'API** (inviati nello schema): `description`, `format`, `const`, `itemClass`, `minItems` (solo 0 o 1).

      **Vincoli validati dall'SDK** (rimossi dallo schema inviato, aggiunti alla descrizione e validati rispetto alla risposta): `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
    ```

    Definisci una classe che estende `Anthropic::BaseModel` e passala a `messages.create()` come `output_config: {format: Model}`. L'SDK deriva uno schema JSON dalla classe, lo trasforma e assegna il risultato analizzato all'attributo `parsed_output` della risposta come oggetto Ruby tipizzato.

    L'esempio sopra produce:

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

    <Accordion title="Funzionalità avanzate dei modelli">
      Il Ruby SDK supporta funzionalità aggiuntive di definizione dei modelli per schemi più ricchi:

      * **Parola chiave `doc:`:** aggiungi descrizioni ai campi per un output dello schema più informativo
      * **`Anthropic::ArrayOf[T]`:** array tipizzati. Passa i vincoli a livello di array (`min_items:`, `max_items:`) come parole chiave su `required`/`optional`, non su `ArrayOf` stesso
      * **`Anthropic::EnumOf[:a, :b]`:** campi enum con valori vincolati
      * **`Anthropic::UnionOf[T1, T2]`:** tipi unione mappati su `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 greco è...">...]>
      ```
    </Accordion>
  </Tab>
</Tabs>

## Come funziona la trasformazione dell'SDK

La maggior parte degli helper dell'SDK trasforma gli schemi che usano funzionalità non supportate. I passaggi della trasformazione:

1. **Rimuove i vincoli non supportati** (ad esempio, `minimum`, `maximum`, `minLength`, `maxLength`)
2. **Aggiorna le descrizioni** aggiungendo ciascun vincolo non supportato alla descrizione del campo (ad esempio, `{minimum: 100}`)
3. **Aggiunge `additionalProperties: false`** a tutti gli oggetti
4. **Filtra i formati delle stringhe** limitandoli all'elenco di quelli supportati
5. **Convalida le risposte** rispetto al tuo schema originale e a tutti i suoi vincoli, se l'helper convalida le risposte

Claude riceve uno schema semplificato, ma un helper che convalida le risposte applica comunque ogni vincolo nel tuo codice.

**Esempio:** un campo con `minimum: 100` diventa un semplice intero nello schema inviato, e l'SDK aggiunge `{minimum: 100}` alla descrizione del campo. Un helper che convalida le risposte verifica comunque la risposta rispetto a `minimum: 100`.

## Usare uno schema JSON grezzo

Per usare uno schema JSON proveniente da un file, da una specifica OpenAPI o da codice che lo costruisce in fase di esecuzione, passalo in `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 restituisce JSON come questo nel blocco di contenuto testuale della risposta:

    ```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'esempio sopra produce:

    ```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'esempio sopra produce:

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

    Per spostare i [vincoli non supportati dall'API](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#json-schema-limitations) nelle descrizioni dei campi, passa lo schema attraverso `transform_schema()` del pacchetto `anthropic` prima di inviarlo, e modifica il risultato se necessario. `transform_schema()` accetta anche un modello Pydantic. A differenza di `client.messages.parse()`, restituisce lo schema trasformato invece di inviarlo.
  </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 è tipizzato come { name: string; email: string; planInterest: string } | null
    console.log(response.parsed_output);
    ```

    Usa `jsonSchemaOutputFormat()` per passare un semplice schema JSON a `parse()`, senza installare Zod. L'API restituisce una risposta conforme allo schema e, se dichiari lo schema con `as const`, il tipo di `parsed_output` viene inferito automaticamente da TypeScript in base al tuo schema. Per uno schema importato o generato, `parsed_output` è tipizzato come `unknown`.

    Per impostazione predefinita, l'helper [trasforma lo schema](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#how-sdk-transformation-works) nello stesso modo di `zodOutputFormat()`. Passa `{ transform: false }` come secondo argomento per inviarlo invariato. A differenza di `zodOutputFormat()`, non convalida la risposta, quindi non rileverà le violazioni dei [vincoli non supportati dall'API](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#json-schema-limitations), come `minimum`.

    L'esempio sopra produce:

    ```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)
    {
        // Il JSON è garantito conforme allo schema
        var contact = JsonSerializer.Deserialize<JsonElement>(textBlock.Text);
        Console.WriteLine(contact);
    }
    ```

    L'esempio sopra produce:

    ```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'esempio sopra produce:

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

    Per fare in modo che l'SDK trasformi uno schema di tipo map, passalo attraverso `BetaJSONSchemaOutputFormat()` sull'API beta.
  </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'esempio sopra produce:

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

    Per un esempio più completo che costruisce uno schema annidato con array e descrizioni, consulta [`StructuredOutputsRawExample.java`](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/StructuredOutputsRawExample.java) nel repository dell'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);
    ```

    Passa lo schema come array associativo a `OutputConfig::with()` e decodifica la risposta con `json_decode()`.

    L'esempio sopra produce:

    ```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'esempio sopra produce:

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

## Casi d'uso comuni

<AccordionGroup>
  <Accordion title="Estrazione di dati">
    Estrai dati strutturati da testo non strutturato:

    <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="Classificazione">
    Classifica i contenuti con categorie strutturate:

    <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="Formattazione delle risposte API">
    Genera risposte pronte per le 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>

## Uso rigoroso degli strumenti

Per imporre la conformità a JSON Schema sugli input degli strumenti con campionamento vincolato da grammatica, vedi [Uso rigoroso degli strumenti](https://platform.claude.com/docs/it/agents-and-tools/tool-use/strict-tool-use).

### Usare entrambe le funzionalità insieme

Gli output JSON e l'uso rigoroso degli strumenti risolvono problemi diversi e funzionano insieme:

* Gli **output JSON** controllano il formato di risposta di Claude (cosa dice Claude)
* L'**uso rigoroso degli strumenti** valida i parametri degli strumenti (come Claude chiama le tue funzioni)

Quando combinati, Claude può chiamare strumenti con parametri garantiti validi E restituire risposte JSON strutturate. Questo è utile per i flussi di lavoro agentici in cui hai bisogno sia di chiamate agli strumenti affidabili sia di output finali strutturati.

<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
  # Output JSON: formato di risposta strutturato
  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
  # Uso degli strumenti rigoroso: parametri degli strumenti garantiti
  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",
          }
      ],
      # Output JSON: formato di risposta strutturato
      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,
              },
          }
      },
      # Uso degli strumenti rigoroso: parametri degli strumenti garantiti
      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" }],
    // Output JSON: formato di risposta strutturato
    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
        }
      }
    },
    // Uso degli strumenti rigoroso: parametri degli strumenti garantiti
    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 può prima chiamare lo strumento (tool_use) o rispondere con JSON (text)
  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" }],
      // Output JSON: formato di risposta strutturato
      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),
              },
          },
      },
      // Uso degli strumenti rigoroso: parametri degli strumenti garantiti
      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")),
  	},
  	// Output JSON: formato di risposta strutturato
  	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"},
  			},
  		},
  	},
  	// Uso degli strumenti rigoroso: parametri degli strumenti garantiti
  	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();

  // Output JSON: formato di risposta strutturato
  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();

  // Uso degli strumenti rigoroso: parametri degli strumenti garantiti
  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',
      // Output JSON: formato di risposta strutturato
      outputConfig: ['format' => TripPlan::class],
      // Uso degli strumenti rigoroso: parametri degli strumenti garantiti
      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 può chiamare prima lo strumento (tool_use) o rispondere con JSON (testo)
  $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"}
    ],
    # Output JSON: formato di risposta strutturato
    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
        }
      }
    },
    # Uso degli strumenti rigoroso: parametri degli strumenti garantiti
    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>

## Considerazioni importanti

### Compilazione e cache delle grammatiche

Gli output strutturati usano il campionamento vincolato con artefatti di grammatica compilati. Questo introduce alcune caratteristiche prestazionali di cui essere consapevoli:

* **Latenza della prima richiesta:** la prima volta che usi uno schema specifico, c'è una "latency" (latenza) aggiuntiva mentre la grammatica viene compilata

* **Cache automatica:** le grammatiche compilate vengono memorizzate nella cache per 24 ore dall'ultimo utilizzo, rendendo le richieste successive molto più veloci

* **Invalidazione della cache:** la cache viene invalidata se modifichi:

  * La struttura dello schema JSON
  * L'insieme degli strumenti nella tua richiesta (quando usi sia gli output strutturati sia l'uso degli strumenti)
  * Modificare solo i campi `name` o `description` non invalida la cache

### Modifica del prompt e costi dei token

Quando usi gli output strutturati, Claude riceve automaticamente un "system prompt" (prompt di sistema) aggiuntivo che spiega il formato di output previsto. Questo significa che:

* Il conteggio dei tuoi token di input è leggermente più alto
* Il prompt iniettato ti costa token come qualsiasi altro prompt di sistema
* Modificare il parametro `output_config.format` invaliderà qualsiasi [cache dei prompt](https://platform.claude.com/docs/it/build-with-claude/prompt-caching) per quel thread di conversazione

### Limitazioni di JSON Schema

Gli output strutturati supportano JSON Schema standard con alcune limitazioni. Sia gli output JSON sia l'uso rigoroso degli strumenti condividono queste limitazioni.

<Accordion title="Funzionalità supportate">
  * Tutti i tipi di base: object, array, string, integer, number, boolean, null
  * `enum` (solo stringhe, numeri, booleani o null - nessun tipo complesso). Per un'avvertenza sulle maiuscole, consulta [Output non validi](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#invalid-outputs).
  * `const`
  * `anyOf` e `allOf` (con limitazioni - `allOf` con `$ref` non supportato)
  * `$ref`, `$def` e `definitions` (`$ref` esterni non supportati)
  * Proprietà `default` per tutti i tipi supportati
  * `required` e `additionalProperties` (deve essere impostato su `false` per gli oggetti)
  * Formati di stringa: `date-time`, `time`, `date`, `duration`, `email`, `hostname`, `uri`, `ipv4`, `ipv6`, `uuid`
  * `minItems` per gli array (supportati solo i valori 0 e 1)
</Accordion>

<Accordion title="Non supportato">
  * Schemi ricorsivi
  * Tipi complessi all'interno degli enum
  * `$ref` esterni (ad esempio, `'$ref': 'http://...'`)
  * Vincoli numerici (come `minimum`, `maximum`, `multipleOf`)
  * Vincoli sulle stringhe (`minLength`, `maxLength`)
  * Vincoli sugli array oltre `minItems` pari a 0 o 1
  * `additionalProperties` impostato su qualsiasi valore diverso da `false`

  Se usi una funzionalità non supportata, riceverai un errore 400 con i dettagli.
</Accordion>

<Accordion title="Supporto dei pattern (regex)">
  **Funzionalità regex supportate:**

  * Corrispondenza completa (`^...$`) e corrispondenza parziale
  * Quantificatori: `*`, `+`, `?`, casi semplici di `{n,m}`
  * Classi di caratteri: `[]`, `.`, `\d`, `\w`, `\s`
  * Gruppi: `(...)`

  **NON supportate:**

  * Backreference ai gruppi (ad esempio, `\1`, `\2`)
  * Asserzioni lookahead/lookbehind (ad esempio, `(?=...)`, `(?!...)`)
  * Confini di parola: `\b`, `\B`
  * Quantificatori `{n,m}` complessi con intervalli ampi

  I pattern regex semplici funzionano bene. I pattern complessi possono causare errori 400.
</Accordion>

<Tip>
  Gli helper dell'SDK possono trasformare gli schemi con [vincoli non supportati dall'API](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#json-schema-limitations). Consulta [Come funziona la trasformazione dell'SDK](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#how-sdk-transformation-works).
</Tip>

### Ordinamento delle proprietà

Quando usi gli output strutturati, le proprietà negli oggetti mantengono l'ordinamento definito nel tuo schema, con un'importante avvertenza: **le proprietà obbligatorie appaiono per prime, seguite dalle proprietà opzionali**.

Ad esempio, dato questo schema:

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

L'output ordinerà le proprietà come segue:

1. `name` (obbligatoria, nell'ordine dello schema)
2. `email` (obbligatoria, nell'ordine dello schema)
3. `notes` (opzionale, nell'ordine dello schema)
4. `age` (opzionale, nell'ordine dello schema)

Questo significa che l'output potrebbe apparire così:

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

Se l'ordine delle proprietà nell'output è importante per la tua applicazione, contrassegna tutte le proprietà come obbligatorie, oppure tieni conto di questo riordinamento nella tua logica di parsing.

### Output non validi

Sebbene gli output strutturati garantiscano la conformità allo schema nella maggior parte dei casi, esistono scenari in cui l'output potrebbe non corrispondere al tuo schema:

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

Claude mantiene le sue proprietà di sicurezza e utilità anche quando usa gli output strutturati. Se Claude rifiuta una richiesta per motivi di sicurezza:

* La risposta ha `stop_reason: "refusal"`
* Riceverai un codice di stato 200
* Ti verranno addebitati i token generati
* L'output potrebbe non corrispondere al tuo schema perché il messaggio di rifiuto ha la precedenza sui vincoli dello schema

Una proprietà che richiede il "thinking" (ragionamento) del modello o un ragionamento passo dopo passo può portare a un rifiuto `reasoning_extraction`. Chiedi invece una breve spiegazione. Consulta [Mantieni il ragionamento nei blocchi di ragionamento](https://platform.claude.com/docs/it/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks).

**Limite di token raggiunto** (`stop_reason: "max_tokens"`)

Se la risposta viene troncata per aver raggiunto il limite `max_tokens`:

* La risposta ha `stop_reason: "max_tokens"`
* L'output potrebbe essere incompleto e non corrispondere al tuo schema
* Riprova con un valore `max_tokens` più alto per ottenere l'output strutturato completo

**Maiuscole e minuscole nei valori enum**

Gli output strutturati non garantiscono l'uso delle maiuscole nei valori `enum` e `const` di tipo stringa: Claude può restituire un valore che differisce dal tuo schema solo per le maiuscole, tipicamente nella prima lettera di una parola che segue uno spazio. Ad esempio, dato questo schema:

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

L'output può contenere `"Conversation Topic 3"` ("T" maiuscola) anche se quel valore esatto non è nell'enum. La risposta si completa normalmente, senza errori e senza uno `stop_reason` speciale. Questo vale sia per gli output JSON sia per l'uso rigoroso degli strumenti. Confronta i valori enum senza distinzione tra maiuscole e minuscole ed evita valori enum che differiscono solo per le maiuscole.

### Limiti di complessità dello schema

Gli output strutturati funzionano compilando i tuoi schemi JSON in una grammatica che vincola l'output di Claude. Schemi più complessi producono grammatiche più grandi che richiedono più tempo per essere compilate. Per proteggersi da tempi di compilazione eccessivi, l'API applica diversi limiti di complessità.

#### Limiti espliciti

I seguenti limiti si applicano a tutte le richieste con `output_config.format` o `strict: true`:

| Limite                         | Valore | Descrizione                                                                                                                                                                                                            |
| ------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Strumenti strict per richiesta | 20     | Numero massimo di strumenti con `strict: true`. Gli strumenti non strict non contano ai fini di questo limite.                                                                                                         |
| Parametri opzionali            | 24     | Totale dei parametri opzionali in tutti gli schemi di strumenti strict e gli schemi di output JSON. Ogni parametro non elencato in `required` conta ai fini di questo limite.                                          |
| Parametri con tipi unione      | 16     | Totale dei parametri che usano `anyOf` o array di tipi (ad esempio, `"type": ["string", "null"]`) in tutti gli schemi strict. Questi sono particolarmente costosi perché creano un costo di compilazione esponenziale. |

<Note>
  Questi limiti si applicano al totale combinato di tutti gli schemi strict in una singola richiesta. Ad esempio, se hai 4 strumenti strict con 6 parametri opzionali ciascuno, raggiungerai il limite di 24 parametri anche se nessun singolo strumento sembra complesso.
</Note>

#### Limiti interni aggiuntivi

Oltre ai limiti espliciti nella tabella precedente, esistono limiti interni aggiuntivi sulla dimensione della grammatica compilata. Questi limiti esistono perché la complessità dello schema non si riduce a una singola dimensione: funzionalità come parametri opzionali, tipi unione, oggetti annidati e numero di strumenti interagiscono tra loro in modi che possono rendere la grammatica compilata sproporzionatamente grande.

Quando questi limiti vengono superati, riceverai un errore 400 con il messaggio "Schema is too complex for compilation." Questi errori significano che la complessità combinata dei tuoi schemi supera ciò che può essere compilato in modo efficiente, anche se ogni singolo limite nella tabella precedente è rispettato. Come ultima misura di salvaguardia, l'API applica anche un **timeout di compilazione di 180 secondi**. Gli schemi che superano tutti i controlli espliciti ma producono grammatiche compilate molto grandi possono raggiungere questo timeout.

#### Suggerimenti per ridurre la complessità dello schema

Se stai raggiungendo i limiti di complessità, prova queste strategie nell'ordine:

1. **Contrassegna come strict solo gli strumenti critici.** Se hai molti strumenti, riservalo agli strumenti in cui le violazioni dello schema causano problemi reali e affidati all'aderenza naturale di Claude per gli strumenti più semplici.

2. **Riduci i parametri opzionali.** Rendi i parametri `required` dove possibile. Ogni parametro opzionale raddoppia approssimativamente una porzione dello spazio degli stati della grammatica. Se un parametro ha sempre un valore predefinito ragionevole, considera di renderlo obbligatorio e di far fornire esplicitamente a Claude quel valore predefinito.

3. **Semplifica le strutture annidate.** Gli oggetti profondamente annidati con campi opzionali aumentano la complessità. Appiattisci le strutture dove possibile.

4. **Suddividi in più richieste.** Se hai molti strumenti strict, considera di suddividerli in richieste separate o sotto-agenti.

Per problemi persistenti con schemi validi, [contatta il supporto](https://support.claude.com/en/articles/9015913-how-to-get-support) con la definizione del tuo schema.

## Migrazione dalla beta

Il parametro `output_format` è stato spostato in `output_config.format` e gli header beta non sono più necessari. Il parametro `output_format` è deprecato e verrà rimosso in futuro. Per usarlo comunque, aggiungi l'header beta `structured-outputs-2025-11-13`. Senza di esso, l'API restituisce un errore 400.

L'SDK Python (v1.0 e successive) non accetta `output_format={...}` su `client.beta.messages.create()` o `count_tokens()` e solleva un `TypeError`. Usa invece `output_config`. Consulta [Usare uno schema JSON grezzo](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#using-a-raw-json-schema) per la forma aggiornata dell'API.

## Conservazione dei dati

I prompt e le risposte vengono elaborati con ZDR quando si usano gli output strutturati. Tuttavia, lo schema JSON stesso viene temporaneamente memorizzato nella cache per un massimo di 24 ore dall'ultimo utilizzo a fini di ottimizzazione. Nessun dato di prompt o risposta viene conservato oltre la risposta dell'API.

Gli output strutturati sono idonei per HIPAA, ma **le PHI non devono essere incluse nelle definizioni degli schemi JSON**. L'API compila gli schemi JSON in grammatiche che vengono memorizzate nella cache separatamente dal contenuto dei messaggi, e questi schemi in cache non ricevono le stesse protezioni PHI dei prompt e delle risposte. Non includere PHI nei nomi delle proprietà dello schema, nei valori `enum`, nei valori `const` o nelle espressioni regolari `pattern`. Le PHI dovrebbero apparire solo nel contenuto dei messaggi (prompt e risposte), dove sono protette dalle garanzie HIPAA.

Per l'idoneità ZDR e HIPAA di tutte le funzionalità, vedi [API e conservazione dei dati](https://platform.claude.com/docs/it/manage-claude/api-and-data-retention).

## Compatibilità delle funzionalità

**Funziona con:**

* **[Elaborazione batch](https://platform.claude.com/docs/it/build-with-claude/batch-processing):** elabora output strutturati su larga scala con uno sconto del 50%
* **[Conteggio dei token](https://platform.claude.com/docs/it/build-with-claude/token-counting):** conta i token senza compilazione
* **[Streaming](https://platform.claude.com/docs/it/build-with-claude/streaming):** trasmetti in streaming gli output strutturati come le normali risposte
* **[Uso combinato](https://platform.claude.com/docs/it/build-with-claude/structured-outputs#using-both-features-together):** usa gli output JSON (`output_config.format`) e l'uso rigoroso degli strumenti (`strict: true`) insieme nella stessa richiesta

**Incompatibile con:**

* **[Citazioni](https://platform.claude.com/docs/it/build-with-claude/citations):** le citazioni richiedono l'alternanza di blocchi di citazione con il testo, il che è in conflitto con i vincoli rigorosi dello schema JSON. Restituisce un errore 400 se le citazioni sono abilitate con `output_config.format`.
* **Precompilazione dei messaggi:** incompatibile con gli output JSON

<Tip>
  **Ambito della grammatica:** le grammatiche si applicano solo all'output diretto di Claude, non alle chiamate di uso degli strumenti, ai risultati degli strumenti o ai tag di ragionamento (quando si usa il [ragionamento](https://platform.claude.com/docs/it/build-with-claude/thinking)). Lo stato della grammatica si reimposta tra le sezioni, consentendo a Claude di pensare liberamente pur producendo un output strutturato nella risposta finale.
</Tip>

## Passaggi successivi

<CardGroup cols={2}>
  <Card title="Citazioni" icon="book-bookmark" href="https://platform.claude.com/docs/it/build-with-claude/citations">
    Fai in modo che Claude citi le sue fonti quando risponde a domande sui documenti forniti.
  </Card>

  <Card title="Uso rigoroso degli strumenti" icon="check" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/strict-tool-use">
    Imponi la conformità a JSON Schema sugli input degli strumenti di Claude con campionamento vincolato da grammatica.
  </Card>

  <Card title="Uso degli strumenti con Claude" icon="wrench" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/overview">
    Collega Claude a strumenti e API esterni. Scopri dove vengono eseguiti gli strumenti e come funziona il ciclo agentico.
  </Card>

  <Card title="Prezzi" icon="calculator" href="https://platform.claude.com/docs/it/about-claude/pricing">
    Scopri la struttura dei prezzi di Anthropic per modelli e funzionalità.
  </Card>
</CardGroup>
