Jev API tutorial: first request and error troubleshooting
Call the TypeSafe Jev API with cURL, read typed answers, troubleshoot 401, 422, 429, 529, 5xx and timeout failures, and add a safe fallback.
1. Get a key and keep it server-side
Create the API key in the TypeSafe console and store it in TYPESAFE_API_KEY. Never put the key in browser JavaScript, a public repository, a screenshot, or the state that the model evaluates.
export TYPESAFE_API_KEY="your-api-key" 2. Send the smallest complete request
A request needs a model, the evidence to inspect in state, and named questions. Begin with one bounded Choice so that both the input contract and the returned answer are easy to verify.
#!/bin/sh
set -eu
# Diagnostic direct-API example. Run it only from a trusted server-side shell.
# It makes one request and leaves retry policy to the calling application.
if [ -z "${TYPESAFE_API_KEY:-}" ]; then
printf '%s\n' 'TYPESAFE_API_KEY is required; keep it server-side and do not print it.' >&2
exit 2
fi
curl --fail-with-body --silent --show-error --include \
--connect-timeout 5 \
--max-time 20 \
--request POST https://api.typesafe.ai/v1/systemone \
--header "Authorization: Bearer $TYPESAFE_API_KEY" \
--header 'Content-Type: application/json' \
--write-out '\nHTTP %{http_code}\n' \
--data-binary @- <<'JSON'
{
"model": "jev-latest",
"state": {
"message": "Checkout returns HTTP 500 after payment."
},
"questions": {
"route": {
"type": "choice",
"instructions": "Which team should handle this request?",
"criteria": {
"billing": "Charges, invoices, or subscriptions",
"technical": "Bugs, APIs, or integrations",
"human": "Ambiguous or sensitive cases"
}
}
}
}
JSON
Requires curl 7.76+. The script sends one request so that 401/422 failures or uncertain answers are not hidden behind automatic retries. It preserves the error body, HTTP status, and response headers. If you share x-typesafe-request-id with support, remove the business state first; never copy the Authorization request header or API key.
3. Read the typed response
The answer is stored under the question ID you supplied. Choice returns the selected option, a probability for every option, and confidence. Score returns a position on your ordered rubric. Noul returns P(true) and has no separate confidence field.
{
"model": "jev-1.13.0",
"answers": {
"route": {
"type": "choice",
"choice": "technical",
"confidence": 0.78,
"probabilities": {
"technical": 0.85,
"billing": 0.10,
"human": 0.05
}
}
}
} 4. Turn the answer into a safe decision path
Validate the answer shape and option keys before branching. Keep side effects in ordinary code, use stricter gates for high-impact actions, and distinguish transport failure from a valid but uncertain model result.
const REVIEW_THRESHOLD = 0.75; // Illustrative, not calibrated.
const allowed = new Set(['billing', 'technical', 'human']);
const route = response.answers?.route;
const valid = route && allowed.has(route.choice)
&& Number.isFinite(route.confidence)
&& route.confidence >= 0 && route.confidence <= 1;
console.log(!valid || route.choice === 'human' || route.confidence < REVIEW_THRESHOLD
? 'Human review required'
: 'Suggested team: ' + route.choice);
// Suggestions only; no side effects. 5. Add more questions without hiding policy
Questions against the same state can be sent together and are evaluated independently. Split compound judgments into focused questions, then combine their answers with visible, versioned business rules in code.
6. Troubleshoot by failure layer
Start with the HTTP status, then inspect the sanitized error body and x-typesafe-request-id response header. Authentication and request-shape errors need a fix, not a retry. Rate limits, overload, server failures, and network timeouts may receive bounded retries. A low-confidence 2xx answer is a valid uncertain result, not a transport error.
| Signal | Meaning | Next action |
|---|---|---|
| Missing local key; 401 | Authentication is absent or invalid | Check the server environment and Bearer format; do not auto-retry |
| 400 / 422 | Request or field validation failed | Fix fields, question type, or criteria from the error body; do not retry unchanged input |
| 403 / 404 | Permission denied, or the endpoint/resource does not match | Verify permissions, URL, and environment; do not auto-retry |
| 429 / 529 | Rate limited or temporarily overloaded | Honor Retry-After and use exponential backoff inside a total deadline |
| 408, 5xx, network, or timeout | Possibly transient transport or server failure | Retry a bounded number of times, then review or use a safe fallback |
| 2xx, but invalid response shape | Client contract drift or an invalid response | Stop the business branch; retain sanitized diagnostics and request ID |
| 2xx, valid shape, low confidence | The model returned an uncertain answer; the API did not fail | Review or abstain under calibrated policy; do not retry to fish for another answer |
A one-minute troubleshooting order
Do not start with blind retries. This order removes local and non-retryable failures first, then contains genuine transient failures inside an explicit budget.
- Confirm the request runs server-side and TYPESAFE_API_KEY is loaded; test presence without printing its value.
- Record HTTP status, sanitized error body, x-typesafe-request-id, and duration—not the key or full sensitive state.
- Fix authentication for 401/403 and the request for 400/422; only 429/529/transient 5xx/timeouts enter bounded retry.
- After a 2xx, validate question IDs, answer type, option allowlist, and numeric ranges before applying review/abstain policy.