Decisions API reference
The Decisions API evaluates evidence and returns a typed answer: the probability that a statement is true, a choice from supplied categories, or a score against an ordered rubric. Use it for classification, request routing, relevance checks, and prioritization. It does not generate a conversation reply or a custom JSON object.
AvalAI exposes the OpenAI Decisions request schema at /v1/decisions for TYPESAFE AI's Jev models. The provider's native request format uses the separate SystemOne endpoint. These endpoints have different field names and response formats.
Live verification, 2026-10-23: the three-option classification request below and one shared-text request containing predicate, choice, and score questions returned HTTP 200 with typed answers on both jev-latest and jev-preview. This verifies the documented small text requests; broader input types, large request limits, boolean choices, cache behavior, and refusals were not established by these checks.
Endpoint and authentication
POST https://api.avalai.ir/v1/decisionsSend an AvalAI API key in Authorization: Bearer … and a JSON body with Content-Type: application/json. Read the key from AVALAI_API_KEY in your server environment; do not embed it in browser code or public source files. See authentication.
Models and pricing
| AvalAI model ID | Current routed version |
|---|---|
jev-preview | jev-1.13.0 |
jev-latest | jev-1.14.0 |
Both aliases have the same AvalAI price: USD 0.042 per 1 million input tokens, USD 0.00 per 1 million cached input tokens, and USD 0.00 per 1 million output tokens. The aliases identify the routed versions listed above; keep the alias and request configuration in your evaluation records so you can compare behavior after a version change. See the provider page and pricing.
OpenAI's own model availability and pricing apply to OpenAI's service. They do not determine AvalAI's Jev routes or commercial terms. microsoft-decision-1 is a possible future addition and is not an available model in this release.
Request body
Live verification: the corrected request using choices returned HTTP 200 with both aliases on October 23, 2026. The live checks also covered the Predicate, Choice, and Score definitions below. An earlier request used the incorrect field options and returned HTTP 400; correcting that field resolved the failure. Native System One has been checked separately and uses a different response format.
| Field | Type | Required | Meaning |
|---|---|---|---|
model | string | Yes | An AvalAI decision model ID, such as jev-latest or jev-preview. |
input | string or array | Yes | Shared evidence that every question evaluates. The examples below use a text string. |
questions | array of question objects | Yes | The independent questions to answer about that evidence. |
In the upstream OpenAI schema, input also accepts an ordered array of messages, function calls and outputs, or custom tool calls and outputs. A tool output must identify its corresponding call with call_id. Within that upstream schema, user messages can contain text, images, and JSON; system and developer messages contain input text, and assistant messages contain output text or refusals.
That broader upstream schema is not a promise that every input type is supported by Jev through AvalAI. Start with shared text and validate any additional input type against your selected route. In particular, do not assume image inputs, input_json, tool history, streaming, prompt cache controls, or OpenAI's numeric limits are supported just because the endpoint uses the OpenAI schema. For native text or JSON context, see SystemOne.
Question fields
| Field | Type | Applies to | Meaning |
|---|---|---|---|
type | string | All questions | predicate, choice, or score. |
instructions | string | All questions | The condition, category-selection rule, or scoring instructions. Use this exact field name. |
name | string | All questions, optional | Identifies the question in the answer. Give each question a distinct name. |
choices | array | choice | The allowed options, each with a value and an optional description. |
levels | array | score | The ordered rubric levels, each with a label and an optional description. |
Use choices, not options, on /v1/decisions. Use instructions for each question. The native /v1/systemone endpoint uses criteria and a different request schema. Mixing the two formats can produce a 400 invalid-request response.
The upstream OpenAI reference allows 1–400 questions, 2–255 unique choices, and 2–10 score levels. These are OpenAI limits, not verified AvalAI Jev limits. Keep requests bounded and test the sizes your application requires.
Predicate
A predicate checks a single condition. Its answer contains probability, the estimate that the condition is true. Phrase the question around one observable property and define what counts as evidence.
{
"type": "predicate",
"name": "duplicate_charge",
"instructions": "Does the customer report being charged more than once for the same order?"
}Choice
A choice selects a supplied category. The upstream schema supports string or boolean value fields; a string and a boolean are distinct even if they look alike. The text examples use strings. Supply descriptions that distinguish overlapping categories and include an explicit fallback when categories do not cover all inputs.
{
"type": "choice",
"name": "department",
"instructions": "Which department should handle this complaint?",
"choices": [
{
"value": "billing",
"description": "Payments, invoices, and refunds."
},
{
"value": "technical",
"description": "Problems using the product."
},
{
"value": "shipping",
"description": "Delivery and tracking."
},
{
"value": "other",
"description": "Requests outside these categories."
}
]
}Score
A score rates evidence against ordered levels. Arrange levels from lowest to highest and give adjacent levels distinct criteria. Level indices start at 0; the upstream score is their probability-weighted average, so it can fall between levels. It is not an arbitrary number range that you set with minimum or maximum.
{
"type": "score",
"name": "severity",
"instructions": "How severe is this issue?",
"levels": [
{
"label": "Cosmetic",
"description": "Appearance only; no lost functionality."
},
{
"label": "Workaround available",
"description": "A task fails, but another way works."
},
{
"label": "Fully blocked",
"description": "A task fails with no workaround."
}
]
}Text classification example
The clients below send the same request to AvalAI. The Python example uses only the standard library. The JavaScript example requires Node.js 18 or later and runs on the server. Each sends a single bounded attempt so an invalid request is visible; use the retry policy for production traffic.
curl --fail-with-body \
--connect-timeout 10 --max-time 30 \
https://api.avalai.ir/v1/decisions \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary '{
"model": "jev-latest",
"input": "I was charged twice. Please help.",
"questions": [{
"type": "choice",
"name": "route",
"instructions": "Which team should handle this message?",
"choices": [
{"value": "billing", "description": "Payments or invoices"},
{"value": "technical", "description": "Software or API problems"},
{"value": "other", "description": "Anything else"}
]
}]
}'import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen
payload = {
"model": "jev-latest",
"input": "I was charged twice. Please help.",
"questions": [
{
"type": "choice",
"name": "route",
"instructions": "Which team should handle this message?",
"choices": [
{"value": "billing", "description": "Payments or invoices"},
{"value": "technical", "description": "Software or API problems"},
{"value": "other", "description": "Anything else"},
],
}
],
}
request = Request(
"https://api.avalai.ir/v1/decisions",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {os.environ['AVALAI_API_KEY']}",
"Content-Type": "application/json",
},
method="POST",
)
try:
with urlopen(request, timeout=30) as response:
decision = json.load(response)
except HTTPError as error:
print(f"HTTP {error.code}: {error.read().decode('utf-8', errors='replace')}")
raise
print(json.dumps(decision, indent=2, ensure_ascii=False))const apiKey = process.env.AVALAI_API_KEY;
if (!apiKey) throw new Error("AVALAI_API_KEY is required");
const payload = {
model: "jev-latest",
input: "I was charged twice. Please help.",
questions: [{
type: "choice",
name: "route",
instructions: "Which team should handle this message?",
choices: [
{ value: "billing", description: "Payments or invoices" },
{ value: "technical", description: "Software or API problems" },
{ value: "other", description: "Anything else" },
],
}],
};
const response = await fetch("https://api.avalai.ir/v1/decisions", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(30_000),
});
const text = await response.text();
if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
console.log(JSON.stringify(JSON.parse(text), null, 2));To compare the preview route, change only model to jev-preview. To ask a predicate or score question, replace the question object with the corresponding example above and supply appropriate evidence in input.
Current official OpenAI SDKs document client.decisions.create(...), but availability depends on the installed SDK version and its types. Raw HTTP is a useful baseline when your SDK has no Decisions resource or restricts model identifiers. Do not send this body through chat.completions.create: /v1/decisions uses input and questions, not the Chat Completions messages format.
Response schema and interpretation
The verified AvalAI text responses contain model, answers, usage, and AvalAI's estimated_cost field. The checked answers follow question order and echo name. The upstream schema also permits an unnamed question with a null answer name; this variant was not tested. Read each answer's type before interpreting its fields.
| Answer type | Upstream fields | Interpretation |
|---|---|---|
predicate | name, probability, type | Estimated probability that the condition is true. |
choice | name, choice, probabilities, confidence, type | Selected supplied value, per-option probabilities, and a separate confidence value. |
score | name, score, probabilities, confidence, type | Weighted level index, per-level probabilities, and a separate confidence value. |
refusal | name, type | In the upstream schema, the model declined an individual question; other questions can still receive answers. |
Complete captured AvalAI response to the three-option request above, using jev-latest, received on 2026-10-23 16:11:38 UTC (12:11:38 p.m. America/New_York). It returned HTTP 200 with avalai-request-id: 01a16988-37e0-7360-b61b-5d703e489cb4. The same request with jev-preview returned HTTP 200 at 16:11:48 UTC, with request ID 01a16988-5fcb-7101-b32e-5eebaed63b9b. Numeric answers, token counts, and currency conversion values can differ on future requests:
{
"model": "jev-latest",
"answers": [
{
"type": "choice",
"name": "route",
"choice": "billing",
"probabilities": [
{
"value": "billing",
"probability": 1.0
},
{
"value": "technical",
"probability": 0.0
},
{
"value": "other",
"probability": 0.0
}
],
"confidence": 1.0
}
],
"usage": {
"input_tokens": 333,
"input_tokens_details": {
"cached_tokens": 0,
"cache_write_tokens": 0
},
"output_tokens": 38,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 371
},
"estimated_cost": {
"unit": "0.0000139860",
"irt": 3.78,
"exchange_rate": 270000
}
}Choice probability entries use value and probability. Score probability entries additionally include label; their value is the zero-based level index. confidence is separate from the selected option's probability: do not replace one with the other or invent a formula from the returned probabilities. Do not assume that a provider's probability entries always contain every option.
The captured usage reports 333 input tokens and 38 output tokens, totaling 371. The cache-read, cache-write, and reasoning token counts are 0 in this capture. Zero output pricing does not mean zero reported output tokens.
The USD estimate is 333 × 0.042 / 1,000,000 = 0.0000139860, matching estimated_cost.unit; billing uses the priced input tokens here, not total_tokens. estimated_cost.irt is 3.78 at the captured exchange_rate of 270000. These are returned estimates from that request, not a fixed exchange rate or a promise of identical token accounting for other inputs. The presence of cache fields with zero values does not prove cache-hit or cache-write behavior.
Handle missing, malformed, unknown, or refused answers as unresolved decisions. A refusal is not a negative predicate or a fallback category. Refusal behavior and untested upstream response variants require additional route-specific verification through AvalAI.
Set thresholds with labeled examples from your application and the cost of incorrect routing. A high probability does not prove that a decision is correct. Send uncertain results to a review queue and keep the application action separate from the model answer. See the decision-model guide and Jev cookbook.
Multiple questions and workflow boundaries
Independent questions can share one input: for example, route a ticket and check whether it mentions a duplicate charge. A question cannot consume another question's answer in the same request. For a dependent workflow, read the first response and then build a separate request.
Decisions evaluates supplied evidence; your application executes the resulting route, filter, or review action. If you need extracted fields, a written explanation, or a custom output object, use a suitable Responses API model and structured output support instead.
Errors and retries
| Symptom | What to check | Retry policy |
|---|---|---|
400 or invalid request | JSON syntax, model, input, questions, instructions, and choices/levels. Never replace choices with options or send native criteria here. | Correct the request first. Repeating the same invalid body will not fix it. |
401 | AvalAI key validity and the Bearer header. | Correct authentication first. |
403 | Account or key access to the selected route. | Resolve access before retrying. |
404 | The exact /v1/decisions URL and model ID. | Correct the path or model selection first. |
429 | Rate limiting versus exhausted credit or quota in the returned error body. | Retry temporary throttling with a delay; credit or quota errors require account action. |
500, 502, 503, or 504 | A temporary gateway or provider failure. | Retry with a bounded policy; follow a valid Retry-After when present. |
| Network failure or timeout | Connectivity, per-attempt timeout, and whether the server may already have processed the request. | Retry only within your overall deadline and attempt budget. |
Follow a valid Retry-After delay as a minimum. When it is absent or invalid, use exponential backoff with jitter. Limit both attempts and total elapsed time; if the server requests a delay beyond your deadline, defer the work instead of retrying sooner. Account for SDK retries when adding your own retry loop. Retried requests may be processed and charged more than once; apply any downstream action only once using an application job ID.
For debugging, retain the endpoint, model alias, HTTP status, redacted error body, and response request identifiers described in response headers. Keep API keys and sensitive input out of shared logs. See error handling and rate limits.
Sources and related guides
- Official OpenAI Decisions guide: question types, examples, interpretation, and upstream capability boundaries.
- Official OpenAI create-decision reference: exact request and response fields.
- Official OpenAI retry guidance: server delays, bounded backoff, and SDK retry interaction.
- TYPESAFE AI on AvalAI, native SystemOne reference, decision-model guide, and Jev cookbook.