Jev
Jev answers typed questions through OpenRouter's Decisions API. Here that is POST /v1/decisions with model kai: the same request body at a new base URL with a Hanzo key, billed $0.021 per million input tokens against Jev's $0.042.
Jev is TypeSafe's decision model. Through OpenRouter it is
POST https://openrouter.ai/api/alpha/decisions with
"model": "typesafe/jev-1.13". Hanzo serves the same wire at
POST https://api.hanzo.ai/v1/decisions, where kai is
Hanzo's decision model. The question types, their criteria and the answers
are the Decisions API's on both sides, so a Jev request becomes a Kai request
when three things change: the base URL, the key and model.
Start here
# 1. mint a key — a decision takes a secret key; a pk- is refused with 403
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
-H "Authorization: Bearer $HANZO_SESSION" \
-H 'Content-Type: application/json' \
-d '{"type":"secret"}'
# 2. a Jev request with its base URL, key and model changed
curl -sS https://api.hanzo.ai/v1/decisions \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"model": "kai",
"state": "The second charge on my March invoice is still pending.",
"questions": {"is_bug": {"type": "noul", "instructions": "Is this a product defect?"}}}'
# 3. kai's row in the model catalogue, with its price; no key needed
curl -sS https://api.hanzo.ai/v1/models | jq '.data[] | select(.id == "kai")'Step 2 needs an org whose plan covers paid models; without one it answers
402 with "code": "plan_required" and a link to pick a plan. Step 3 answers
"outputs": ["decision"] and "pricing" with "input_per_million": 0.021 and
"output_per_million": 0: the dollars per million input tokens every kai call
is billed at ("prompt": "0.000000021" is the same rate per token).
What changes
The Jev column is OpenRouter's wire as the benchmark harness called it for
11,099 questions
(three_way.py);
the Kai column is what api.hanzo.ai answers.
| Jev on OpenRouter | Kai on Hanzo | |
|---|---|---|
| Endpoint | POST https://openrouter.ai/api/alpha/decisions | POST https://api.hanzo.ai/v1/decisions |
| Key | an OpenRouter key | a Hanzo secret key, sk-; see API keys |
model in the request | typesafe/jev-1.13 | kai |
model in the answer | the dated snapshot that served, typesafe/jev-1.13-20260917 in the harness's run | kai; the checkpoint is routing.checkpoint, kai-1 |
id | OpenRouter's | dec_ and 32 hex digits |
provider | the provider OpenRouter routed to | Hanzo |
usage.output_tokens | as OpenRouter counts them, not billed | 0: Kai generates nothing |
usage.cost | the call's price in dollars | not sent: the price is usage.input_tokens at $0.021 per million, and each call is a row in GET /v1/billing/usage |
| Fields only Hanzo sends | — | answer_confidence on each answer; routing, state_hash and latency_ms on the response (Decisions) |
| Price | $0.042 per million input tokens | $0.021 per million input tokens |
Hanzo checks a request before any work: 1 to 100 questions, a body of at most
16 MiB, and at most 256 characters of session_id or user. Kai reads each
question with the state beside it, at most 1,024 tokens together, and each
option whole up to 512 tokens; a request past either is refused with 422
before any work. A key that sends faster than its per-minute rate is answered
429 with Retry-After. The errors are listed on
Decisions.
The call
Jev, on OpenRouter:
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": {"message": "I was charged twice for my March invoice and the second charge is still pending."},
"questions": {
"is_bug": {"type": "noul", "instructions": "Is this a product defect?",
"criteria": {"true": "a defect", "false": "working as intended"}},
"team": {"type": "choice", "instructions": "Which team should handle it?",
"criteria": {"account": "logins and profiles",
"payments": "charges, invoices and refunds",
"product": "features and bugs"}},
"urgency": {"type": "score", "instructions": "How urgent is it?",
"criteria": ["later", "this week", "today", "now"]}
}
}'Kai, on Hanzo: the same body, with model changed.
curl https://api.hanzo.ai/v1/decisions \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kai",
"state": {"message": "I was charged twice for my March invoice and the second charge is still pending."},
"questions": {
"is_bug": {"type": "noul", "instructions": "Is this a product defect?",
"criteria": {"true": "a defect", "false": "working as intended"}},
"team": {"type": "choice", "instructions": "Which team should handle it?",
"criteria": {"account": "logins and profiles",
"payments": "charges, invoices and refunds",
"product": "features and bugs"}},
"urgency": {"type": "score", "instructions": "How urgent is it?",
"criteria": ["later", "this week", "today", "now"]}
}
}'kai-1's answer, from its v0.3.14 runtime on CPU, the release api.hanzo.ai
serves, with latency_ms left out because it is a measurement:
{
"id": "dec_b9c80aaf7c6d56d83d1cee9fa3aa013e",
"model": "kai",
"provider": "Hanzo",
"answers": {
"is_bug": {"type": "noul", "noul": 0.40156308478254465, "confidence": 0.1968738304349107,
"answer_confidence": 0.5984369152174553},
"team": {"type": "choice", "choice": "payments", "confidence": 0.9998402014322358,
"probabilities": {"account": 3.643255552012215e-05, "payments": 0.9998934676214906,
"product": 7.00998229893226e-05},
"answer_confidence": 0.9998934676214906},
"urgency": {"type": "score", "score": 1.784693056354545, "confidence": 0.08352544779484232,
"legend": {"0": "later", "1": "this week", "2": "today", "3": "now"},
"probabilities": {"0": 0.11681518789817974, "1": 0.2761086470523921,
"2": 0.31264408584613174, "3": 0.2944320792032965},
"answer_confidence": 0.31264408584613174}
},
"usage": {"input_tokens": 63, "output_tokens": 0},
"routing": {
"backend": "kai",
"checkpoint": "kai-1",
"sha256": "0834a74f2d140642a453373da09e5a128e3d2c8d9e8bb1dcaf86d7210a4ecdfc",
"calibration": "cal_e23c27a1f768bff7",
"device": "cpu",
"reach": 1024,
"trained": 1024,
"positions": 8192,
"extrapolated": false,
"reason": "explicit model='kai'"
},
"state_hash": "sha256:3893b9038056e7104d3c76ffa6f703dea7ce1d0b4bb8be153e4adbc3432b212b"
}team is settled. urgency is not: no level holds more than 0.32, and its
confidence is 0.0835. Kai's probabilities are not rounded. Billed, the call is
its 63 input tokens: 63 × $0.021 / 1,000,000 = $0.000001323.
What stays the same
- The request.
model,stateandquestions, and the optionalprovider,session_id,userandtrace. Kai accepts the optional four and uses none of them. - The questions.
choice,noulandscore, each withtype,instructionsandcriteria: a choice maps each label to its description, a noul may describetrueandfalse, a score lists its levels lowest first. - The answers.
choice,noul,score,confidence,probabilitiesandlegend, in the same shapes, under the names you gave.confidenceis(n·p_max − 1)/(n − 1)on both, so a threshold carries over as arithmetic. What a threshold buys is the model's own calibration, so tune it again on your outcomes. usage.input_tokensandusage.output_tokens.- Errors past the gateway:
{"error": {"code", "message"}}on both.
What you gain
Half the price per input token. Kai is billed $0.021 per million input tokens and Jev $0.042, with output free on both. Each model counts tokens with its own tokenizer. Kai counts the request's own text once: the state, and each question's instructions, labels, descriptions and levels.
Higher accuracy on 11 of 12 suites. On the frozen harness, kai-1, the build
api.hanzo.ai serves, is more accurate than Jev on every suite but jailbreak,
0.9175 against 0.940, and on 30 of the 51 MASSIVE languages. Those runs were
measured on CUDA in BF16; production answers on CPU. Suite by suite:
Kai's results. Its probabilities are less well
calibrated than Jev's on four suites (expected calibration error, Kai against
Jev): typed decisions 0.171 against 0.047, toxicity 0.195 against 0.177,
jailbreak 0.074 against 0.047 and Banking77 0.074 against 0.073. A threshold
tuned on Jev's confidence is to be tuned again.
Speed
No latency of the served build (kai-1, on CPU) is published yet, so none is
quoted here. Each response's latency_ms is the decision's wall time in the
model; time your own calls end to end. Each Kai replica answers one request at
a time, so a long request, many questions or many options, delays the
requests behind it on that replica.
What does not carry
A long state. Kai reads each question with the state beside it, at most
1,024 tokens together, and refuses a longer one with 422 and code
state_too_long, naming the tokens it needs. An option is read whole up to 512
tokens; a longer one is option_too_long. Jev's window is 32,000 tokens
(OpenRouter). Keep the state to what
decides the question, or split a long document across calls.
usage.cost. Jev's answer states its price; Kai's states
usage.input_tokens, billed at $0.021 per million. The org's spend is
GET /v1/billing/usage, one row per call, model kai.
Self-hosting. Kai runs at api.hanzo.ai only: its weights and its runtime
are not published, so it cannot be self-hosted.
Jev through Hanzo. typesafe/jev-1.13 sent to api.hanzo.ai is forwarded
to OpenRouter and billed $0.042 per million input tokens
(pricing). It is partly unavailable: a Jev request
can be answered 503 with code upstream_unavailable and the message
typesafe/jev-1.13 is temporarily unavailable, with a Retry-After. Retry it,
or call Jev on OpenRouter directly.