How to Use Decision Models with the Unified GenAI API
November 11, 2026
Generative models write text for people to read.
But a lot of AI work in a product is really a decision: is this ticket urgent, which team should handle it, does this reply promise a refund, how well does this candidate match the job?
Asking a chat model, then parsing its prose, adds latency, cost and fragile parsing code.
Decision models are built for that job. Instead of generating text, they score a fixed set of answers and return calibrated probabilities your code can branch on directly. With the Unified GenAI API, you call them through the same genai_prompt endpoint you already use for chat. Add typed questions and you get typed answers back.
What you can ask
Every decision request sends shared input (your messages) plus one or more typed questions. Each question is answered independently against the same input.
| Question type | Use it to | What you get back |
|---|---|---|
BOOLEAN | Check whether a statement holds (gates, filters, policy checks) | score: the probability (0–1) that the answer is yes |
CHOICE | Pick one option from a fixed list (routing, classification, tool or model selection) | value: the top option, probabilities for every option, confidence |
SCORE | Rate the input on an ordered scale (severity, priority, rubric grading) | score: the probability-weighted level (0 = first level), probabilities per level, confidence |
| Output tokens are typically zero or near zero, so decisions are fast and inexpensive compared with generating and parsing text. |
Step 1: Find a decision model
List the models on a GenAI connection. Decision models have has_questions: true; models that also accept images have has_vision: true.
curl "https://api.unified.to/genai/$CONNECTION_ID/model" \
-H "Authorization: Bearer $UNIFIED_API_KEY"
Use https://api-eu.unified.to or https://api-au.unified.to for workspaces in the EU or AU regions.
Not every provider flags its decision models in the model list (OpenAI, for example, doesn't). The table under Supported providers lists the decision model IDs for each integration.
Step 2: Ask your questions
Send a POST to /genai/{connection_id}/prompt with your messages and a questions array. Give each question a unique id; its answer comes back with the same id. For CHOICE and SCORE questions, list the options (or levels, lowest first) in options.
curl -X POST "https://api.unified.to/genai/$CONNECTION_ID/prompt" \
-H "Authorization: Bearer $UNIFIED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model_id": "gpt-6-luna",
"messages": [
{ "role": "USER", "content": "Audio cuts out on the left earcup. Fix it today or I cancel my membership." }
],
"questions": [
{ "id": "is_defect", "type": "BOOLEAN", "instructions": "Does the message describe a hardware defect?" },
{
"id": "department",
"type": "CHOICE",
"instructions": "Which team should handle this request?",
"options": ["billing", "technical", "shipping", "other"]
},
{
"id": "churn_risk",
"type": "SCORE",
"instructions": "How likely is the customer to churn if this is not resolved now?",
"options": ["No churn signal", "Mild frustration", "Mentions alternatives", "Threatens to cancel"]
}
]
}'
The model_id depends on the provider; see the table below. The response contains one answer per question:
{
"model_id": "gpt-6-luna",
"answers": [
{ "id": "is_defect", "type": "BOOLEAN", "score": 0.94 },
{
"id": "department",
"type": "CHOICE",
"value": "technical",
"probabilities": [
{ "option": "technical", "probability": 0.91 },
{ "option": "billing", "probability": 0.05 },
{ "option": "other", "probability": 0.03 },
{ "option": "shipping", "probability": 0.01 }
],
"confidence": 0.91
},
{
"id": "churn_risk",
"type": "SCORE",
"score": 2.97,
"probabilities": [
{ "option": "No churn signal", "probability": 0.0 },
{ "option": "Mild frustration", "probability": 0.0 },
{ "option": "Mentions alternatives", "probability": 0.03 },
{ "option": "Threatens to cancel", "probability": 0.97 }
],
"confidence": 0.97
}
],
"tokens_used": 324
}
The values above are illustrative. The original provider response is always available in raw.
Step 3: Act on the answers
Use the answer to choose an action, and the probability or confidence to decide whether to act automatically or escalate:
const res = await fetch(`https://api.unified.to/genai/${connectionId}/prompt`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNIFIED_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ model_id: 'gpt-6-luna', messages, questions }),
});
const { answers } = await res.json();
const byId = Object.fromEntries(answers.map((a) => [a.id, a]));
if (byId.is_defect?.score > 0.9 && byId.department?.confidence >= 0.7) {
routeTo(byId.department.value);
} else {
routeTo('human_review');
}
Set thresholds using labeled examples from your own traffic, and weigh the cost of a false positive against a false negative.
Images
Models with has_vision: true can evaluate images. Attach them to a message as base64 data URLs:
{
"role": "USER",
"content": "Customer photo for return request #8821.",
"images": ["data:image/jpeg;base64,/9j/4AAQSk..."]
}
Remote image URLs are not accepted; send the image bytes inline.
Supported providers
| Integration | Decision models (model_id) | Limits and notes |
|---|---|---|
| OpenAI | gpt-6-luna (default when model_id is empty) | Up to 200 questions. CHOICE: 2–255 options. SCORE: 2–10 levels. USER messages only. Images as data URLs. |
| TypeSafe | Jev, e.g. jev-latest (model_id and questions required) | CHOICE: up to 255 options. SCORE: 2–10 levels. Text only. A conversation is sent with its roles. |
| Cloudflare Workers AI | Clef: @cf/cloudflare/clef, @cf/cloudflare/clef-flash | 1–64 questions. Images: PNG, JPEG or WebP data URLs, up to 4 (4 MiB each, 8 MiB total). The same connection also serves chat models. |
| Zoom AI Services | zoom-decisions, zoom-decisions-pro | 1–10 questions. CHOICE: 2–255 options. SCORE: 2–10 levels. Images as data URLs. |
| Google Gemini | gemini-decision-1 (default when model_id is empty) | Up to 1,000 questions. CHOICE and SCORE: 2–255 options or levels. USER messages only. Images as data URLs. |
Requests that break a provider's limits are rejected with a clear 400 before anything is sent. On the generative providers (OpenAI, Cloudflare Workers AI, Google Gemini), a prompt without questions keeps working as a normal chat request. |
Tips for good decisions
- Ask narrow, single-purpose questions. Combine several simple answers with ordinary
ifstatements in your code, rather than packing everything into one question. - Add a catch-all option such as
othertoCHOICEquestions. Choice questions always spread probability across your options, even when none fits. - Order
SCORElevels from lowest to highest and keep adjacent levels clearly distinct. The returnedscorecan fall between levels, so check the per-level probabilities when inputs are borderline. - Put independent questions in one request. They share the same input, so asking several at once is cheaper and keeps the context consistent.
- Handle missing answers. Some providers can decline a single question; it is then left out of
answers, while the other questions are still answered.
When to use a generative model instead
Use a regular chat model (a genai_prompt without questions) when you need to write, summarize, extract fields or explain. Decision models and chat models work well together: use a decision model to route or gate a request, and then send the approved requests to a generative model.