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 OpenRouterKai on Hanzo
EndpointPOST https://openrouter.ai/api/alpha/decisionsPOST https://api.hanzo.ai/v1/decisions
Keyan OpenRouter keya Hanzo secret key, sk-; see API keys
model in the requesttypesafe/jev-1.13kai
model in the answerthe dated snapshot that served, typesafe/jev-1.13-20260917 in the harness's runkai; the checkpoint is routing.checkpoint, kai-1
idOpenRouter'sdec_ and 32 hex digits
providerthe provider OpenRouter routed toHanzo
usage.output_tokensas OpenRouter counts them, not billed0: Kai generates nothing
usage.costthe call's price in dollarsnot 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, state and questions, and the optional provider, session_id, user and trace. Kai accepts the optional four and uses none of them.
  • The questions. choice, noul and score, each with type, instructions and criteria: a choice maps each label to its description, a noul may describe true and false, a score lists its levels lowest first.
  • The answers. choice, noul, score, confidence, probabilities and legend, in the same shapes, under the names you gave. confidence is (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_tokens and usage.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.

Was this page useful?
Last updated Oct 12, 2026