Ship Decisions, Not Prompts
Send a state and a set of questions. Get back a calibrated answer and a full probability distribution for each one, in about a quarter of a second. No prompt to tune, no JSON to salvage out of prose, no streaming to babysit.
Creating a key does not run the model and is not charged.
Step 1
Create a Key
Keys live in Settings → API keys. The plaintext is shown once, at creation, and only a hash is stored — if it is lost, rotate rather than recover. Each key has its own rate limit and daily decision budget, so a key per environment turns an incident into one revocation.
JEV_API_KEY=sk-glm5-your-keyA key spends your balance. Never ship it in frontend JavaScript, a mobile bundle, a public repository or an analytics payload. If the browser needs a decision, put your own endpoint in front of it.
Step 2
Hand This Brief to Your Coding Agent
Copy the whole thing into Claude Code, Cursor, Codex or whatever writes code with you. It carries the full contract — the endpoint, the three question shapes, the two container rules everyone gets wrong, the decimal score, the retry policy, and the rule that the key never leaves your server.
You are integrating the Jev AI decision API into this codebase.
WHAT IT IS
Jev is a decision model, not a chat model. You send one "state" (any text or
JSON) plus a set of questions, and it returns a calibrated answer and a full
probability distribution for each question. There is no prompt engineering, no
JSON-in-prose to parse, and no streaming.
ENDPOINT
POST https://jev-ai.org/api/v1/systemone/
Headers:
Authorization: Bearer <JEV_API_KEY>
Content-Type: application/json
Idempotency-Key: <stable id> (optional, strongly recommended)
Use the trailing slash. This host canonicalises URLs and answers
/api/v1/systemone with a 308 redirect; most clients follow it, curl only with -L.
SECURITY - NOT NEGOTIABLE
- The key is a server-side secret. Read it from process.env.JEV_API_KEY (or the
equivalent for this stack). Never put it in client code, a mobile bundle, a
committed file, a log line, or an error message.
- If the browser needs a decision, add a server route in this codebase that
calls Jev and returns only the answer. Do not proxy the raw key.
REQUEST BODY
{
"model": "jev-1.13", // or "jev-latest"; optional, defaults to jev-1.13
"state": "<the text or JSON to decide about>",
"questions": {
"<question_id>": { ...one of the three shapes below... }
}
}
Up to 20 questions per call. Question ids come back as the keys of "answers",
so name them the way the calling code will read them.
THE THREE QUESTION TYPES - GET `criteria` RIGHT
1) noul - a probability. No criteria.
{ "type": "noul", "instructions": "Is the customer asking for a refund?" }
2) choice - one label from a set. criteria is an OBJECT: label -> description.
{
"type": "choice",
"instructions": "Route this ticket to the team that owns the first reply.",
"criteria": {
"billing": "Invoices, refunds, duplicate charges, plan changes, tax.",
"technical": "API errors, integrations, outages, SDKs.",
"sales": "Pre-purchase questions about plans, limits or trials."
}
}
3) score - a position on an ordered scale. criteria is an ARRAY of tier labels,
lowest to highest.
{
"type": "score",
"instructions": "Rate the customer's frustration.",
"criteria": ["Calm", "Mildly annoyed", "Frustrated", "Angry"]
}
Do NOT swap those containers. An array on a choice question, or an object on a
score question, is a 400.
RESPONSE
{
"id": "dec_...", // log this; it is the support handle
"model": "jev-1.13",
"model_version": "jev-1.13-20260917",
"answers": {
"refund": { "type": "noul", "noul": 0.93 },
"queue": { "type": "choice", "choice": "billing",
"probabilities": { "billing": 1, "technical": 0, "sales": 0 },
"confidence": 1 },
"anger": { "type": "score", "score": 1.89,
"legend": { "0": "Calm", "1": "Mildly annoyed", "2": "Frustrated", "3": "Angry" },
"probabilities": { "0": 0, "1": 0.11, "2": 0.89, "3": 0 },
"confidence": 0.89 }
},
"usage": { "input_tokens": 503, "output_tokens": 70,
"charged_tokens": 503, "charged_credits": 0, "wallet": "tokens" }
}
TYPES - THE ONE THAT BITES
"score" is a DECIMAL, not an integer. 1.89 on a four-tier scale is a normal
answer: it is the expected value of the distribution. Type it as a float
(number, f64, etc.). An integer type or a schema with type: "integer" will
reject valid responses. "legend" and the score "probabilities" are keyed by the
tier index as a STRING ("0", "1", ...).
"noul" answers carry no confidence field; distance from 0.5 is the confidence.
ERROR HANDLING - REQUIRED
- Branch on error.code, not on the message.
- Retry with backoff on 429, 502, 503, 504. Honour the Retry-After header on 429.
- Do not retry 400, 401, 402, 404, 409, 413, 422 - fix the request or the account.
- Failed requests are never charged, so a retry costs nothing. Send a stable
Idempotency-Key so a retry after a timeout cannot produce a second decision.
- 422 means the input exceeded the 32,000-token context. It is never silently
truncated: split the state instead.
- 503 always means the service is temporarily unavailable on their side, never
a problem with the key or the balance.
HOW TO USE IT WELL
- Ask every question about one state in ONE call. The state is read once and
charged once no matter how many questions ride along, so a fourth question is
nearly free while a fourth request is not.
- Put the boundary cases in the criteria descriptions, not in the instructions.
- Never ask for an explanation, reasoning, or a JSON format in "instructions".
The model answers with a distribution; asking for prose only spends tokens.
- One decision per question. Split "is it urgent and billing-related?" into two.
- Act on the distribution, not only the winner. A choice split 0.52 / 0.48 is a
case to escalate, not a routing decision.
WHAT TO BUILD HERE
Write a small typed client for this endpoint, with the three question shapes as
a discriminated union, the retry and idempotency rules above, and the key read
from the environment. Then wire it into the feature we are working on.
Documentation: https://jev-ai.org/docs/Step 3
Make the First Call
Three questions about one support ticket — a probability, a routed label and a scored scale — in a single request. Export your key and paste this into a terminal.
curl https://jev-ai.org/api/v1/systemone/ \
-H "Authorization: Bearer $JEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.13",
"state": "We were billed $49 twice on September 3. I opened ticket #4417 four days ago and have had no reply, and our books close on Friday.",
"questions": {
"needs_human": {
"type": "noul",
"instructions": "Does this ticket need a human reply rather than an automated one?"
},
"queue": {
"type": "choice",
"instructions": "Route this ticket to the team that should own the first reply.",
"criteria": {
"billing": "Invoices, refunds, duplicate charges, plan changes, tax.",
"technical": "API errors, integrations, outages, SDKs.",
"sales": "Pre-purchase questions about plans, limits or trials."
}
},
"anger": {
"type": "score",
"instructions": "Rate the customer'"'"'s frustration.",
"criteria": ["Calm", "Mildly annoyed", "Frustrated", "Angry"]
}
}
}'{
"id": "dec_24088aa50f8c6c46361f8a2a",
"model": "jev-1.13",
"model_version": "jev-1.13-20260917",
"provider": "jev-ai.org",
"answers": {
"needs_human": { "type": "noul", "noul": 0.89 },
"queue": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 1, "technical": 0, "sales": 0 },
"confidence": 1
},
"anger": {
"type": "score",
"score": 1.89,
"legend": { "0": "Calm", "1": "Mildly annoyed", "2": "Frustrated", "3": "Angry" },
"probabilities": { "0": 0, "1": 0.11, "2": 0.89, "3": 0 },
"confidence": 0.89
}
},
"usage": {
"input_tokens": 503,
"output_tokens": 70,
"charged_tokens": 503,
"charged_credits": 0,
"wallet": "tokens"
},
"latency_ms": 1056
}const response = await fetch("https://jev-ai.org/api/v1/systemone/", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.JEV_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": ticketId,
},
body: JSON.stringify({
model: "jev-1.13",
state: ticket.body,
questions: {
queue: {
type: "choice",
instructions: "Route this ticket to the team that owns the first reply.",
criteria: {
billing: "Invoices, refunds, duplicate charges, plan changes, tax.",
technical: "API errors, integrations, outages, SDKs.",
},
},
},
}),
});
const { answers, usage } = await response.json();
// answers.queue.choice -> "billing"
// answers.queue.probabilities -> the full distribution
// usage.charged_tokens -> what this call cost youUse the trailing slash. This host canonicalises URLs, so /api/v1/systemone answers a 308 redirect. Most clients follow it and preserve the POST; curl needs -L.
score is a decimal. 1.89 on a four-tier scale is a normal answer — it is the expected value of the distribution, which is what makes it sortable. An integer type will reject valid responses.
Billing
What a Decision Costs
Two balances, one order of precedence, and nothing charged unless the call succeeds.
Tokens first, then one credit
An API call spends the token wallet first, charged on the input tokens it read. If the token balance cannot cover the whole call, the entire call costs 1 credit instead — never a partial drain of both. Every response says which wallet paid.
Output tokens are free
They are counted in usage.output_tokens and never billed. Only input is charged, which is why asking four questions about one state costs barely more than asking one.
Failed requests are not charged
A decision is billed only when it returns 200. Anything else — a 422 for oversized input, a 503 while the model is unavailable, your own cancelled request — releases its hold in full.
Creating a key costs nothing
Creating, listing and revoking keys, reading a request status, and GET /api/v1/models never run the model and are never charged.
Limits per key: 10,000 decisions a day, 60 requests a minute by default (raise it to 300 in key settings), 20 questions per call and a 32,000-token context. Models: jev-1.13 and jev-latest. When neither wallet can pay, the call is refused with a 402 that states exactly what was needed — 1 credit, or the input tokens — and what the account holds. Full detail in the billing docs.
Ready When Your Key Is
Create a key, paste the brief, ship the endpoint. The docs cover every status code, the two wallets and the exact shape of all three question types.
