# OpenAI Decisions API: A Practical Guide for Fast, Typed AI Decisions
## Introduction
When building applications that need to make quick, structured decisions—like categorizing customer messages, validating documents, or analyzing images—developers often turn to large language models for help. However, calling a full generation endpoint for a simple classification task can be slow, expensive, and overkill.
OpenAI recently introduced the **Decisions API**, a specialized endpoint designed for exactly this kind of scenario. It accepts a model, some input, and a list of questions, then returns typed answers with built-in confidence scores—enabling applications to decide whether to trust a result or escalate it to a human reviewer.
In this article, we’ll explore what the Decisions API is, how it works, when it makes sense to use it, and walk through practical code examples in Python.
—
## Understanding How the API Works
At its core, the Decisions API is built around a simple concept: you send it a piece of content—text, images, or both—and ask it a set of structured questions. Each question has a defined type, and the API responds with a corresponding answer format.
The beta version of the API uses **GPT-6 Luna** as its underlying model and is accessed via a dedicated `/v1/decisions` endpoint.
### The Three Question Types
| Type | Purpose | What It Returns |
|——|———|—————–|
| **Predicate** | Asks a yes/no question | A probability between 0 and 1 |
| **Choice** | Asks the model to pick from a defined list | A selected value, probabilities, and confidence |
| **Score** | Evaluates input against ordered levels | A score, probabilities, and confidence |
A predicate does **not** return a Boolean directly. Instead, it gives you a probability, and your code decides the threshold at which that probability triggers an action. This gives developers fine-grained control over how aggressive or cautious their application logic should be.
Answers are returned in the same order as the questions were submitted. The API may also refuse individual questions, so it’s important to check each answer’s type field before reading its other fields.
—
## When the Decisions API Makes Sense
The Decisions API shines in scenarios where you need a fast, structured answer from a defined set of options. Think of common workflows like:
– **Routing incoming messages** to the correct support team
– **Selecting a search index** based on a query’s intent
– **Choosing an agent’s next action** from a permitted list
– **Flagging documents** that are missing required fields
It also has separate pricing that differs from the standard generation endpoints. At launch, OpenAI lists **$0.10 per million input tokens**, with no charges for output tokens, cache reads, or cache writes. Long-context multipliers and regional processing premiums still apply.
As a rough estimate, one million requests averaging 1,000 billable input tokens would cost approximately $100 at the base rate.
### When Not to Use It
The Decisions API isn’t a replacement for generation. If you need a written explanation, free-form text, or an extracted JSON object with arbitrary fields, use Structured Outputs with the Responses API instead. Similarly, if a decision can be handled by a simple Python conditional—such as checking whether an order total exceeds a threshold—keep it in plain code and avoid an unnecessary API call.
A model becomes useful when the input expresses something that rules alone can’t easily recognise, such as a customer describing the same issue in multiple different ways. Even then, the extra network call needs to save enough downstream effort to justify the added latency.
—
## Setting Up Python
The official `openai-python` library added Decisions support in **version 3.26.0**. To get started:
“`bash
pip install openai==3.26.0
“`
You’ll also need an OpenAI API key. Create one by logging into your OpenAI account, navigating to the API keys section, and following the prompts to generate a new secret key. Once you have your key, set it as an environment variable:
“`powershell
$env:OPENAI_API_KEY=”your-api-key”
“`
Each code example below is a complete, standalone Python program. The three files work independently and each makes a billable API request when run.
—
## Code Example 1: Routing a Customer Support Request
Imagine you run an online shop with three specialist queues—**returns**, **billing**, and **delivery**—plus a **general** queue for anything that doesn’t fit neatly into the others. You need to classify incoming customer messages automatically.
“`python
from openai import OpenAI
MODEL = “gpt-6-luna”
def main() -> None:
with OpenAI(timeout=20.0) as client:
result = client.decisions.create(
model=MODEL,
input=”My keyboard arrived with three broken keys. Can you replace it?”,
questions=[{
“type”: “choice”,
“name”: “queue”,
“instructions”: “Choose the queue for the customer’s main request.”,
“choices”: [
{“value”: “returns”, “description”: “Damaged goods or replacements.”},
{“value”: “billing”, “description”: “Charges or invoice errors.”},
{“value”: “delivery”, “description”: “Missing or delayed deliveries.”},
{“value”: “general”, “description”: “Everything else or unclear intent.”},
],
}],
)
answer = result.answers[0]
if answer.type == “refusal”:
print(“Send to manual triage”)
elif answer.type == “choice”:
print(answer.choice, answer.confidence)
queue = answer.choice if answer.confidence >= 0.8 else “manual_triage”
print(“Queue:”, queue)
if __name__ == “__main__”:
main()
“`
When tested with a message about a broken keyboard, the output was:
“`
returns 1.0
Queue: returns
“`
When a different message was used—”Do you have a website I could look at?”—the model correctly routed it to the general queue with a confidence of 0.8.
The `confidence` threshold here is illustrative and not an OpenAI recommendation. Before setting your own threshold, test against messages that have already been classified by humans and measure error rates at several different thresholds.
The choice objects include both values and descriptions, which helps the model distinguish between similar categories—such as damaged goods versus a delivery that never arrived.
—
## Code Example 2: Checking a Document for Missing Information
Suppose your team writes return instructions in several different formats. You want to automatically flag instructions that are missing either a return deadline or a postal return address.
“`python
from openai import OpenAI, RateLimitError
MODEL = “gpt-6-luna”
def main() -> None:
with OpenAI(timeout=20.0) as client:
checks = {
“deadline”: “Does the text explicitly give a deadline for returning an item?”,
“address”: “Does the text explicitly provide a postal return address?”,
}
result = client.decisions.create(
model=MODEL,
input=”Return the item within 30 days. Email support to request our address.”,
questions=[
{“type”: “predicate”, “name”: name, “instructions”: question}
for name, question in checks.items()
],
)
for answer in result.answers:
if answer.type == “refusal”:
print(answer.name, “review required”)
elif answer.type == “predicate”:
status = “present” if answer.probability >= 0.9 else “check manually”
print(answer.name, status, answer.probability)
if __name__ == “__main__”:
try:
main()
except RateLimitError as exc:
if exc.code == “credit_balance_exhausted”:
raise SystemExit(
“Your OpenAI API credit balance is exhausted. Add credits for ”
“the organisation associated with your API key and then run ”
“this example again.”
) from None
raise
“`
The output from this example was:
“`
deadline present 1.0
address check manually 0.0
“`
This result highlights an important nuance about how these models interpret language. Being told to *request* an address is not the same as being *given* one. The model correctly identified that the text mentions reaching out to get the address but does not actually provide it.
Both predicate questions can share a single request because neither depends on the other’s answer. If a later question needs to reference an earlier result, make a separate request after inspecting that result.
—
## Code Example 3: Inspecting a Product Photograph
One of the standout features of the Decisions API is its ability to process images alongside text. In this example, we test whether the model can distinguish between damaged and undamaged parcels using three photographs.
“`python
import base64
from pathlib import Path
from openai import OpenAI, RateLimitError
MODEL = “gpt-6-luna”
PARCEL_IMAGES = (“undamaged.png”, “heavy_damage.png”, “slight_damage.png”)
def main() -> None:
with OpenAI(timeout=20.0) as client:
for filename in PARCEL_IMAGES:
image_path = Path(__file__).with_name(filename)
encoded = base64.b64encode(
image_path.read_bytes()
).decode(“ascii”)
result = client.decisions.create(
model=MODEL,
input=[{
“role”: “user”,
“content”: [
{
“type”: “input_text”,
“text”: “Image of a delivered parcel.”,
},
{
“type”: “input_image”,
“image_url”: f”data:image/png;base64,{encoded}”,
},
],
}],
questions=[{
“type”: “predicate”,
“name”: “visible_damage”,
“instructions”: (
“Does the packaging visibly have a tear, ”
“hole or crushed corner?”
),
}],
)
answer = result.answers[0]
if answer.type == “refusal”:
print(f”{filename}: Inspect the photograph manually”)
elif answer.type == “predicate”:
print(
f”{filename}: Probability of visible packaging ”
f”damage: {answer.probability}”
)
if __name__ == “__main__”:
try:
main()
except RateLimitError as exc:
if exc.code == “credit_balance_exhausted”:
raise SystemExit(
“Your OpenAI API credit balance is exhausted. Add credits for ”
“the organisation associated with your API key and then run ”
“this example again.”
) from None
raise
“`
A few important technical notes for image handling:
– Images must be provided as **inline data URLs** using base64 encoding. Ordinary web URLs and file IDs are not accepted.
– The endpoint supports up to **128 images per request**.
– The message input supports only the **user role** with text and image parts.
Here were my results:
“`
undamaged.png: Probability of visible packaging damage: 0.05
heavy_damage.png: Probability of visible packaging damage: 1.0
slight_damage.png: Probability of visible packaging damage: 0.98
“`
The model performed well across all three images, correctly identifying the undamaged parcel as almost certainly undamaged and the heavily damaged one as clearly damaged. The slight-damage image scored 0.98, which is impressive—subtle damage can be difficult even for human reviewers to spot consistently.
—
## Frequently Asked Questions
### Q1: What is the Decisions API used for?
The Decisions API is designed for applications that need fast, structured answers from a defined set of options—such as classifying content, routing requests, validating documents, or evaluating images. It is not intended for open-ended generation tasks like writing essays or extracting complex JSON structures.
### Q2: How is the Decisions API priced?
At launch, OpenAI charges **$0.10 per million input tokens**, with no fees for output tokens, cache reads, or cache writes. Regional processing premiums and long-context multipliers may still apply.
### Q3: Can I use the Decisions API with images?
Yes. The API supports inline image data (base64-encoded) alongside text input. You can include up to 128 images in a single request.
### Q4: Does the API return Boolean values for yes/no questions?
No. Predicate-type questions return a **probability between 0 and 1**. It is up to your application code to choose the threshold that converts that probability into a decision.
### Q5: What happens if the API refuses a question?
The answer’s `type` field will be set to `”refusal”`. You should check for this type before reading other fields and handle it appropriately—for example, by sending the request to a human reviewer.
### Q6: How does the Decisions API compare to JEV in terms of speed?
TypeSafeAI has reported response times of 70–500 milliseconds for JEV. In testing, the OpenAI Decisions API response times often run into multiple seconds. However, a direct timed comparison was not performed in this article, and performance can vary based on context length and request complexity.
### Q7: Should I use the Decisions API or the standard Responses API?
Use the Decisions API when you have a fixed set of answer types (yes/no, choose from a list, or score against a rubric) and need a fast, low-cost endpoint. Use the Responses API with Structured Outputs when you need free-form generation, extracted objects with arbitrary fields, or written explanations.
### Q8: What Python SDK version do I need?
You need **openai-python version 3.26.0 or later**. Install it with `pip install openai==3.26.0`.
—
## Conclusion
The OpenAI Decisions API fills an important niche in the AI tooling landscape. By providing a dedicated, lightweight endpoint for structured decision-making, it allows developers to build fast and cost-effective classification and routing workflows without the overhead of full text generation.
Key strengths of the API include its support for both text and image inputs, its built-in confidence scoring, and its separate (and lower) pricing tier for decision-only workloads. These features make it a compelling option for production systems that need to process high volumes of structured questions quickly.
That said, the Decisions API is not a one-size-fits-all solution. Simple rules should stay in code, generation tasks should use the appropriate endpoints, and the speed advantage—while present—is not always dramatic depending on your use case.
As with any AI tool, the best approach is to test it against your real data, measure accuracy at multiple confidence thresholds, and compare cost and latency against alternatives before committing to production use.
Thank you for reading



