What Is Jev? TypeSafe's Decision Model Explained for Developers
Kenny Rogers ·

What is Jev? And what should you use it for? It’s TypeSafe’s decision model that takes a passage of text plus a typed question, and returns a typed answer that is one of the choices among your predefined set of answers. So if I have a question about ticket routing, like should this ticket go to billing, technical, or account, Jev looks at the text of the ticket and answers with one of the predefined choices, billing. But it’s even cooler than that because it gives you the calibrated probability of the answer choice as well as the probability of each of the other choices. Oh, and there’s also an overall confidence score (more on that later). TypeSafe calls these System One models, and Jev is the first of them.
But what does Jev actually return? Let’s find out! It returns three primitives, and there are real API responses for each below. Here’s how to read the probabilities without fooling yourself. And here’s how to call Jev with an OpenRouter API key.
What is a decision model (System One model)?
Jev is a decision model. A System One model is one that takes in a piece of state and, given that state, returns a decision. In this case, Jev’s decision is always a typed value you defined in advance, plus an actual probability number from zero up to one. The name comes from what Daniel Kahneman called System One in Thinking, Fast and Slow: fast, pattern-matching thinking, where System Two is slow and deliberate.
So what makes this a decision model and not some oracle? A decision model must return from a small set of values you decided beforehand. There is no free-form text, so nothing you need to parse or worry about hallucination.
Now, TypeSafe calibrated Jev so that when it says 0.8 (an 80% chance), it means that, like an 80% chance of rain, Jev is right about that kind of answer about 80% of the time (System One concepts).
That’s only true when you average across many answers. Any single answer can still be wrong. That makes a practical difference; it’s a point we’ll come back to later.
Jev vs LLM: what each one returns
The difference between Jev and LLMs becomes clear once you understand what both do. Both can read natural language, but the split is entirely on the output side.
Here’s how the two compare side by side:
| Generative LLM | Jev | |
|---|---|---|
| Output | Tokens: prose, code, JSON you asked for | A typed answer plus a probability distribution |
| Shape guaranteed? | Only with structured outputs, and the content can still be wrong | Always one of your options, levels, or a yes/no probability |
| Uncertainty | Hidden inside the prose | Returned as numbers you can threshold on |
| Input | Text, and often images, audio, or video | Text only (strings, JSON objects, arrays of text) |
| Price on OpenRouter | Varies by model, input and output billed | $0.042 per million input tokens, output free |
Whenever you need words as the main output, for example, a reply, summary, or code patch, reach for an LLM. When you need to make a code-actionable decision, use Jev. In just about every real-world case, the two systems work together: Jev routes and verifies things, while the LLM provides the language. And that’s what the companion post Jev vs LLM: when to use each is all about. It benchmarks that split on 140 support cases and ties the two together in TypeScript.
Jev’s three primitives: Choice, Score, and Noul
Every question you ask Jev is one of three types. A request has two parts: state, the text to evaluate, and a questions object containing one or more typed questions. Jev answers all in one pass, each answer returned under the key you gave.
The examples below are real calls I placed on September 21, 2026. They run against typesafe/jev-1.13 through OpenRouter’s Decisions API, and if you set OPENROUTER_API_KEY, you can run the examples as written.
Choice: pick one option from a fixed set
When you get a Choice from the API, it always has three things: the selected option, a probability for every option, and a confidence value.
This command sends the request:
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "My invoice for September shows two charges for the Pro plan. I only have one workspace.",
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Charges, invoices, and refunds",
"technical": "Bugs, outages, and broken features",
"account": "Login, password, and profile changes"
}
}
}
}'
This response shows what the API returned:
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"team": {
"type": "choice",
"choice": "billing",
"probabilities": { "technical": 0, "account": 0, "billing": 1 },
"confidence": 1
}
},
"usage": { "input_tokens": 357, "output_tokens": 38, "cost": 0.000014994 },
"id": "gen-dec-1790013975-0Cpw7ykY8YRfP85l4eqS",
"provider": "TypeSafe"
}
Jev reads the criteria descriptions before making a choice, so write them as you would brief a new hire. TypeSafe’s API reference says the question id (team here) is never sent to the model. All the meaning has to be in the descriptions. Second, the model field names the exact snapshot OpenRouter served.
Score: rate on ordered levels you describe
Score takes an ordered array of level descriptions. It returns a number representing the score for the text and a legend mapping every index in the array to its description, plus Jev’s probability for each level and a confidence value.
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "Export to CSV fails with a 500 error for every workspace in our org since this morning. We can still download JSON exports, but our finance team can only import CSV.",
"questions": {
"severity": {
"type": "score",
"instructions": "How severe is this bug report?",
"criteria": [
"Cosmetic; no impact to functionality",
"Broken or degraded feature, but a workaround exists",
"Blocking issue; no workaround exists"
]
}
}
}'
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"severity": {
"type": "score",
"score": 1.15,
"legend": {
"0": "Cosmetic; no impact to functionality",
"1": "Broken or degraded feature, but a workaround exists",
"2": "Blocking issue; no workaround exists"
},
"probabilities": { "0": 0, "1": 0.85, "2": 0.15 },
"confidence": 0.77
}
},
"usage": { "input_tokens": 354, "output_tokens": 17, "cost": 0.000014868 },
"id": "gen-dec-1790013976-PwmCQLkieCXkoK3qb8Ja",
"provider": "TypeSafe"
}
The score, 1.15, is the probability-weighted average of the levels, 0.85 times 1 plus 0.15 times 2, as shown in the above response. Your own run will land a few hundredths away because Jev’s probabilities vary slightly between calls. Reading “we can still download JSON exports” as a workaround, Jev lands mostly on level 1. It also gives some weight to “blocking” because finance can’t use the workaround.
Because scores are on an ordinal scale, you must keep the levels in a real order from least to most and describe each level in words. The TypeSafe docs list the Score primitive details, including a cap of 10 levels.
Noul: probability that a yes/no proposition holds
Noul is the simplest primitive of the three. It takes as input a proposition and returns the probability that the answer is yes.
The command below sends the request:
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "I was charged twice for September. Please refund the duplicate charge.",
"questions": {
"refund": {
"type": "noul",
"instructions": "Is the customer asking for money back?"
}
}
}'
Here’s the response that the API returned:
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"refund": { "type": "noul", "noul": 0.99 }
},
"usage": { "input_tokens": 287, "output_tokens": 20, "cost": 0.000012054 },
"id": "gen-dec-1790013977-LxrJdV3aOEliWmRmdmh9",
"provider": "TypeSafe"
}
Keep in mind that a Noul answer has no separate confidence field. The probability is the whole answer. The next section explains how to interpret it.
Ask all three in one call
You might even be asking yourself, hey, how many questions can go in that questions object? As many as you want. The state doesn’t have to be a plain string: it can be a JSON object with all the flexibility that implies. Your instructions can then refer to its fields by name in backticks.
Let’s say you want to ask a Choice, a Score, and a Noul all at once from the same ticket domain in a single round trip.
The command which sent this request is shown below.
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": {
"ticket": "Hi, I was charged twice for my Pro subscription this month. Please fix this before my next payroll run on Friday."
},
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle `ticket`?",
"criteria": {
"billing": "Charges, invoices, and refunds",
"technical": "Bugs, outages, and broken features",
"account": "Login, password, and profile changes"
}
},
"urgency": {
"type": "score",
"instructions": "How urgent is `ticket`?",
"criteria": [
"No deadline; routine question",
"Customer wants a fix soon but nothing is blocked",
"Customer names a deadline or something is blocked now"
]
},
"refund": {
"type": "noul",
"instructions": "Is the customer in `ticket` asking for money back?"
}
}
}'
The response shown below is the one the API returned.
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"team": {
"type": "choice",
"choice": "billing",
"probabilities": { "technical": 0, "billing": 1, "account": 0 },
"confidence": 1
},
"urgency": {
"type": "score",
"score": 2,
"legend": {
"0": "No deadline; routine question",
"1": "Customer wants a fix soon but nothing is blocked",
"2": "Customer names a deadline or something is blocked now"
},
"probabilities": { "0": 0, "1": 0, "2": 1 },
"confidence": 1
},
"refund": { "type": "noul", "noul": 0.82 }
},
"usage": { "input_tokens": 447, "output_tokens": 69, "cost": 0.000018774 },
"id": "gen-dec-1790013867-chEjwDPvoiiffM3J3eDF",
"provider": "TypeSafe"
}
If all went smoothly, each answer lands under the key you gave it. In code, you’ll then be able to go answers.team.choice and answers.refund.noul directly.
How to read Jev probabilities and confidence
There are three guidelines to keep in mind when reading Jev’s probabilities and confidence.
When the Noul is near 0.5, that’s Jev saying it doesn’t know what to think. Read it as a state of uncertainty. Let’s do a quick experiment to show you how. I asked the same refund question, “Is the customer asking for money back?”, about the state “There are two charges on my card this month. If one of them is a mistake, what are my options?” on two separate calls. One returned a Noul of 0.52, and on the exact same message (yes, verbatim), Jev returned a Noul of 0.49. If you’re tempted to say, “Aw, the customer is just asking for money back,” consider that the customer never once asked for money back in the message. They’re circling it, but Jev is feeling the tug in two directions, and you and I wouldn’t be certain either. Meanwhile, the explicit refund request in the earlier Noul call hit 0.99. See the pattern? When the outcome is in the middle, treat it as a third outcome, one you can take action on. Ask a follow-up question, or hand the ticket over to a person.
Choice and Score confidence is a measure of how concentrated the distribution is. The more all of the distribution’s weight falls on one outcome, the higher the confidence. A choice concentrated around a single option yields a high confidence number, on a scale from 0 to 1. The higher the confidence number, the less hesitation there is between the options. But just because Jev concentrates its attention on an option doesn’t mean the answer is right. TypeSafe draws its confidence numbers from the shape of its probabilities, not their actual correctness (confidence docs). In the extreme case, a model that assigns all of its weight to one outcome will have a confidence of 1.0, while less concentration will diminish that number.
You can verify that with the billing example. This state was muddier: “I upgraded to Pro yesterday but the dashboard still says Free and I got charged. Which one is it?” The response below shows you exactly what the API returned:
{
"type": "choice",
"choice": "billing",
"probabilities": { "account": 0.02, "technical": 0.19, "billing": 0.79 },
"confidence": 0.69
}
Jev still picks billing, although a fifth of its weight sits on technical, and the confidence drops to 0.69. Confidence is a measure of how torn Jev is between options, but whether those options are well chosen is up to you.
Pick your own thresholds from your own labeled data. The operational advice is simple. Calibration holds in aggregate, which means that the right cutoff for you depends on how expensive your mistakes are. TypeSafe’s confidence guide suggests three bands: Act automatically if the confidence passes one threshold. Proceed with caution in the middle, flagging for an additional layer of user confirmation or simply flagging for review. And route to a person or different system if the confidence falls below the other threshold.
How do you set those bands? Label a few hundred examples. For each of them, inspect where Jev’s probabilities fall. Then pick bands that ensure the error rate within the act band is one that you can live with.
What Jev leaves to your code
There are a few things that Jev doesn’t do that you might expect it to do as a model. Those stay in your own code or with an LLM:
-
Text generation. Every Jev response is one of the three typed shapes shown earlier, never a completion, never a JSON body, never an explanation. When a pipeline needs a sentence, an LLM produces it and Jev checks it. The verified cascade cookbook shows this pattern.
-
Visible reasoning. Jev returns only the distribution over your options, nothing else. No rationale, no chain of thought. Keep the distribution as the signal to log and threshold on. For an audit trail, log the request ID, names of the questions, the probabilities, and the threshold your code applied. Keep the
stateitself out of the log because tickets and documents contain customer data. -
Tool calls, conversation, or multi-step plans. Jev answers the questions you sent about the state you sent in one round trip. It makes no tool calls, holds no conversation, and takes no steps. It works inside agents as the deciding part, for example gating tool calls.
That’s three! Here are two more constraints to keep in mind:
-
Jev accepts text only, strings, JSON objects, or arrays of text. No images, audio, or video.
-
Exact arithmetic, date math, and threshold comparisons aren’t Jev’s job. Compute them first and hand Jev the semantic part.
When to use the Jev model
Now that you’ve got the idea of what Jev does, let’s talk about when it makes sense: when the answer your LLM is expected to provide fits into an enum, a boolean, or a number.
When to use Jev
- Routing and triage: you determine queue, owner, priority. One Choice and one Score per ticket.
- Classification and tagging at scale: cheap enough (see pricing below) to run on every row; the output is already a label.
- Gating agent actions: before a destructive tool run, ask a Noul whether the user’s request authorizes deleting these files; block below your threshold.
- Verifying LLM output: draft with an LLM, then ask Jev whether that draft is grounded in the policy text you supplied.
- Ranking and filtering: score candidates against described levels, then sort.
When to skip Jev
When the answer needs to be prose, or when the input is an image, or when the logic is deterministic. An exact rule? A regex or database lookup beats any model. To poke at it before coding, Jev Lab runs several of these patterns in the browser on live calls.
How to access Jev on OpenRouter
You’ll need an OpenRouter API key and a model ID: typesafe/jev-1.13 (pinned) or ~typesafe/jev-latest (alias to track the newest version). The same call can be made three different ways: using the OpenRouter SDK, the TypeSafe SDK, or raw HTTP.
Decisions API with the OpenRouter SDK
The code below shows the same call as the one in the muddier ticket in the confidence section: a POST https://openrouter.ai/api/alpha/decisions call via the OpenRouter TypeScript SDK’s alpha.decisions. The code has been tested with SDK 1.3.11, installed via npm install @openrouter/sdk. Because of the top-level await, run it with bun run or as an ES module. Let’s go:
import { OpenRouter } from '@openrouter/sdk';
const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
const decision = await openrouter.alpha.decisions.create({
decisionsRequest: {
model: 'typesafe/jev-1.13',
state: 'I upgraded to Pro yesterday but the dashboard still says Free and I got charged. Which one is it?',
questions: {
team: {
type: 'choice',
instructions: 'Which team should handle this ticket?',
criteria: {
billing: 'Charges, invoices, and refunds',
technical: 'Bugs, outages, and broken features',
account: 'Login, password, and profile changes',
},
},
},
},
});
const team = decision.answers.team;
if (team.type === 'choice') {
console.log(team.choice, team.probabilities, team.confidence);
}
billing { account: 0.01, technical: 0.15, billing: 0.84 } 0.77
Note that the numbers here differ from the earlier run on the same ticket (0.79 and 0.69). Jev’s probabilities vary somewhat from call to call, so it’s best to set threshold values on bands rather than exact values. Finally, note that the OpenRouter SDK returns decision.usage.inputTokens and decision.usage.outputTokens in camelCase, rather than the raw API’s input_tokens and output_tokens.
TypeSafe JavaScript SDK pointed at OpenRouter
The second calling path is the TypeSafe SDK: point the SDK’s base URL at OpenRouter and pass your OpenRouter API key as the API key. The SDK will append /v1/systemone to the base URL. Bare model names like jev-1.13 map to typesafe/jev-1.13. The code below is the same call to Jev, using @typesafe-ai/sdk; the code has been tested with SDK 0.6.0:
import { TypeSafeClient } from '@typesafe-ai/sdk';
const client = new TypeSafeClient({
apiKey: process.env.OPENROUTER_API_KEY,
baseURL: 'https://openrouter.ai/api',
});
const result = await client.systemOne({
model: 'jev-1.13',
state: 'Export to CSV fails with a 500 error for every workspace in our org since this morning. We can still download JSON exports, but our finance team can only import CSV.',
questions: {
severity: {
type: 'score',
instructions: 'How severe is this bug report?',
criteria: [
'Cosmetic; no impact to functionality',
'Broken or degraded feature, but a workaround exists',
'Blocking issue; no workaround exists',
],
},
},
});
console.log(result.answers.severity);
{
type: "score",
score: 1.19,
legend: {
"0": "Cosmetic; no impact to functionality",
"1": "Broken or degraded feature, but a workaround exists",
"2": "Blocking issue; no workaround exists",
},
probabilities: {
"0": 0,
"1": 0.81,
"2": 0.19,
},
confidence: 0.72,
}
TypeSafe Python SDK pointed at OpenRouter
The same idea in Python, with typesafe-sdk 0.7.1 (pip install typesafe-sdk):
import os
from typesafe_sdk import TypeSafeClient
client = TypeSafeClient(
api_key=os.environ["OPENROUTER_API_KEY"],
base_url="https://openrouter.ai/api",
)
result = client.system_one(
model="jev-1.13",
state="There are two charges on my card this month. If one of them is a mistake, what are my options?",
questions={
"refund": {"type": "noul", "instructions": "Is the customer asking for money back?"}
},
)
print(result.answers["refund"])
type='noul' noul=0.49
Keep your OpenRouter key safe and sound on the server-side. The TypeSafe SDK guide has details about the alias mapping, the response envelope, and error handling.
Jev pricing and context window
From the OpenRouter model page on September 21, 2026:
| Jev 1.13 on OpenRouter | |
|---|---|
| Input price | $0.042 per million tokens |
| Output price | $0 |
| Context window | 32,000 tokens |
| Modality | Text in, decisions out |
| Provider | TypeSafe |
To sum up: the three-question ticket call above used 447 input tokens and cost $0.000019 (about two thousandths of a cent). A million tickets of that size come to about $19. Output is free and the answers are tiny (between 17 and 69 tokens across every call above). Because the key order inside probabilities shifts between calls, I suggest you index entries by name rather than by position.
OpenRouter lists a 32k context window. TypeSafe’s model page lists 64k tokens per request total, with 32k for your state plus the longest question.
Is Jev open source?
Quick question: Is Jev proprietary? The answer is obvious (yes), but let me explain. TypeSafe hasn’t published Jev’s weights or a paper on Jev, and everyone who wants to use Jev calls the same hosted model.
Try it
Now it’s time to see all this with your very own OpenRouter API key! Grab it, paste one of the curl commands above, and change the state to a real message from your own system.
- If you would rather use TypeSafe’s client, read the TypeSafe SDK guide.
- Work through how to gate tool calls with Jev, putting a Noul in front of an agent action.
- Read Jev vs LLM: when to use each for benchmarks, and the route-then-write pattern.
- And before you budget, check the model page to see what the current pricing looks like for that model!
FAQ
What is Jev?
Jev is a decision model made by TypeSafe. You interact with most models by sending them text. You don’t do only that with Jev. Instead, you send it text plus one or more typed questions, and the answers you get back are typed answers with calibrated probabilities. No text generation. TypeSafe calls this category of models System One models.
Who makes Jev?
Jev is a decision model. It was built by TypeSafe, an AI lab that was founded by Diogo Almeida, Erik Gafni, and Sasha Sheng. Jev was released in early access on September 15, 2026. Right now, the current version is Jev 1.13. You can get it through TypeSafe’s own API or through OpenRouter.
Is Jev an LLM?
No. Jev is a non-generative decision model, while LLMs are generative models: Jev reads your natural language like LLMs do, but then generates no text, no tokens. Jev returns one of three answer shapes, depending on your question: either a choice among a set of pre-determined options, a score on a defined set of levels, or the probability of yes vs no. Because there’s no generated text, there’s nothing to parse and no chance of output hallucination.
What is Jev used for?
Okay, so you’ve got to decide which support ticket gets routed to which team. You’ve got to classify this blog post into a content type. You want to gate an AI agent’s tool call based on a confidence threshold. You’ve got to rank a set of candidates in order. And you’ve got to verify a draft against a policy. Sound familiar? In these situations, you’re the one who would previously ask an LLM a question, then use a regex to parse out a boolean value or an enum value. Meet Jev, the model purpose-built for just these kinds of decisions, replacing that LLM-plus-regex workflow.
How do I access Jev?
So you want to access Jev through OpenRouter? Well, you only need an OpenRouter API key and a POST to the following URL: https://openrouter.ai/api/alpha/decisions, with model typesafe/jev-1.13. Alternatively, you can use the decisions client in OpenRouter’s SDK. You can even use the TypeSafe JavaScript and Python SDKs. Just set the base URL to https://openrouter.ai/api and pass your OpenRouter key.
What does Jev cost?
Right now, Jev 1.13 on OpenRouter costs $0.042 per million input tokens (and output is completely free) as of September 21, 2026. Which means a typical three-question call on a short support ticket used around 450 input tokens, costing you roughly two thousandths of a cent. So always remember to check out the model page to see current pricing.
Is Jev open source?
No. Jev is a proprietary model and as such there are no published weights and no paper. You can, however, call it through the TypeSafe API or OpenRouter.