Pour utiliser l'API Decisions d'OpenAI, envoyez une requête POST à https://api.openai.com/v1/decisions avec "model": "gpt-6-luna", une input (texte, images, ou les deux), et un tableau questions où chaque question est un predicate, un choice ou un score. Vous recevez des réponses typées avec des probabilités au lieu de texte à analyser, et vous payez 0,10 $ par million de tokens d'entrée, sans frais de sortie, de lecture ou d'écriture de cache. Le point de terminaison est en bêta publique à partir du 6 octobre 2026.
Ce guide couvre l'obtention d'une clé, le premier appel en curl, Python et JavaScript, la lecture de chaque type de réponse, trois questions sur un seul ticket de support, l'entrée d'images, les seuils et une configuration de test dans Apidog. Pour savoir quand choisir ce point de terminaison, commencez par qu'est-ce que l'API Decisions d'OpenAI.
Vue d'ensemble de la requête de l'API Decisions
| Champ | Ce qu'il accepte |
|---|---|
model |
gpt-6-luna (le seul modèle disponible en bêta) |
input |
Une chaîne de caractères, ou un tableau de messages user dont le content est une chaîne de caractères ou des parties de type input_text et input_image |
questions[].type |
predicate, choice, ou score |
questions[].instructions |
Obligatoire ; la question en mots simples |
questions[].name |
Facultatif ; renvoyé dans la réponse (null si omis) |
questions[].choices |
Uniquement pour choice ; 2 à 255 objets {value, description} uniques, value étant une chaîne de caractères ou un booléen |
questions[].levels |
Uniquement pour score ; objets {label, description} ordonnés, du plus bas au plus haut, indices à partir de 0 |
safety_identifier |
ID opaque facultatif de l'utilisateur final, jusqu'à 128 caractères |
Source : la référence de l'API Decisions. Il n'y a pas de temperature, stream, tools ou text.format sur ce point de terminaison.
Obtenir une clé et faire le premier appel
Créez une clé dans le tableau de bord d'OpenAI (le guide des clés API OpenAI le couvre), exportez-la en tant que OPENAI_API_KEY et ne la collez jamais dans le code. Posez ensuite une question oui/non :
curl https://api.openai.com/v1/decisions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"input": "The box arrived crushed and the screen is cracked.",
"questions": [
{"type": "predicate", "name": "damaged",
"instructions": "Does the customer report a damaged item?"}
]
}'
La réponse contient trois champs de premier niveau : model, answers et usage. C'est la structure de la référence d'OpenAI, avec output_tokens à 0 car le point de terminaison ne facture aucune sortie :
{
"model": "gpt-6-luna",
"answers": [
{"type": "predicate", "name": "damaged", "probability": 0.95}
],
"usage": {
"input_tokens": 42,
"input_tokens_details": {"cached_tokens": 0, "cache_write_tokens": 0},
"output_tokens": 0,
"output_tokens_details": {"reasoning_tokens": 0},
"total_tokens": 42
}
}
Le même appel en Python (SDK 3.26.0 ou ultérieur) :
from openai import OpenAI
client = OpenAI() # reads OPENAI_API_KEY from the environment
decision = client.decisions.create(
model="gpt-6-luna",
input="The box arrived crushed and the screen is cracked.",
questions=[
{"type": "predicate", "name": "damaged",
"instructions": "Does the customer report a damaged item?"}
],
)
print(decision.answers[0].probability)
Et en JavaScript (SDK 7.30.0 ou ultérieur) :
import OpenAI from "openai";
const client = new OpenAI();
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: "The box arrived crushed and the screen is cracked.",
questions: [
{ type: "predicate", name: "damaged",
instructions: "Does the customer report a damaged item?" },
],
});
console.log(decision.answers[0].probability);
Lire la réponse par type
Les réponses reviennent dans l'ordre de vos questions, chacune avec un type. Utilisez le type, car toute question peut revenir comme un refusal (refus).
predicaterenvoieprobability, une estimation de 0 à 1 que la condition est vraie.choicerenvoiechoice(lavaluegagnante),probabilitiessous forme de tableau d'objets{value, probability}, etconfidence.scorerenvoiescore,probabilitiessous forme de tableau d'objets{value, label, probability}(oùvalueest l'index de niveau basé sur 0), etconfidence. Le score est la moyenne pondérée par la probabilité des indices de niveau, il peut donc se situer entre les niveaux : 1.1 signifie "entre le niveau 1 et le niveau 2, proche de 1".refusalne renvoie quetypeetname. Le modèle a refusé cette question ; d'autres questions dans la même requête peuvent toujours recevoir des réponses.
for a in decision.answers:
if a.type == "refusal":
send_to_review(a.name)
elif a.type == "predicate":
flag = a.probability > 0.9
elif a.type == "choice":
route = a.choice if a.confidence > 0.8 else "review"
elif a.type == "score":
priority = round(a.score)
Le guide d'OpenAI établit la distinction ainsi : choice pour les catégories sans ordre, comme les départements ; score pour les niveaux ordonnés, comme la gravité.
Trois questions sur un seul ticket de support
Les questions indépendantes partagent une requête et une seule input, et chaque question peut utiliser un type différent. Voici un prédicat, un choix et un score sur un seul ticket :
{
"model": "gpt-6-luna",
"input": "I was charged twice for my order.",
"questions": [
{"type": "predicate", "name": "refund_requested",
"instructions": "Is the customer asking for money back?"},
{"type": "choice", "name": "department",
"instructions": "Which team should handle this ticket?",
"choices": [
{"value": "billing", "description": "Charges, refunds, invoices"},
{"value": "technical", "description": "Bugs and errors in the product"},
{"value": "shipping", "description": "Delivery and tracking"},
{"value": "other", "description": "Anything else"}
]},
{"type": "score", "name": "urgency",
"instructions": "How urgent is this ticket?",
"levels": [
{"label": "low", "description": "No time pressure"},
{"label": "medium", "description": "Needs a reply this week"},
{"label": "high", "description": "Customer is blocked or losing money"}
]}
]
}
Le tableau answers revient dans le même ordre. Les valeurs choice ci-dessous sont les valeurs de référence d'OpenAI pour cette entrée exacte ; les valeurs de prédicat et de score sont illustratives :
"answers": [
{"type": "predicate", "name": "refund_requested", "probability": 0.88},
{"type": "choice", "name": "department", "choice": "billing",
"probabilities": [
{"value": "billing", "probability": 0.95},
{"value": "technical", "probability": 0.02},
{"value": "shipping", "probability": 0.01},
{"value": "other", "probability": 0.02}
],
"confidence": 0.93},
{"type": "score", "name": "urgency", "score": 1.6,
"probabilities": [
{"value": 0, "label": "low", "probability": 0.05},
{"value": 1, "label": "medium", "probability": 0.30},
{"value": 2, "label": "high", "probability": 0.65}
],
"confidence": 0.65}
]
Deux règles du guide : incluez une option de repli telle que other lorsque vos catégories ne couvrent pas toutes les entrées, et formulez les questions autour de critères observables afin que les niveaux de score adjacents signifient des choses différentes. Si une deuxième décision dépend de la première réponse, envoyez une requête séparée.
Entrée d'image
Passez une image comme partie de contenu à l'intérieur d'un message user. Le guide documente les URL de données base64 en ligne :
{
"model": "gpt-6-luna",
"input": [{
"role": "user",
"content": [
{"type": "input_text", "text": "Photo attached to a return request."},
{"type": "input_image", "image_url": "data:image/jpeg;base64,/9j/4AAQ..."}
]
}],
"questions": [
{"type": "predicate", "name": "visible_damage",
"instructions": "Is the product visibly damaged?"}
]
}
La référence de l'API liste également les URL HTTP(S) accessibles publiquement, jusqu'à 128 images dans tous les messages d'une requête, et un champ detail facultatif (low, high, auto, original). Testez donc les URL hébergées avec votre propre compte avant de vous y fier. Les entrées file_id ne sont prises en charge sur aucune des pages.
Choisir des seuils à partir d'exemples étiquetés
OpenAI ne publie aucune donnée de précision ou de calibration pour ce point de terminaison. Ses recommandations sont d'utiliser des exemples étiquetés de votre propre application pour définir des seuils de routage, de filtrage ou de révision, en fonction du coût des faux positifs par rapport aux faux négatifs. En pratique, cela signifie un petit fichier CSV de tickets réels avec le département choisi par un humain, traités par la même requête, afin que vous puissiez voir où la confidence sépare les routes claires de celles qui nécessitent une intervention humaine. La section suivante construit cette boucle.
Tester l'API Decisions dans Apidog
Les requêtes sauvegardées rendent l'ajustement des seuils et les vérifications de régression reproductibles. Voici la configuration dans Apidog :
- Stockez la clé comme variable d'environnement. Créez un environnement, ajoutez
OPENAI_API_KEYcomme variable secrète (Environnements et variables secrètes Apidog montre la configuration), et définissez l'en-têteAuthorizationàBearer {{OPENAI_API_KEY}}. La clé n'atterrit jamais dans le corps d'une requête partagée. - Sauvegardez une requête par type de question. Créez une requête POST vers
https://api.openai.com/v1/decisionsavecContent-Type: application/json, collez la question de typechoicede l'exemple de ticket ci-dessus seule, et sauvegardez-la. Dupliquez-la pour les versions prédicat et score. - Ajoutez des assertions JSONPath. Sur la requête de type `choice` : le statut est 200, `$.answers[0].type` est égal à `choice`, `$.answers[0].choice` est égal à `billing`, `$.answers[0].confidence` est supérieur à 0.8, et `$.usage.output_tokens` est égal à 0. Pour le prédicat de dommage, affirmez que `$.answers[?(@.name=='damaged')].probability` est supérieur à 0.9. Un changement de formulation dans vos instructions, ou un changement de comportement du modèle, fera désormais échouer un test au lieu de mal acheminer les tickets.
- Exécutez-le sur des tickets étiquetés. Construisez un scénario de test à partir de la requête sauvegardée et attachez un petit fichier CSV avec deux colonnes,
ticket_textetexpected_department. Mappez{{ticket_text}}dansinputet affirmez que$.answers[0].choiceest égal à{{expected_department}}. Le rapport d'exécution affiche laconfidencepour chaque ligne, ce sont les données qu'OpenAI vous dit d'utiliser pour définir les seuils. Le point en dessous duquel chaque erreur de routage se situe devient votre seuil de "routage automatique" dans le code. - Simulez le tableau
answerspour le frontend. Orientez le routeur ou l'interface utilisateur vers une simulation du même point de terminaison qui renvoie une réponsechoiceavec uneconfidencesupérieure et inférieure à votre seuil, plus unrefusal, afin que le chemin de la file d'attente de révision soit construit avant que vous ne dépensiez un token d'entrée. Les réponses de simulation conditionnelles dans Apidog couvrent la commutation des simulations en fonction du contenu de la requête. - Exécutez le scénario en CI. Exportez un jeton d'accès, puis ajoutez une étape à votre pipeline :
apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
-t "$SCENARIO_ID" -e "$ENV_ID" -r cli,junit
Une assertion échouée fait échouer la build, donc une baisse silencieuse de la confidence est détectée avant le déploiement plutôt que dans la file d'attente du support. Pour des modèles plus larges, consultez le test des applications LLM.
Gérer les erreurs et les cas limites
- 429 limite de taux atteinte. Aucune limite spécifique à Decisions n'est publiée ; vérifiez Paramètres > Organisation > Limites pour vos chiffres. Attendez de manière exponentielle et respectez un en-tête
Retry-Aftersi présent. Le guide sur le dépassement de la limite de taux propose un wrapper de nouvelle tentative. - Réponses de refus. Traitez
type: "refusal"comme un résultat de routage, et non comme une exception. Envoyez ce ticket à un humain et conservez les autres réponses de la même requête. - Décisions dépendantes. Chaque question est évaluée indépendamment par rapport à l'entrée partagée. Tout ce qui dépend d'une réponse précédente nécessite sa propre requête.
FAQ
Combien coûte l'API Decisions ? 0,10 $ par million de tokens d'entrée sur gpt-6-luna, sans frais de sortie, de lecture ou d'écriture de cache. Un ticket de 500 tokens avec trois questions coûte 500 / 1 000 000 x 0,10 $ = 0,00005 $, donc un million de ces tickets coûte 50 $. Une entrée de contexte long de plus de 272K tokens est 2 fois plus chère, et le traitement régional ajoute 10 %.
L'API Decisions est-elle gratuite ? Non. Il n'y a pas de niveau gratuit pour Decisions. Si vous souhaitez essayer GPT-6 Luna sans payer, l'article Routes gratuites de GPT-6 Luna liste ce qui existe.
Quelle est sa rapidité ? OpenAI indique qu'elle est environ 10 fois plus rapide que l'API Responses et ne publie aucun chiffre de latence absolu. Un développeur sur le forum OpenAI a signalé des décisions d'image en environ 0,8 seconde.
Quels modèles fonctionnent avec l'API Decisions ? Seulement gpt-6-luna aujourd'hui. C'est un point de terminaison sur Luna, pas un modèle séparé. Voir qu'est-ce que GPT-6 Luna pour le modèle lui-même.
Quand devrais-je utiliser les sorties structurées à la place ? Lorsque vous avez besoin d'un objet dans votre propre schéma JSON, comme des champs extraits ou une explication écrite, ou l'appel de fonction lorsque le modèle doit demander un outil avec des arguments. L'article API Decisions vs API Responses montre le même ticket traité des deux manières.
Comment se compare-t-elle à Jev ? Les deux renvoient des réponses typées avec des probabilités et ne facturent que l'entrée ; Jev est textuel uniquement à 0,042 $ par million. La comparaison API Decisions vs Jev contient le tableau complet.
Prochaine étape
Envoyez la requête de ticket à trois questions de ce guide, puis exécutez-la sur 20 de vos propres tickets étiquetés et voyez où la confidence sépare les routes correctes des mauvaises. Ensuite, téléchargez Apidog pour conserver ensemble la requête, le scénario CSV et les assertions, afin que le seuil que vous choisissez aujourd'hui soit revérifié à chaque déploiement.
