Request contract: one label list
Send POST /api/v1/decisions with Authorization: Bearer YOUR_API_KEY, Content-Type: application/json, and an Idempotency-Key of 8–100 characters. The body contains text (3–6,000 characters), instructions (3–1,500 characters), labels (2–12 unique choices, each up to 60 characters on one line), and threshold (0.5–1, default 0.85).
The key belongs to an account with an active access period; create it in the workspace dashboard. One successful request consumes one decision credit. Anonymous browser trials use a separate allowance and never expose your provider credentials.
A body is one shape or the other. Send instructions with labels for a single label decision, or questions for a typed one, never both — and send the same Idempotency-Key only for the same logical request.
Try the same request in the playground · Create and revoke an API key · OpenRouter Jev tutorial
Request contract: typed questions
Send questions instead of instructions and labels to ask up to four typed questions about the same text. Every question has an id (1–32 characters: lowercase letters, numbers or underscores, starting with a letter, unique in the request), a type, instructions (3–1,500 characters) and its criteria: 2–12 criterion labels for choice, exactly true and false for noul, and 2–7 ordered levels of up to 80 characters for score. A criterion guidance line may be empty, but may not exceed 300 characters or wrap.
A body that carries questions together with instructions or labels is refused with 400 ambiguous_input, as is a request with more than four questions or a repeated id. One request costs one credit either way: asking more questions changes how much is answered, not the price.
{
"text": "My checkout page shows a blank screen after I click Pay. I tried two browsers.",
"questions": [
{"id": "team", "type": "choice", "instructions": "Which team should own this ticket?", "criteria": {"billing": "Charges or refunds.", "technical": "Errors or broken behaviour.", "account": "Sign-in, permissions or profile."}},
{"id": "is_bug", "type": "noul", "instructions": "Is the writer reporting a defect?", "criteria": {"true": "They describe broken behaviour.", "false": "They ask a question or request a feature."}},
{"id": "urgency", "type": "score", "instructions": "How urgently does this need an answer?", "criteria": ["Can wait", "This week", "Blocking work"]}
],
"threshold": 0.85
}LLM as a judge: typed rules you can audit · Batch classification and exports
Response contract
A new decision answers 201 with choice, confidence, probabilities, needsReview, model, id and latencyMs. model names the upstream Jev model that answered; this deployment requests typesafe/jev-1.13. An account request also returns the remaining credits, and a replayed idempotent request adds replayed: true.
A typed request adds answers: one entry per question id, typed as the question was. A choice carries its label, confidence and probabilities; a noul carries one probability between 0 and 1 and no confidence, because the provider does not send one; a score carries its position on your levels with confidence, probabilities and a legend when the provider supplies one. The top-level choice, confidence and probabilities mirror the first question — a noul mirrors as true or false with a null confidence and an empty map — and needsReview is true when any answer needs review.
needsReview is this service’s reading of an answer at your threshold. A choice or a score falls below it when confidence is missing or lower than the threshold; a noul is measured on its probability directly, so 0.80 needs review at a threshold of 0.85. Probabilities are checked for domain and bounds only: they are not required to sum to one, the largest is not required to match the answer, and a missing map comes back as {}. A model failure is an error response, never a default label.
{
"answers": {
"team": { "type": "choice", "choice": "technical", "confidence": 0.88, "probabilities": { "billing": 0.03, "technical": 0.88, "account": 0.09 }, "needsReview": false },
"is_bug": { "type": "noul", "noul": 0.93, "needsReview": false },
"urgency": { "type": "score", "score": 2, "confidence": 0.80, "probabilities": { "0": 0.03, "1": 0.17, "2": 0.80 }, "needsReview": true }
},
"choice": "technical",
"confidence": 0.88,
"probabilities": { "billing": 0.03, "technical": 0.88, "account": 0.09 },
"needsReview": true,
"model": "typesafe/jev-1.13",
"id": "6f2c1a4e-9d7b-4c11-8f3a-2b5e9c0d1a77",
"latencyMs": 1180,
"credits": 19
}Safe retries
Use a unique key for each logical request and reuse it with the exact payload when the network outcome is uncertain, including a generic gateway error. Replaying a completed request returns its stored result without another debit, with replayed: true. A 409 idempotency_conflict means the input differs; 409 request_running means keep the key and wait. Only after the API confirms request_failed should you fix the cause and create a new key for another attempt. Stop after bounded retries and retain the request identity for support.
