Au 22/10/2026, les notes de version de la plateforme Claude placent la version d'API 2026-10-21 au 21/10/2026. Le SDK Python 2.0.0 et le SDK TypeScript 1.0.0 envoient cet en-tête par défaut. Les sorties structurées sont en GA : le schéma va dans output_config.format à la racine de la requête, type vaut json_schema, et le contrat réel est l'objet schema. L'ancien output_format sans en-tête beta renvoie 400. Pourquoi valider encore en local : pourquoi l'IA aime JSON. La ligne Bedrock : quel bloc text lire pour Haiku 5.5.
Le client Messages envoie désormais anthropic-version: 2026-10-21, et l'extraction de ticket renvoie 400. Trois causes fréquentes : output_format est encore à la racine ; temperature reste là pour « figer le JSON » ; le schéma contient minimum. Les trois collent à la doc du jour de version. Ce n'est pas le modèle qui a oublié le JSON.
Ce que le 21 octobre a vraiment basculé
Au 22/10/2026, d'après les notes de version de la plateforme Claude et Migrating to 2026-10-21 :
- En-tête de version : sans SDK, poser soi-même
anthropic-version: 2026-10-21. Python 2.0.0, TypeScript 1.0.0, Go 2.0.0, Java 3.0.0, C# 13.0.0, PHP 1.0.0, Ruby 2.0.0 et CLI 2.0.0 l'envoient par défaut. SDK et CLI passent sous Apache 2.0. - Trois 400 immédiats sur Messages : un
documentdirectement dans un messageassistant; untextutilisateur avec descitationsnon vides ;temperature,top_poutop_kdans la requête, y compris les valeurs par défaut. - Ne pas mélanger avec Bedrock : la page de migration dit que
2026-10-21sur Bedrock ne concerne que Claude 4.7 et plus récent. Les modèles plus anciens gardent la version actuelle. La note d'hier sur les sorties structurées Bedrock suit le guide utilisateur AWS, pas ce numéro de version.
Les sorties structurées n'ont pas attendu le 21 octobre pour passer en GA. La doc dit que output_format a été déplacé vers output_config.format et n'a plus besoin d'en-tête beta. Les nouveaux SDK envoient la nouvelle version par défaut, donc l'ancien champ, les paramètres d'échantillonnage et le sous-ensemble de schéma cassent le même jour.
Une phrase. Une fois l'en-tête en 2026-10-21, le contrat va dans output_config.format.schema. Ne plus envoyer output_format ni temperature.
Le schéma est dans format.schema, pas dans output_format
Au 22/10/2026, la page Structured outputs fige cette forme dans l'exemple cURL. L'en-tête de version de l'exemple reste 2023-06-01 ; après le jour de montée, le passer à 2026-10-21. La hiérarchie des champs ne change pas :
{
"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
}
}
}
}
Ne pas aplatir les trois couches : output_config est le champ de requête, format.type vaut json_schema, schema est le contrat objet. additionalProperties doit valoir false. L'en-tête beta structured-outputs-2025-11-13 ne sert qu'à la transition si l'on veut encore envoyer output_format ; sans cet en-tête, l'ancien champ renvoie 400.
La doc dit aussi : changer output_config.format casse le prompt cache de ce fil. Si le contrat change, la clé de cache change avec.
Le output_format de parse() n'est pas un champ filaire
Le chemin Python recommandé est client.messages.parse(). Le paramètre s'appelle encore output_format et prend un modèle Pydantic. Le SDK le convertit en output_config.format, valide, puis pose le résultat dans parsed_output. C'est un assistant client, pas l'ancienne clé du corps REST.
La même page dit : à partir du SDK Python 1.0, client.beta.messages.create() et count_tokens() n'acceptent plus output_format={...} et lèvent TypeError. Un schéma brut passe par client.messages.create(..., output_config={...}). Le parse() TypeScript utilise output_config.format plus zodOutputFormat(), aligné sur le fil.
Dans les types du SDK beta, output_config n'est accroché qu'à BetaSystemMessageParam. Le poser sur un message user / assistant échoue au typage ; Ruby lève NoMethodError. Le output_config à la racine de la requête reste l'emplacement GA de Messages.
Le JSON est dans un bloc text
La doc dit : le JSON conforme au schéma est dans un bloc de contenu text de la réponse. L'exemple Python de schéma brut n'est pas content[0].text, mais :
text = next(block.text for block in response.content if block.type == "text") contact = json.loads(text)
La contrainte grammaticale ne couvre que la sortie directe du modèle. La section Feature compatibility dit : la grammaire ne couvre pas les appels d'outil, les résultats d'outil, ni les balises thinking ; l'état grammatical se réinitialise entre les segments. La pensée peut donc précéder, et le JSON final reste dans un bloc dont type est text. En streaming, accumuler le message complet avant de désérialiser.
Activer les citations en même temps que output_config.format renvoie 400. Le message prefilling n'est pas non plus compatible avec une sortie JSON.
Les mots-clés hors sous-ensemble renvoient 400
La sortie JSON et les outils strict partagent les mêmes limites. Types de base, enum (pas de types complexes), const, anyOf / allOf (allOf plus $ref interdit), $ref, $def, definitions (pas de $ref externe). Un objet doit porter required, et additionalProperties ne peut valoir que false. Pour un tableau, minItems n'accepte que 0 et 1.
Non pris en charge : schémas récursifs, $ref externe, contraintes numériques (minimum / maximum / multipleOf), minLength / maxLength des chaînes, et toute valeur de additionalProperties autre que false. Les inclure renvoie 400.
L'assistant SDK retire d'abord les contraintes non prises en charge, les écrit dans la description du champ, puis valide localement contre votre schéma d'origine. Un output_config brut n'a pas cette étape : minimum: 100 est rejeté tout de suite. Le mot-clé de la page est $def, pas le $defs plus courant des brouillons JSON Schema.
Les enum / const de chaînes ne garantissent pas la casse. La première lettre du mot après un espace peut différer de la liste ; la requête se termine quand même, sans stop_reason spécial. Comparer en local sans tenir compte de la casse, et ne pas préparer deux valeurs d'énumération qui ne diffèrent que par la casse.
Les outils strict contraignent l'entrée
output_config.format dit ce que Claude raconte. tools[].strict: true dit comment il appelle une fonction. Les deux peuvent coexister dans la même requête. L'exemple de la doc met le résumé d'itinéraire dans le schéma de sortie, et destination / date de search_flights dans input_schema avec strict: true.
Les deux schémas passent par le même sous-ensemble. Avec beaucoup d'outils, la doc suggère de n'activer strict que sur ceux qui cassent ; les paramètres optionnels font à peu près doubler l'espace d'états grammatical.
Vérifier dans la boîte à outils
Séparer d'abord « la forme de requête » et « l'objet contact ». Les quatre extraits doivent se parser ; un seul couple sert de contrat.
{
"ok_format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["name", "email"],
"additionalProperties": false
}
},
"legacy_output_format": {
"output_format": {
"type": "json_schema",
"schema": { "type": "object", "additionalProperties": false }
}
},
"ok_contact": { "name": "John Smith", "email": "john@example.com" },
"extra_field": { "name": "John Smith", "email": "john@example.com", "phone": "1" }
}
- Coller les quatre exemples dans le formatage JSON. Les quatre se parsent. La clé de
legacy_output_formatest l'ancien nom de champ ; ne plus l'envoyer sur le fil. - Avec JSON → Schema, partir de
ok_contact, puis ajouterrequiredetadditionalProperties: false. Ne pas mettreminimumdans le schéma envoyé à l'API. - Dans la validation de Schema, confronter
ok_format.schemaàok_contact: ça doit passer.extra_fielddoit être refusé. - Les entrées d'outil vont aux appels de fonction. L'objet final va à la sortie structurée.
Les données restent dans le navigateur, rien n'est envoyé. Pouvoir refuser le phone en trop avant de changer de grande version de SDK.
Questions fréquentes
Le parse() Python écrit encore output_format : est-ce le champ qui renvoie 400 ?
Non. parse() prend une classe de modèle ; le SDK convertit en output_config.format. C'est le output_format={...} de beta create / count_tokens qui explose.
Après la montée en 2026-10-21, peut-on encore mettre temperature à 0 pour figer le JSON ?
Non. Les trois paramètres d'échantillonnage ont disparu des requêtes Messages. La sortie structurée passe par la grammaire, pas par la température.
Le premier bloc de content est-il le JSON ?
Lire le bloc text. Un segment de pensée peut précéder.
Si le schéma contient minimum, l'API impose-t-elle la borne ?
Un schéma brut renvoie 400. L'assistant retire la contrainte ; la borne reste locale.
Est-ce la même note que celle d'hier sur les sorties structurées Bedrock ?
Non. Celle-là suit le guide utilisateur Bedrock du 20/10/2026. Celle-ci porte sur la version d'API Claude du 21/10/2026 et output_config.
En bref
Au 22/10/2026, la version d'API 2026-10-21 a été publiée le 21/10/2026. Les nouveaux SDK envoient cet en-tête par défaut. Le JSON Schema va dans output_config.format.schema ; sur l'objet, additionalProperties ne peut valoir que false. Envoyer encore output_format (sans en-tête beta de transition) ou temperature renvoie 400. Le output_format de parse() n'est qu'un paramètre d'assistant Python. Lire le JSON dans un bloc dont type est text. minimum n'est pas dans le sous-ensemble. Les outils strict contraignent l'entrée ; le schéma de sortie contraint la dernière phrase. Ensuite, refuser les champs en trop et l'ancienne clé, puis monter le SDK Python 2.0.0.