Decisions
Ask yes/no, choice and score questions about a text and get a probability for every option
The Decisions API answers typed questions about a piece of text. You send the text and your questions, and you get one answer per question with a probability for every option. Nothing is generated for you to parse, and the answer can't fall outside the options you supplied. Use it for triage, routing, tagging and moderation, where you need a label and a number.
Every example here reads your key from NEBUL_API_KEY; export it first. The Quick Start covers key setup.
Models
Two models serve decisions today. Call them by these exact IDs.
| Model | Context |
|---|---|
google/gemma-4-31B-it | 262,144 tokens |
Qwen/Qwen3-0.6B | 40,960 tokens |
Other chat models return 501 instead of an answer.
Request
POST https://api.inference.nebul.io/v1/decisions
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model | String | Yes | A model that serves decisions. Pick one from Models. |
input | String or Array | Yes | The text every question is about: a string, or an array of {role: "user", content} messages. Text only. |
questions | Array | Yes | 1 to 64 questions, answered in the order you send them. |
questions[].type | String | Yes | predicate, choice or score. |
questions[].instructions | String | Yes | The question, written as you would ask a person. |
questions[].name | String | No | Echoed in the answer so you can match answer to question. |
questions[].choices | Array | choice only | 2 to 26 objects of {value, description}. value is a string or a boolean. |
questions[].levels | Array | score only | 2 to 10 objects of {label, description}, lowest level first. |
cache_salt | String | No | Salt for the prefix cache. See Prompt / KV Caching. |
Input is text only. Send an image part or a tool call inside input and the request fails with a 400, so flatten agent transcripts and screenshots to text before you call the endpoint.
Question types
| Type | Question | You send | You get back | Use for |
|---|---|---|---|---|
predicate | Is this true about the input? | instructions | probability, 0 to 1 | Binary flags: needs_refund, is_urgent. |
choice | Which one of these? | 2 to 26 {value, description} | choice, probabilities, confidence | Routing, categories, language. |
score | How much, on this scale? | 2 to 10 ordered {label, description} | score, probabilities, confidence | Severity, sentiment, urgency. |
choice reads one label token per option, which caps it at 26. score reads digit tokens and returns the expected level, so a score of 1.2 on low, medium, high sits between medium and high. When you would otherwise ask several predicates about one scale, use one score: one read, and the levels are mutually exclusive by construction.
Example
Ask two questions about one support message:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["NEBUL_API_KEY"],
base_url="https://api.inference.nebul.io/v1",
)
response = client.decisions.create(
model="google/gemma-4-31B-it",
input="The parcel arrived damaged and support ignored my emails.",
questions=[
{
"type": "choice",
"name": "team",
"instructions": "Which team should handle this?",
"choices": [
{"value": "payments", "description": "billing and duplicate charges"},
{"value": "technical", "description": "app and login problems"},
{"value": "other", "description": "anything else"},
],
},
{
"type": "score",
"name": "severity",
"instructions": "How severe is the problem?",
"levels": [{"label": "low"}, {"label": "medium"}, {"label": "high"}],
},
],
)
for answer in response.answers:
print(answer)Response
One entry in answers per question, in request order, carrying the name you set.
{
"model": "google/gemma-4-31B-it",
"answers": [
{
"type": "choice",
"name": "team",
"choice": "other",
"probabilities": [
{ "value": "payments", "probability": 4.2e-11 },
{ "value": "technical", "probability": 6.3e-10 },
{ "value": "other", "probability": 0.9999999993 }
],
"confidence": 0.9999998807
},
{
"type": "score",
"name": "severity",
"score": 2.0,
"probabilities": [
{ "value": 0, "label": "low", "probability": 1.03e-10 },
{ "value": 1, "label": "medium", "probability": 2.85e-8 },
{ "value": 2, "label": "high", "probability": 0.9999999714 }
],
"confidence": 0.9999996423
}
],
"usage": {
"input_tokens": 126,
"input_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 64 },
"output_tokens": 2,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 128
}
}| Field | Type | Description |
|---|---|---|
model | String | The model that answered. |
answers | Array | One entry per question, in request order. |
answers[].probability | Number | predicate: the probability that the statement is true, from 0 to 1. |
answers[].choice | String or Boolean | choice: the option with the highest probability. |
answers[].probabilities | Array | choice, score: every option with its probability, in the order you sent them. |
answers[].score | Number | score: the expected level, counting from 0 for the first level you sent. With low, medium, high, a score of 2.0 means high. |
answers[].confidence | Number | choice, score: the chosen option's probability among every token the model could emit, so a read that mostly wanted a non-option word reports a lower number. |
usage.input_tokens | Integer | The input counted once, however many questions you ask about it. |
usage.input_tokens_details.cached_tokens | Integer | How much of the input came from the prefix cache. |
usage.output_tokens | Integer | One read per question. |
usage.total_tokens | Integer | Input plus output. |
Every question comes back as one of these three types. The endpoint doesn't decline to answer, so a question the model has no idea about still returns a probability, usually a low one. Set your own threshold when a wrong label costs more than a missing one.
Probabilities
Probabilities sum to 1 across the options of a question. They are the model's raw readout, so they sit close to 0 and 1 and a wrong option can still score near certainty. Compare options within a question, and treat the number as a ranking signal rather than a calibrated confidence.
Limits
| Limit | Value |
|---|---|
| Questions per request | 64 |
Options per choice | 26 |
Levels per score | 10 |
Exceeding a limit returns a 400 with the reason, for example questions: at most 64 for this model or This model supports at most 26 choices per question. The option and level ceilings come from the model reading one label token per option: 26 letters for a choice, 10 digits for a score.
Errors
| Status | Cause |
|---|---|
| 400 | Malformed request, or a limit exceeded. |
| 404 | Unknown model ({"error_type": "not_found"} with the model ID). |
| 501 | The model runs on a vLLM build that doesn't serve decisions yet. |
| 503 | The model is not available. |
Errors use the standard envelope: {"error": {"message", "type", "param", "code"}}.