Answer typed questions about a state
https://api.stav.ai/v1/decisionsAsks a decision model (Jev, Clef, …) typed questions about a state
and returns one answer per question, each with a probability. The
request and answer shape are Jev's and OpenRouter's: change only the
base URL (https://api.stav.ai/v1/decisions) and the key.
Scope inference:decisions.
Name a model listed with the decisions surface on GET /v1/models;
auto and Routers are refused (router_not_supported). Any field
not listed here is refused by name (unsupported_parameter), and so
is stream. 1 to 32 questions per request.
Question types: noul (yes / no — the answer carries noul, the
probability of yes), choice (one of the named options in
criteria — the answer carries choice and confidence) and
score (ordered levels in criteria — the answer carries the
provider's weighted score and per-level probabilities, passed
through as the provider sends them).
Images go in images as data URLs, only to a model whose Provider
Endpoint takes them (stav_decision_images on the model object);
elsewhere the request is refused with image_input_not_supported.
Billing: input tokens at catalogue price; output tokens and a
per-request price only where the endpoint is priced in them.
usage.cost is the charge in the workspace currency, from Stav's
own ledger (absent when the workspace's own key served the call).
A provider answer missing a question is never returned in part: the
next Provider Endpoint is tried, else 502 upstream_invalid_answer.
The state, the questions and the answers are never stored outside a
capture window the workspace opened.
Request body
modelstringrequiredstatestring | objectrequiredWhat the questions are about — text, or a JSON object.
questionsobjectrequiredKeyed by a name you choose (letters, digits,
_,.,-); the answers use the same keys.imagesarray of string
Notes
POST https://api.stav.ai/v1/decisions takes the same JSON as TypeSafe's Jev API and OpenRouter's Decisions API. A decision model reads a state — text, a JSON object, an agent trace — and answers typed questions with a probability. No prose, so nothing to parse and nothing invented.
Coming from Jev or OpenRouter, change two things: the base URL and the key.
The three question types
| Type | Ask | criteria | The answer carries |
|---|---|---|---|
noul | A yes / no question | Not taken | noul — the probability of yes, 0 to 1 |
choice | Pick one of named options | An object of 2 or more options: {"name": "description"} | choice — the picked option, and confidence |
score | Rate on ordered levels | An array of 2 or more levels, lowest first | The provider's weighted score and its per-level probabilities, passed through as sent |
Every decision model takes this same request. For OpenAI's gpt-6-luna, Stav converts it to OpenAI's Decisions API and the answers back, so your code does not change when you change models. If OpenAI declines a question, you get 422 decision_refused naming it — never part of an answer. GET /v1/models lists every decision model you can call.
A noul answer is a probability, not a verdict: you decide the threshold. An agent guardrail might run a tool call only at 0.8 or above, and ask a person below it.
Request
| Field | Type | Notes |
|---|---|---|
model | string | Required. A model listed with the decisions surface on GET /v1/models. auto and Routers do not apply. |
state | string or object | Required. What the questions are about. |
questions | object | Required. 1 to 32 questions, keyed by a name you choose (letters, digits, _, ., -). The answers use the same keys. Each has type, instructions and, for choice and score, criteria. |
images | array | Up to 4 images as data URLs (data:image/png;base64,…; PNG, JPEG or WebP). Only on a model whose endpoint takes images — stav_decision_images on the model object. Remote URLs are refused. |
Any other field — stream included — is refused by name with 400 unsupported_parameter. Nothing is dropped silently.
Response
{
"model": "openai/gpt-6-luna",
"answers": {
"team": {"type": "choice", "choice": "billing", "confidence": 0.95},
"urgent": {"type": "noul", "noul": 0.31}
},
"usage": {"input_tokens": 212, "output_tokens": 0, "cost": 0.00005, "currency": "EUR"}
}
Every question you asked is answered, in the type you asked, or the request fails: a provider answer that misses a key or changes a type is never passed on in part. Stav tries the model's next Provider Endpoint, and answers 502 upstream_invalid_answer when none answers whole.
usage.cost is what your wallet is charged, in your workspace's currency, from Stav's own ledger — never the provider's own figure. It is absent when your workspace's own provider key (BYOK) served the call. The usual response headers come with it: the serving provider, its sovereignty level and the countries the state may have been processed in.
How it differs from chat
- Name the model.
autoand Routers serve the chat surfaces only. - Which models qualify. A model is callable here when a Provider Endpoint of it has been proven to answer decisions — Stav asks each endpoint one yes / no question with a known answer and records whether a probability came back.
- The same policy wall. Compute country, zero data retention, no-training, the sovereignty floor and blocked providers apply exactly as on chat. Some decision models are hosted in the US; a workspace whose policy excludes that is refused with the same codes as chat, and the refusal says why.
- Your key needs the
inference:decisionsscope. Without it:403 insufficient_scope. A key with no scopes has full access. - Billing. Input tokens at catalogue price. Output tokens and a per-request price only where the provider prices them — most decision models price input only. A model is callable only where the provider reports token usage: Stav never bills an estimate. A failed request is not charged. See Credits and billing.
- Your data. The state, the questions and the answers are not kept in the request log, which records the number of questions and their types. Inside a capture window you opened, the request and answer JSON are kept like a chat prompt.
Errors
| Status | When |
|---|---|
400 unsupported_parameter | An unknown field, stream, or a question field a type does not take |
400 invalid_value | A required field missing, a question out of shape — param names it (questions.<key>.criteria) |
400 image_input_not_supported | Images were sent to a model whose endpoints do not take them |
400 router_not_supported | model was auto or a Router |
402 | Not enough credits, or a spend limit was reached |
403 insufficient_scope | The key lacks inference:decisions |
404 decisions_not_enabled | Decisions are not enabled on this deployment |
404 model_not_found | The model is not a decision model, or has no Provider Endpoint on this surface priced in a unit Stav can measure |
501 decisions_not_supported | The model's Inference Provider has no decisions door |
422 decision_refused | The model declined to answer a question (OpenAI). The message names it; the input is billed as the provider billed it |
502 upstream_invalid_answer | No Provider Endpoint answered every question in the type asked |
| Provider status | The provider refused the request; its message is passed through as provider_error |
Full catalogue: Errors.
On the model page
A decision model's page has a Try model button: three presets (agent guardrail, support routing, lead scoring), a form or the raw JSON, a threshold slider for yes / no questions, the answer as probability bars, the receipt with the cost, and Get code for the call you just ran.
Response headers
| Header | Meaning |
|---|---|
X-Stav-Request-Id | Unique request identifier, identical to the completion |
X-Stav-Model | The model that actually served the request — not necessarily the one you asked for, when routing or failover intervened. |
X-Stav-Provider | The provider that served the request. |
X-Stav-Sovereignty-Level | The sovereignty level of the serving Provider Endpoint. |
X-Stav-Compute-Countries | Every country the state may have been processed in, comma-separated ISO 3166-1 alpha-2, when known. |
X-RateLimit-Limit | Per-minute request cap on this key. |
X-RateLimit-Remaining | Requests remaining in the current window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the current window rolls over. |
Errors
| Status | When |
|---|---|
| 400 |
|
| 401 | Missing or invalid API key. |
| 402 | The wallet or a spend limit refused the request. |
| 403 | API key lacks the required scope (e.g., |
| 404 |
|
| 422 |
|
| 429 | Per-key or per-IP rate limit exceeded. |
| 501 |
|
| 502 |
|
Every error code, what it means and what to do: Errors.