API
September 20, 2026 · View on GitHub
evaluate POSTs to TypeSafe https://api.typesafe.ai/v1/systemone (or OpenRouter /api/alpha/decisions when only OPENROUTER_API_KEY is set). Auth: Authorization: Bearer <key>.
Request
{
"state": "Help! My payouts have been failing for 3 days.",
"model": "jev-latest",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?",
"criteria": { "true": "Explicitly time-sensitive", "false": "No urgency expressed" }
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"]
}
}
}
| Field | Required | Notes |
|---|---|---|
state | yes | string | object | array of text |
model | API yes; evaluate no | Defaults to jev-latest, or ~typesafe/jev-latest on OpenRouter. Pass-through if set. |
questions | yes | Map of id → {type, instructions, criteria?}. Id is not sent to the model. |
Response
{
"model": "jev-1.13.0",
"answers": {
"is_urgent": { "type": "noul", "noul": 0.92 },
"department": {
"type": "choice",
"choice": "technical",
"probabilities": { "billing": 0.08, "technical": 0.85, "sales": 0.07 },
"confidence": 0.82
},
"frustration": {
"type": "score",
"score": 1.6,
"legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
"probabilities": { "0": 0.05, "1": 0.3, "2": 0.65 },
"confidence": 0.78
}
},
"usage": { "input_tokens": 312, "output_tokens": 48 }
}
response.model is the versioned ID that answered, which may differ from the alias you sent. Log it. Score legend / probabilities keys are stringified 0-based indices.
Errors
| Status | Meaning |
|---|---|
| 400 | {"detail":{"error_type":"max_tokens_exceeded"}} — the request is over the context budget. Refused, never truncated (probed 2026-09-20 at ~192 KB and ~384 KB of state; pinned by TestLiveJevOverBudget). Not retried. |
| 401 | Missing/invalid key |
| 422 | Body failed validation (field path in body) |
| 429 | Rate limit — backoff; honor retry-after |
| 529 | Overloaded — backoff |
evaluate retries 429 and 529 with exponential backoff (up to 3), honoring retry-after (capped at 60s). Other API errors surface as tool errors. Body cap 16 MiB. Timeout 60s.
Retries are invisible from outside the binary, so a caller timing the tool cannot tell a slow answer from a backoff. EVALUATE_CALL_STATS=1 prints one line per call to stderr — {"evaluate_call":{"attempts":2,"elapsed_ms":812.4,"ok":true}} — and changes nothing else; the tool result never carries timing (ADR-004 §2). A diagnostic for eval harnesses, off by default.
Also
GET /v1/modelslists aliases (name,description,release_date). Versioned IDs likejev-1.13.0are accepted even when not listed.- Preview API (
POST /preview/evaluation,document,prompts[],probability/chosen/expectation) is gone. v1 only. Migration. - OpenAPI:
https://api.typesafe.ai/openapi.json(info.version 0.2.0 as of fetch). - Python SDK
typesafe-sdk; JS@typesafe-ai/sdk. This repo talks HTTP, not those SDKs.
Official: API reference