Skip to content
TheThinkLoop

Guide

Decisions API vs Structured Outputs: which to use for classification

How the Decisions API differs from Structured Outputs for LLM classification and routing, where each one's confidence comes from, and how to combine them.

Updated - 7 min read

Before the Decisions API existed, the standard way to get a classification out of a language model was Structured Outputs: define a JSON schema with an enum, and the model fills it in. That still works. So when should you use which? The short answer is that they return different kinds of information, and the choice follows from what you need to do with the result.

The core difference

Structured Outputs constrains the shape of what a model writes. The Decisions API returns a typed answer with probability information. A useful rule of thumb: use Decisions when your application needs one of its three answer types, and Structured Outputs when you need to generate an object that follows your own schema, such as extracted fields or a written explanation.

Classification with Structured Outputsjavascript
// Structured Outputs: you define the shape, the model writes values into it.
const response = await client.responses.create({
  model: "<a general-purpose model>",
  input: [
    { role: "system", content: "Classify the support ticket." },
    { role: "user", content: ticketText },
  ],
  text: {
    format: {
      type: "json_schema",
      name: "triage",
      strict: true,
      schema: {
        type: "object",
        properties: {
          team: { type: "string", enum: ["sales", "support", "billing", "other"] },
          reason: { type: "string" },
        },
        required: ["team", "reason"],
        additionalProperties: false,
      },
    },
  },
});
// → { "team": "billing", "reason": "The customer disputes an invoice amount." }
The same classification with the Decisions APIjavascript
// Decisions API: you define the question, the model returns a typed answer.
const decision = await client.decisions.create({
  model: "gpt-6-luna",
  input: ticketText,
  questions: [
    {
      type: "choice",
      name: "team",
      instructions: "Which team should own this ticket?",
      choices: [
        { value: "sales" }, { value: "support" }, { value: "billing" }, { value: "other" },
      ],
    },
  ],
});
// → { type: "choice", choice: "billing", confidence: 0.91,
//     probabilities: [{ value: "billing", probability: 0.92 }, ...] }

Side by side

Decisions APIStructured Outputs
ReturnsA probability, a choice, or a score.Any JSON object matching your schema.
Probability for each optionYes, in the response.Not part of the output.
Free text and explanationsNo.Yes.
Field extractionNo.Yes.
Answer guaranteed to be one of your optionsYes.Yes, with a strict schema and an enum.
Several questions about one inputBuilt in: one request, one copy of the input.Possible, by adding fields to the schema.
ModelsDecision models (gpt-6-luna at the time of writing).General-purpose models that support it.
MaturityBeta, announced September 2026.Established.

The part that matters: where confidence comes from

If you only need a label, either approach will give you one. The difference shows up when you need to know how much to trust it, which you do as soon as you want to automate some decisions and review others.

With Structured Outputs, there are two common ways to get a confidence value:

  • Ask for one in the schema. Adding "confidence": { "type": "number" } gets you a number. That number is text the model generated, like any other field. Nothing ties it to how often answers with that number are right. It may be useful, but you have to measure that yourself.
  • Read token log probabilities. Where the API exposes them, the probability of the token for each enum value is a real distribution. It takes more code, depends on how labels tokenize, and gets awkward for multi-token labels.

The Decisions API returns the distribution directly, as part of the documented response. That is its main practical advantage: thresholds, review routing, and calibration all start from a probability, and here you get one without building the plumbing.

When Structured Outputs is the better tool

  • You need a reason in words. Customer-facing explanations, audit notes, and reviewer summaries are generated text.
  • You need to extract values. Order numbers, dates, amounts, and names are not decisions.
  • The answer isn’t from a fixed set. If the model has to propose something, it isn’t choosing among options.
  • You need a model the Decisions API doesn’t offer, or you can’t build on a beta API yet.

When the Decisions API is the better tool

  • You will act on a threshold. Automate above, review below.
  • You are making the same judgment thousands of times and latency or cost per decision matters. At launch OpenAI described the API as about ten times faster than the Responses API; we have not independently measured that yet.
  • You want several facts about one input and would rather not design a schema and prompt for each.
  • You want the output to be boring. A typed answer has fewer ways to go wrong than generated JSON.

Using them together

These are not competing choices. A common design uses the Decisions API for the decision and Structured Outputs for the words around it, and only pays for the second when a person will read the result.

Decide first, explain only when neededjavascript
// 1. Decide. Fast, cheap, and thresholdable.
const { answers } = await client.decisions.create({ /* ... */ });
const main = answers[0];

// 2. Only for cases going to a person, generate a summary to help them.
if (main.type !== "refusal" && main.confidence < AUTOMATE_AT) {
  const note = await summarizeForReviewer(ticketText); // Structured Outputs
  sendToReview({ ticket, suggestion: main.choice, confidence: main.confidence, note });
}

One caution: an explanation generated afterwards is a plausible summary of the input, not a record of how the decision model reached its answer. Label it accordingly for reviewers. If you need to show which facts the model found, use evidence checks, which are evaluated by the same model in the same request.

What we have and haven’t measured

This guide compares the two approaches by design, not by benchmark. We have not yet published measured accuracy, calibration, or latency for either. Our benchmarks page describes the dataset and method we will use, and states plainly which results exist so far. Treat any comparison that offers numbers without a method, including one from us, with suspicion.

Related

Templates that use these ideas

Put it into practice

Try a decision, check it against labeled examples, and talk to us if you want help building one for your workflow.