Guide
Jev Python Guide
The official TypeSafe Python SDK works against this site unchanged: install typesafe-sdk, point the base URL at jev-api.org, and call system_one with a jev-api.org key. This guide covers install, configuration, the three question types, reading answers, and the async client.
Updated
Install
The package is typesafe-sdk on PyPI and needs Python 3.10 or newer. The source lives at github.com/typesafe-ai/typesafe-sdk-python.
pip install typesafe-sdk
# or: uv add typesafe-sdk
# requires Python 3.10+Configure the key and base URL
The SDK reads two environment variables. TYPESAFE_API_KEY takes a key from your jev-api.org dashboard — TypeSafe console keys do not work here — and TYPESAFE_BASE_URL must be https://jev-api.org so requests reach the TypeSafe-compatible /v1/systemone endpoint instead of api.typesafe.ai.
# .env — a key from your jev-api.org dashboard
TYPESAFE_API_KEY=jeva_...
TYPESAFE_BASE_URL=https://jev-api.orgYou can also pass them inline as TypeSafeClient(api_key=..., base_url="https://jev-api.org"). The default model is jev-latest; override it with TYPESAFE_DEFAULT_MODEL or the model argument. The default timeout is 10 seconds.
First call: Noul, Choice and Score
client.system_one takes a state — string, object, or array — and a questions map built from the Noul, Choice and Score classes. Each call below returns in about the model's documented latency band of 70 to 500 ms and uses 1 credit on this site.
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
with TypeSafeClient() as client: # reads TYPESAFE_API_KEY and TYPESAFE_BASE_URL
response = client.system_one(
state={"document": "I was charged twice. Please fix this ASAP."},
questions={
"billing": Noul(instructions="Is this ticket about billing?"),
"tone": Choice(
instructions="What is the customer's tone?",
criteria={"calm": None, "frustrated": None, "angry": None},
),
"urgency": Score(
instructions="How urgent is this ticket?",
criteria=["can wait", "this week", "today"],
),
},
)
print(response.nouls["billing"].noul) # 0.0 .. 1.0
print(response.choices["tone"].choice) # winning option
print(response.scores["urgency"].score) # fractional levelReading answers
The response object exposes typed views: response.nouls, response.choices, and response.scores, keyed by your question ids. A Noul answer carries .noul, the probability of yes between 0 and 1. A Choice answer carries .choice, .probabilities and .confidence. A Score answer carries a fractional .score plus .legend, .probabilities and .confidence.
Use confidence as the handoff signal. When it is low or two options sit close together, route the case to a person or ask a narrower follow-up question instead of lowering the threshold blindly. You can also read the raw per-question object through response.answers["id"].
Async client
AsyncTypeSafeClient has the same interface with awaitable calls — useful inside FastAPI endpoints or workers that fan out several states at once.
from typesafe_sdk import AsyncTypeSafeClient, Noul
async with AsyncTypeSafeClient() as client:
response = await client.system_one(
state="I was charged twice for my subscription.",
questions={"refund": Noul(instructions="Is the customer asking for money back?")},
)
print(response.answers["refund"])Without the SDK: plain requests
If you do not want the dependency, POST the JSON body yourself. This site's simple endpoint /api/v1/decisions accepts the same state-plus-questions shape with model jev-1.13 and slightly tighter limits (1 to 6 questions, 2 to 8 Choice options).
import os
import requests
res = requests.post(
"https://jev-api.org/api/v1/decisions",
headers={"Authorization": f"Bearer {os.environ['JEV_API_KEY']}"},
json={
"model": "jev-1.13",
"state": "I was charged twice for my subscription.",
"questions": {
"refund": {"type": "noul", "instructions": "Is the customer asking for money back?"}
},
},
timeout=30,
)
print(res.json()["answers"]["refund"])Errors and timeouts
jev-api.org returns JSON errors with a detail field. The codes you will see:
- 401 — missing or rejected API key. Check the Authorization: Bearer header and that the key came from this site.
- 402 — the key is valid but the balance cannot cover the call. Add credits on the pricing page.
- 422 — the body failed validation, or the model rejected it (for example over the 64k/32k token limits). The message names the field.
- 429 — rate limited. Back off and retry.
- 502 — the decision service did not return answers. No credit is used.
FAQ
Can I use my TypeSafe console key with this site?
No. Keys are not interchangeable: TypeSafe keys work on api.typesafe.ai, and keys from the jev-api.org dashboard work here. Set TYPESAFE_API_KEY to a jev-api.org key and TYPESAFE_BASE_URL to https://jev-api.org.
Which endpoint does the SDK call on this site?
With base_url set to https://jev-api.org, the SDK posts to /v1/systemone — the TypeSafe-compatible endpoint, which accepts the full question set (up to 256 questions, 255 Choice options, structured instructions) and bills 1 credit per successful call.
Does the SDK retry automatically?
The TypeSafe SDK ships with a default retry policy for transient errors such as 429; you can override it through the client's retry option. Validation errors (422) should be fixed in the body, not retried.
What Python version do I need?
Python 3.10 or newer. Both sync (TypeSafeClient) and async (AsyncTypeSafeClient) clients are in the typesafe-sdk package.
How do I know which model version answered?
The response's model field always reports the versioned id that served the request — jev-1.13.0 today — even when you sent an alias like jev-latest.
Send the first decision
Create a key, set the two environment variables, and run the snippet above.