Why no text generation
I use Jev when the useful result is a typed branch, not a paragraph to paste somewhere else. The System One model evaluates one state against the questions I define and returns structured answers. That keeps the final routing rule in ordinary code, where I can review it and change it without changing the question.
TypeSafe documents three useful question shapes. A Choice selects a category, a Score places the state on ordered descriptive levels, and a Noul question answers a single proposition such as urgency. See the System One guide for the model and question structure.
| Question | Use it for | Inspect in the response |
|---|---|---|
| Choice | One category from named criteria | choice, probabilities, confidence |
| Score | An ordered rubric with descriptive levels | score, probabilities, confidence |
| Noul | One proposition, such as whether a ticket asks for action today | noul |
Get a Jev API key
Create or copy a key from the TypeSafe keys dashboard. I keep it in the server environment under TYPESAFE_API_KEY; the JavaScript client reads that variable when it is constructed.
read -rsp 'TypeSafe API key: ' TYPESAFE_API_KEY
echo
export TYPESAFE_API_KEYThe prompt accepts the value without echoing it. I run the example programs from the same shell and never replace the placeholder in the source code with a real key.
Try the Playground
The official quickstart is a good first pass because it makes the state/question split visible before I write code. Follow these four operations in the Playground:
- Open the Playground and log in.
- Paste any text as the state.
- Add a question. The quickstart uses a Noul question:
Does this message express urgency? - Add more questions. Mix Noul, Choice, and Score in one call and see all results at once.
{
"urgency": {
"type": "noul",
"instructions": "Does this message express urgency?"
}
}For a hands-on Jev example inside JevPlay, try the Chat Intent Analyzer. It is a practical way to see structured intent signals before wiring the same idea into your own endpoint.
Install the JavaScript SDK
The official JavaScript SDK supports Node.js 20 or newer. In this guide I pin the documented client version and use ESM files with a .mjs suffix so the runnable examples have one clear module format.
npm install @typesafe-ai/sdk@0.6.0The SDK documentation covers the client and its typed answers at the JavaScript SDK reference.
Make the first call
Save the following as first-call.mjs. It sends one support ticket as state and asks one Choice question. The client reads TYPESAFE_API_KEY from the environment; no key belongs in this file.
import { TypeSafeClient, choice } from "@typesafe-ai/sdk";
// Reads TYPESAFE_API_KEY from your server's environment.
const client = new TypeSafeClient();
const ticket = "Our checkout integration has failed since this morning. Customers cannot pay. Please fix it today.";
const response = await client.systemOne({
model: "jev-latest",
state: ticket,
questions: {
department: choice(
"Which team should handle this support ticket?",
{
"billing": "Charges, invoices, or refunds",
"technical": "Broken software, outages, or integration failures",
"sales": "Buying, upgrading, or product questions",
"other": "None of these teams is a clear match"
}
)
}
});
console.log(JSON.stringify(response, null, 2));node first-call.mjsThe captured one-question call returned HTTP 200 and resolved the request alias to jev-1.13.0. Its response is reproduced here so I can compare the fields my branch will consume:
{
"model": "jev-1.13.0",
"answers": {
"department": {
"type": "choice",
"choice": "technical",
"confidence": 1,
"probabilities": {
"billing": 0,
"technical": 1,
"other": 0,
"sales": 0
}
}
},
"usage": {
"input_tokens": 377,
"output_tokens": 45
}
}Build the full triage request
Next I keep the same state and ask three independent questions: which team should handle the ticket, how quickly it needs attention, and whether the customer explicitly asks for action today. The request and executable program below are the same example in two forms.
{
"model": "jev-latest",
"state": "Our checkout integration has failed since this morning. Customers cannot pay. Please fix it today.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this support ticket?",
"criteria": {
"billing": "Charges, invoices, or refunds",
"technical": "Broken software, outages, or integration failures",
"sales": "Buying, upgrading, or product questions",
"other": "None of these teams is a clear match"
}
},
"priority": {
"type": "score",
"instructions": "How quickly does this ticket need attention?",
"criteria": [
"Routine: no immediate disruption",
"Soon: impaired work, but a workaround is available",
"Now: customers cannot complete a critical task"
]
},
"is_urgent": {
"type": "noul",
"instructions": "Does the customer explicitly request action today?"
}
}
}import { TypeSafeClient, choice, score, noul } from "@typesafe-ai/sdk";
const client = new TypeSafeClient();
const ticket = "Our checkout integration has failed since this morning. Customers cannot pay. Please fix it today.";
const response = await client.systemOne({
model: "jev-latest",
state: ticket,
questions: {
department: choice(
"Which team should handle this support ticket?",
{
"billing": "Charges, invoices, or refunds",
"technical": "Broken software, outages, or integration failures",
"sales": "Buying, upgrading, or product questions",
"other": "None of these teams is a clear match"
}
),
priority: score(
"How quickly does this ticket need attention?",
[
"Routine: no immediate disruption",
"Soon: impaired work, but a workaround is available",
"Now: customers cannot complete a critical task"
]
),
is_urgent: noul("Does the customer explicitly request action today?")
}
});
console.log(JSON.stringify(response, null, 2));
const { department, priority, is_urgent } = response.answers;
// Example policy thresholds, not TypeSafe defaults.
const needsReview = department.choice === "other"
|| department.confidence < 0.8
|| priority.confidence < 0.8;
if (needsReview) {
console.log({ queue: "manual-review", model: response.model });
} else {
const expedite = is_urgent.noul >= 0.8 || priority.score >= 1.5;
console.log({
queue: department.choice,
priority: expedite ? "high" : "normal",
model: response.model
});
}Run it with node triage.mjs. The three answers are evaluated independently against the same state; then the code branches on each typed result. For this captured HTTP 200 response, the branch prints:
{
queue: 'technical',
priority: 'high',
model: 'jev-1.13.0'
}{
"model": "jev-1.13.0",
"answers": {
"department": {
"type": "choice",
"choice": "technical",
"confidence": 1,
"probabilities": {
"billing": 0,
"sales": 0,
"other": 0,
"technical": 1
}
},
"priority": {
"type": "score",
"score": 2,
"confidence": 1,
"legend": {
"0": "Routine: no immediate disruption",
"1": "Soon: impaired work, but a workaround is available",
"2": "Now: customers cannot complete a critical task"
},
"probabilities": {
"0": 0,
"1": 0,
"2": 1
}
},
"is_urgent": {
"type": "noul",
"noul": 0.98
}
},
"usage": {
"input_tokens": 451,
"output_tokens": 78
}
}The 0.8 confidence checks and 1.5 Score cutoff are example policy thresholds from this program. They are not TypeSafe defaults. The response also includes token usage, which I can use when reviewing a run and planning limits; see the Jev API reference for request fields, pricing, and limits.
When independent answers disagree
Contradictory answers
Two Noul questions might ask whether a ticket must be handled today and whether it is safe to leave until next week. Each is evaluated independently. If both answers are high, my code sends the ticket to review. When the alternatives must be mutually exclusive, I use one Choice question instead of expecting separate questions to enforce that constraint. High confidence still describes the distribution, not a guarantee that the answer is correct; the confidence guide explains that distinction.
Score is a rubric position
Score is an ordered, zero-based rubric that I define. With three levels the positions run from 0 to 2. The returned score is the probability-weighted mean of those level numbers, so it is a position on the rubric, not a percentage or a measurement of the world. Read the probabilities and confidence beside it; different distributions can produce the same score. The official Score reference gives the full response shape.
| Answer | Example check | Application action |
|---|---|---|
| department | choice === "technical" | Select the technical queue only after the review checks pass |
| priority | score >= 1.5 | Mark the ticket high priority under this example policy |
| is_urgent | noul >= 0.8 | Expedite when the application policy says to do so |
Exact arithmetic belongs in code
Compute totals, counts, timestamps, thresholds, and weights with ordinary code. A Score rates text against descriptions; it is not a calculator. In this flow, Jev supplies the three judgments and JavaScript applies the review and priority rules.
Before production
- Keep
TYPESAFE_API_KEYin the server environment and run the example with a real key only from that environment. - Test representative tickets, including each Choice criterion, every Score level, and Noul cases that should not expedite.
- Inspect the full answer objects, especially probabilities and confidence, rather than storing only the winning label or number.
- Write and test the review branch before enabling an automatic queue branch. Treat the thresholds in the sample as local policy to verify, not defaults to copy blindly.
- Decide what to retain from
model,answers, andusageso a later policy change can be explained.
Frequently asked questions
Does Jev write a reply to the customer?
No. The System One examples here ask for typed decisions: a choice, an ordered score, or a Noul probability. My application owns the next action and any customer-facing text.
What does jev-latest mean in a request?
It is the model name used by these examples. Read the model field in the response to see the concrete version returned for that call; the captured examples resolve to jev-1.13.0.
Can I ask several questions in one request?
Yes. The quickstart shows Noul, Choice, and Score questions mixed in one call. Each answer remains its own typed field, so your code can apply separate policies.
Does a Noul answer include confidence?
The Noul response in this guide contains a noul value. It does not add the confidence field shown on Choice and Score answers, so branch on the value your application defines.
Is a high confidence value a correctness guarantee?
No. Confidence describes how concentrated the returned probability distribution is. It is a useful signal for a review policy, but it does not prove that an answer is correct.
Are 0.8 and 1.5 TypeSafe thresholds?
No. Those numbers are example policy choices in the triage program: 0.8 for review checks and 1.5 for the Score branch. Choose and test thresholds for your own workflow.
Is a Score a measurement such as a percentage?
No. Score uses the ordered levels you provide. Its zero-based result is a probability-weighted position on that rubric, so the level descriptions and the probability distribution matter.
What should happen when the answers point in different directions?
Keep the answers separate, apply explicit code rules, and send ambiguous or consequential cases to review. Do not let one convenient field silently override the others.