$ cat index.mdx$ cat ~/blogs/engineering/jev-vs-openai-decisions-api/index.mdx # I put my name on this
Jev vs OpenAI Decisions API: Decision Models Explained
Most codebases that picked up an LLM in the last two years have a line like this somewhere:
reply = chat("Is this support ticket about billing? Answer only yes or no.")
if reply.strip().lower().startswith("yes"):
route_to_billing(ticket)It works most of the time. Then the model answers "Based on the ticket, yes" and the check fails, or it adds a polite preamble, or a reasoning model spends eight seconds thinking about a question a support agent answers in one. The code asked for a decision, the model handed back prose, and now the code has to turn the prose back into a decision.
On September 15, 2026, TypeSafe AI released Jev, a model built for that line and nothing else. It cannot write a sentence. You send it some state and a set of typed questions, and it returns a probability for every answer you allowed. OpenAI followed with its own Decisions API, in public beta since October 6, running on GPT-6 Luna.
I went through both APIs' docs, the two independent benchmarks published so far, and the math behind the numbers they return. My take: if your code asks a model a question and branches on the answer, a decision model is the right tool, and for text input, Jev is the better default today. OpenAI's version wins when the input is an image, or when adding a new vendor would cost you a quarter of procurement paperwork.
Fast questions, slow questions
TypeSafe calls the category "System One models", after Daniel Kahneman's Thinking, Fast and Slow. System 2 is slow, deliberate work: planning a migration, debugging a race condition, writing this post. System 1 is the judgment a knowledgeable person makes in about a second: is this customer angry, is this a refund request, which team owns this ticket.
Chat models are trained for System 2 shaped work and for talking to people. RLHF tuned them toward answers human raters prefer, which makes them great at prose and famously overconfident when you ask them for a number. Yet most of what software actually needs from AI is System 1: a label, a route, a yes or no, thousands of times a minute, inside a code path with a latency budget.
TypeSafe's launch post puts frontier chat models at 3 to 329 seconds end to end and Jev at 70 to 500 milliseconds. That is a vendor number, and I come back to it below with independent ones. The shape of the claim is what matters first: a decision model skips the whole business of generating text, so most of the latency and most of the bill go with it.
What comes back from a decision model
Here is the response from Jev's own quickstart. The state is one support message, "Our API integration started returning 500 errors on every request about 20 minutes ago, and we can't process any customer orders until this is fixed", and the request asks three questions about it:
{
"model": "jev-1.13.0",
"answers": {
"department": {
"type": "choice",
"choice": "technical",
"confidence": 1.0,
"probabilities": { "technical": 1.0, "billing": 0.0, "sales": 0.0 }
},
"frustration": {
"type": "score",
"score": 0.67,
"confidence": 0.5,
"probabilities": { "0": 0.33, "1": 0.67, "2": 0.0 }
},
"is_urgent": { "type": "noul", "noul": 0.99 }
},
"usage": { "input_tokens": 428, "output_tokens": 73 }
}Three things in that response are different from anything a chat model gives you.
First, every answer is one of the options you defined.
technical cannot come back as "Technical." or "tech support".
There is nothing to parse and nothing to validate, because the model never produces a value outside your list.
Second, every answer is a distribution, not just a label. The frustration score sits between "calm" and "frustrated but civil", and the probabilities show exactly how the model split it: a third calm, two thirds frustrated, nothing angry.
Third, all three questions were answered in parallel against the same state, and none of them saw another's answer. That independence is what makes batching cheap. TypeSafe's parallel-questions cookbook reports that 13 questions in one call came out 11.5x cheaper and 9.6x faster than 13 separate calls, with the same answers. Adding a question costs its own tokens and almost no time.
Notice the usage block too: 73 output tokens reported, and you pay for none of them. Both Jev and OpenAI's Decisions API bill input tokens only. A decision model returns a few numbers per question rather than generating text token by token, so there is no generation loop to charge for.
Same three questions, two dialects
Both APIs ask the same three kinds of question.
A yes/no question that returns the probability of yes (TypeSafe calls it a noul, OpenAI calls it a predicate).
A choice that picks one option from a set you define.
A score that places the input on ordered levels you describe.
| Row label | Jev (TypeSafe) | OpenAI Decisions API |
|---|---|---|
| Endpoint | POST https://api.typesafe.ai/v1/systemone | POST https://api.openai.com/v1/decisions |
| Model | jev-latest alias, or a pinned ID like jev-1.14.0 | gpt-6-luna, the only model offered |
| What gets evaluated | state: a string, a JSON object, or an array of strings | input: a string, or user messages with text and images |
| Questions | A map keyed by IDs you choose | An array, each question with a name |
| Yes/no answer | noul: probability from 0 to 1 | predicate: probability from 0 to 1 |
| Choice options | criteria map, up to 255 options | choices list of {value, description} |
| Score levels | criteria array, 2 to 10 levels | levels list of {label, description} |
| Can a question be refused? | No, every answer matches your schema | Yes, an answer can come back as type: "refusal" |
| Images | No, text only | Yes |
| Input price per 1M tokens | $0.042 | $0.10 |
| Output tokens | Free | Free |
To make the difference concrete, here is one hosting support ticket asked both ways. The request shapes follow each vendor's API reference.
Jev:
curl https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.14.0",
"state": {
"ticket": "Since this morning'\''s PHP 8.4 upgrade my WooCommerce checkout shows a white screen. Customers cannot pay. I think I was also billed twice this month.",
"plan": "Business"
},
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should own `ticket`?",
"criteria": {
"technical": "Errors, outages, broken sites, server problems",
"billing": "Charges, invoices, refunds",
"migration": "Moving a site from another host",
"other": null
}
},
"severity": {
"type": "score",
"instructions": "How badly is the customer'\''s business affected?",
"criteria": [
"Cosmetic, nothing is broken",
"Something is broken but a workaround exists",
"Revenue or the whole site is blocked"
]
},
"double_charge": {
"type": "noul",
"instructions": "The customer reports being charged more than once"
}
}
}'OpenAI:
curl https://api.openai.com/v1/decisions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"input": "Plan: Business. Ticket: Since this morning'\''s PHP 8.4 upgrade my WooCommerce checkout shows a white screen. Customers cannot pay. I think I was also billed twice this month.",
"questions": [
{
"type": "choice",
"name": "team",
"instructions": "Which team should own this ticket?",
"choices": [
{"value": "technical", "description": "Errors, outages, broken sites, server problems"},
{"value": "billing", "description": "Charges, invoices, refunds"},
{"value": "migration", "description": "Moving a site from another host"},
{"value": "other", "description": "Anything else"}
]
},
{
"type": "score",
"name": "severity",
"instructions": "How badly is the customer'\''s business affected?",
"levels": [
{"label": "Cosmetic", "description": "Nothing is broken"},
{"label": "Workaround", "description": "Something is broken but a workaround exists"},
{"label": "Blocked", "description": "Revenue or the whole site is blocked"}
]
},
{
"type": "predicate",
"name": "double_charge",
"instructions": "Does the customer report being charged more than once?"
}
]
}'The two dialects are close enough that an adapter between them is a page of code.
They are also already converging on one side.
OpenRouter serves Jev, OpenAI's decisions model, and Microsoft's Decision-1 (released October 9) behind a single /v1/systemone endpoint in TypeSafe's request format, according to the setup notes in Kartik Anand's benchmark repo.
It is the same pattern I wrote about in the MCP explainer: once enough vendors speak one shape, the shape becomes the product you build against.
One detail that will save you an afternoon. On October 16, 2026, OpenAI's Decisions guide says images must be inline base64 data URLs and that hosted URLs are not supported, while the API reference for the same endpoint says a public HTTPS URL is accepted. Send base64, which works under both readings.
Confidence is arithmetic
Both APIs return a confidence number on choice and score answers.
Before I put a threshold on a number, I want to know where it comes from.
TypeSafe publishes its formulas.
For a choice, confidence measures how far the top probability sits above an even split: 0 when every option is equally likely, 1 when all the probability is on one option.
Written out, it is (p_max - 1/n) / (1 - 1/n) for n options.
For a score, it compares how far the probability sits from the most likely level against how far it would sit if it were spread evenly.
OpenAI does not publish a formula. So I wrote a 30-line Python script with TypeSafe's two formulas and fed it the sample answers printed in OpenAI's own Decisions guide, plus two from TypeSafe's docs as a control. This is the output, run on Python 3.14.7:
OpenAI choice 'department' (billing .95, technical .02, shipping .01, other .02)
computed confidence=0.9333 published=0.93
OpenAI score 'severity' (cosmetic .1, workaround .7, blocked .2)
computed confidence=0.5500 score=1.10 published=0.55
TypeSafe score 'bug_severity' (0, .57, .43)
computed confidence=0.3550 score=1.43 published=0.35
Jev quickstart 'frustration' (.33, .67, 0)
computed confidence=0.5050 score=0.67 published=0.5
Torn between neighbours (0, .5, .5)
computed confidence=0.2500 score=1.50
Torn between opposite ends (.5, 0, .5)
computed confidence=0.0000 score=1.00Every published value matches, including OpenAI's. OpenAI's illustrative confidence numbers are exactly what TypeSafe's published formulas produce from the same probabilities. I only checked the guide's samples, so I won't claim OpenAI uses these formulas everywhere, but the samples are consistent with it, and Anand's benchmark checks every provider's choice confidence against the same choice formula.
Three practical consequences. Confidence is a statistic computed from the probabilities, not a second opinion from the model, so it adds no information the probabilities don't already hold. Because both APIs return the full distribution, you can compute your own measure when theirs doesn't fit, and TypeSafe's docs suggest two: the top probability on its own, or the ratio of the top probability to the runner-up. And yes/no answers carry no confidence on either API, because the probability already is the signal: 0.5 means the model has no idea, which is different from "medium".
Same scale does not mean same calibration, though. Calibration is whether answers given at 0.9 turn out right about 90% of the time, measured over many answers, and the benchmarks below show the two models differ a lot on it.
The score that hides a split
The last two lines of that script output are the ones I'd print and tape to a monitor.
A score is the probability-weighted average of the level numbers. If the model puts half its probability on "cosmetic" (level 0) and half on "fully blocked" (level 2), the score comes out as exactly 1.00, which reads as "broken, but a workaround exists". The model believes nothing of the sort. It thinks the ticket is either trivial or a full outage, and the average landed on the one level it gave zero probability.
Confidence catches it, dropping to 0.00 for the opposite-ends split and 0.25 for a split between neighbouring levels.
So the rule I'd follow: only branch on score when confidence is reasonable, and for anything that pages a human, read the probability of the level you care about directly.
What independent tests found
Vendor numbers first, because they are the ones you'll see quoted. TypeSafe's home page says "193.6x faster, 444.6x cheaper". Its own launch post explains those figures come from four workflows built by its model-capabilities team, says they are probably "on the higher end of real world gains", and notes the reference answers lean toward OpenAI and Anthropic models. I respect a vendor that writes its own caveats, and I still wouldn't plan capacity on a number measured on the vendor's own workflows. OpenAI's guide claims its endpoint is about 10x faster than the same model through the Responses API.
Two people have published independent, reproducible comparisons. Kartik Anand ran 500 synthetic bank-withdrawal routing cases through all three models via OpenRouter with byte-identical requests, every case in all six possible option orders, twice, on October 10. Fabio Akita ran a bilingual English and Brazilian Portuguese suite of operational decisions through each vendor's native API.
| Row label | Jev | OpenAI Decisions |
|---|---|---|
| Routing macro-F1 (Anand) | 89.2% | 71.4% |
| Compact bilingual accuracy (Akita) | 97.6% | 90.5% |
| Brier score, lower is better (Anand) | 0.117 | 0.296 |
| Median latency (Anand / Akita) | 233 ms / 255 ms | 214 ms / 222 ms |
| Cost per 1,000 decisions (Anand) | $0.0324 | $0.0611 |
| Same route across all 6 option orders (Anand) | 91.6% | 64.2% |
| Same route when rerun (Anand) | 476 of 500 | 500 of 500 |
Jev is more accurate and better calibrated in both tests. OpenAI is 20 to 30 milliseconds faster and perfectly repeatable: Anand got identical probabilities on 498 of 500 cases across runs, while Jev changed its route on 24. If your decisions get audited, that repeatability is a real point in OpenAI's favour.
Two numbers deserve a second look. At around 200 milliseconds for both, the network path to the provider is a big share of the total, so measure where your latency actually goes before choosing on a 20 ms gap. And list prices are not comparable directly: the same request counted 773 input tokens on Jev and 611 on OpenAI, so the 2.4x list-price gap shrank to about 1.9x in measured cost. Microsoft's Decision-1 also tied Jev on accuracy in both suites at a lower measured cost, so the market already has three serious hosted options.
The caveats are real. Both suites use synthetic cases. Both tested Jev 1.13, and Jev 1.14 shipped on October 14. OpenAI's endpoint is a public beta, and Anand reached it through OpenRouter's translation layer rather than directly. Akita timed the two vendors on different dates, so his latency pair is a rough comparison at best.
Option order is configuration
The result that surprised me most is not accuracy. In Anand's runs, listing the same three options in a different order changed OpenAI's route on 179 of 500 cases. Its macro-F1 swung from 60.6% to 86.6% depending on the order, and whichever option came first got 0.38 average probability against a neutral 0.33. Jev changed route on 41 or 42 cases, still not zero.
Nobody treats the order of options in a list as part of the logic. With these models, it is. Pin the order, keep it in version control next to the descriptions, and test every rubric change across shuffled orders before it ships.
Writing questions that hold up
TypeSafe's docs are good on this, and most of it applies to OpenAI's API too.
Ask for one snap judgment per question. "Does this message convey urgency?" is a good question. "Analyze this message and decide what to do" is a reasoning task wearing a question's clothes, and it will come back with a flat, low-confidence distribution.
Decompose, then weight in code. Instead of "how important is this ticket", ask severity, frustration, and how much detail the report gives an engineer, then combine them with weights in your code. When the business changes its mind about priorities, you change a coefficient and redeploy, instead of rewording a prompt and hoping.
Ask speculative questions up front. If the refund question only matters when the ticket turns out to be billing, ask it anyway in the same request and ignore the answer when it doesn't apply. It costs a few tokens and saves a second round trip. Only make a second request when your code truly can't build it without the first answer.
Always give a choice an exit, such as other, so a ticket that fits nothing can say so instead of being forced into the nearest bucket.
Set thresholds by the cost of being wrong, and set them per vendor. TypeSafe's docs suggest three bands: act automatically when confidence is high, ask for confirmation in the middle, and hand off to a person when it is low. In Anand's run, Microsoft's model picked the right route about as often as Jev, but under thresholds tuned on Jev's probabilities it sent around 50 legitimate cases per run to manual review against Jev's one or two. Swapping vendors means re-fitting thresholds on labelled data, every time.
Applied to the hosting ticket above, the routing code is ordinary if statements over Jev's response:
answers = response["answers"]
team = answers["team"]
queue = team["choice"] if team["confidence"] >= 0.6 else "human-triage"
# Read the level directly instead of trusting the averaged score.
blocked = answers["severity"]["probabilities"]["2"]
page_on_call = queue == "technical" and blocked >= 0.7
# Speculative question: only matters when the ticket did not go to billing.
copy_billing = queue != "billing" and answers["double_charge"]["noul"] >= 0.7Pin the model version once thresholds are tuned.
Jev's jev-latest alias moves with each release and every response names the exact version that answered, so log it.
OpenAI's docs list only gpt-6-luna, so keep a frozen set of 50 to 100 labelled cases and rerun it on a schedule to notice when the answers drift.
Where a decision model is the wrong tool
Anything that needs text out: replies, summaries, explanations, code. That is still chat-model work, and OpenAI's own guide points you to Structured Outputs when you need to extract fields into your own JSON schema.
Judgments that need real reasoning. If a careful person would need ten minutes and a notepad, a System One model will hand you a confident-looking distribution over a question it can't actually answer. Split it into smaller questions, or send it to a reasoning model.
Images and audio, with Jev. Jev reads text only, so a screenshot, a product photo, or a voice note has to become text or structured fields first. OpenAI's endpoint takes images directly, and OpenAI has a guide for connecting voice to it.
Non-English text, with care. TypeSafe says English is where Jev is most accurate and tells you to test other languages on your own content first.
Long documents.
Jev's default limits are 64k tokens per request and 32k for the state plus any single question.
A header named TypeSafe-Danger-Zone-Long-Context raises both to 262k, and TypeSafe warns it is slower and less accurate, which the header name already tells you.
And one claim I'd rephrase. TypeSafe's launch post says Jev "can't hallucinate". What it can't do is hallucinate a type: the answer will always be one of your options. It can still pick the wrong option with confidence, and TypeSafe's own agent skill says it plainly: "Typed output guarantees the interface, not truth." Schema-valid JSON is evidence that parsing will work, nothing more.
Which one I'd pick
For text decisions, Jev. It was more accurate and better calibrated in both independent benchmarks, it costs less per decision, it never refuses, and it lets you pin an exact version under your thresholds.
OpenAI's Decisions API when the input includes images, when your company already has an OpenAI agreement with zero data retention, HIPAA eligibility, or EU data residency in place, or when an audit needs the same input to give the same probabilities every time. For a lot of teams, that second reason will decide it, and it is a legitimate reason.
Either way, put a thin adapter in front, because the formats are close and the market is a month old.
The first step costs an afternoon.
Find one place in your code where a chat model is asked a question and the reply is parsed into an if.
Rewrite it as two to four atomic questions.
Label 200 real cases, run them through both APIs with the options shuffled, and fit your thresholds per vendor from what comes back.
TypeSafe named Jev after William Stanley Jevons, who argued in 1865 that more efficient steam engines would make Britain burn more coal, not less. At three cents per thousand decisions, the same bet applies to machine judgment: the questions nobody asked a model because a chat call cost seconds and cents are about to be asked millions of times a day.