Core Concepts
Decisions
Get typed answers your code can branch on (yes/no probabilities, picks from your options, and scores) in one request
Preview. The /alpha path and the request and response shape may change before this API is marked stable. See release stages.
The Decisions API gives your code a structured answer instead of generated text. You send a state (the content to evaluate) and a map of named, typed questions. You get back one typed answer per question: a probability of yes for a noul question, one of your options for a choice question, or a position on your scale for a score question. Answers are never free text, so there is no output to parse or validate.
Use it when your code needs to branch on a judgment: classifying or routing a request, checking a message against a policy, scoring a passage for relevance, or gating an action on how certain the model is. For free-form text, use chat completions or the Responses API instead.
Requests are answered by TypeSafe's Jev model, on your Inworld API key and bill.
| Endpoint | POST https://api.inworld.ai/alpha/decisions |
| Model | typesafe/jev-latest |
| Streaming | Not supported: the whole answer set comes back in one response |
| Billing | Input tokens only; output tokens are not billed |
Quickstart
Run these examples on your server. Set INWORLD_API_KEY to the Base64 credentials copied from Portal or the CLI; see authentication.
curl -X POST https://api.inworld.ai/alpha/decisions \
-H "Authorization: Basic $INWORLD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-latest",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?"
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
}
}
}'import os
import requests
response = requests.post(
"https://api.inworld.ai/alpha/decisions",
headers={"Authorization": f"Basic {os.environ['INWORLD_API_KEY']}"},
json={
"model": "typesafe/jev-latest",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"is_urgent": {"type": "noul", "instructions": "Does this convey urgency?"},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts",
},
},
},
},
timeout=60,
)
response.raise_for_status()
answers = response.json()["answers"]
print(answers["is_urgent"]["noul"], answers["department"]["choice"])const response = await fetch('https://api.inworld.ai/alpha/decisions', {
method: 'POST',
headers: {
Authorization: `Basic ${process.env.INWORLD_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'typesafe/jev-latest',
state: 'Help! My payouts have been failing for 3 days.',
questions: {
is_urgent: { type: 'noul', instructions: 'Does this convey urgency?' },
department: {
type: 'choice',
instructions: 'Which team should handle this?',
criteria: {
billing: 'Payments, invoicing, refunds',
technical: 'Bugs, outages, integrations',
sales: 'Pricing, upgrades, new accounts',
},
},
},
}),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const { answers } = await response.json();
console.log(answers.is_urgent.noul, answers.department.choice);Response:
{
"model": "typesafe/jev-1.13.0",
"answers": {
"is_urgent": { "type": "noul", "noul": 0.95 },
"department": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 0.88, "technical": 0.12, "sales": 0.0 },
"confidence": 0.81
}
},
"usage": { "input_tokens": 318, "output_tokens": 34 },
"metadata": {
"attempts": [
{ "model": "typesafe/jev-1.13.0", "status_code": 200, "duration_ms": 212.4, "success": true }
],
"generation_id": "0f5d3c4e-8f0b-4a53-9a55-2f3a6f0d8a1b",
"total_duration_ms": 212
}
}Question types
Each entry in questions is keyed by an id you choose, and its answer comes back under the same id. The ids are not sent to the model. Every question has a type and instructions. Each type defines its own criteria.
type | Asks | criteria | Answer fields |
|---|---|---|---|
noul | A yes/no question | Optional. {"true": "...", "false": "..."} describes what yes and no mean | noul: the probability of yes, from 0 to 1 |
choice | Pick one option from a set | Required. An object mapping each option to its description, or to null. Up to 255 options | choice, probabilities (sums to 1), confidence |
score | Rate on ordered levels | Required. An array of level descriptions, lowest first. 2 to 10 levels | score (probability-weighted level index from 0, which can land between levels), legend, probabilities, confidence |
state, instructions and every criterion accept a string, an object, or an array. To keep a long question readable, put the question in one field of an instructions object and the data it refers to in the others, and refer to those fields by name in backticks. All questions are answered against the same state in one pass, so asking several related questions in one request is cheaper and faster than one request per question. TypeSafe's guides to state, question types and confidence cover how to write them well.
Differences from TypeSafe's API
The request and answer bodies follow TypeSafe's System One API. Validation errors are returned with TypeSafe's status and body. The differences:
- Path and credentials. Call
https://api.inworld.ai/alpha/decisionswith your Inworld API key. TypeSafe's SDKs call/v1/systemone, which Inworld does not serve, so call this endpoint over HTTP as shown above. - Model ids. Name the model
typesafe/jev-latest, as you name other providers' models on Inworld Router. The barejev-latestalso works. The response'smodelis the exact version that answered, with the provider prefix, for exampletypesafe/jev-1.13.0. - Routing metadata. Every successful answer gets a
metadataobject with the same keys as chat completions:attempts,generation_idandtotal_duration_ms.
Limits and errors
| Status | Meaning | What to do |
|---|---|---|
400 | Your workspace has zero data retention, and the Decisions API is not available for these workspaces. Nothing is sent to TypeSafe | Call it from a workspace without zero data retention |
401 | Missing or invalid Inworld API key | Check the Authorization header |
413 | The body is larger than 10 MiB | Send less state, or split the questions across requests |
422 | TypeSafe rejected the body, for example invalid JSON, an unknown question type, a missing criteria, or too many options | Fix the request as the error describes |
429, 503, 529 | Rate limited, at capacity, or overloaded | Retry with exponential backoff, and honor Retry-After when present |
502 | TypeSafe could not be reached or is unavailable | Retry with exponential backoff |
504 | No answer within 60 seconds | Retry, or send fewer or smaller questions |
Errors that Inworld raises (400, 413, 502, 504, and a 503 when Inworld is at capacity) use the router's error format, {"error": {"message": "...", "type": "..."}}. A 401 has a plain-text body (Unauthorized). TypeSafe's validation and rate-limit errors, such as 422 and 429, use TypeSafe's status and body.
Billing
Decisions are billed per input token (usage.input_tokens) at the Jev rate, whatever version answers. Output tokens are reported but not billed. An answer that fails or is cut off is not billed.