API overview
JSON over HTTPS. One base URL, one bearer header, one error shape.
https://api-833252724876.us-central1.run.app
These pages are generated from the contract the API publishes at build time, so they describe the deployment that is running right now. Download it.
Credentials
Authorization: Bearer jean_live_...
Content-Type: application/json
Two kinds of bearer token are accepted, and each page says which it needs.
- An API key:
jean_live_…, created in the dashboard, for everything your code does: submitting evals, reading jobs, checking usage. - A session token: issued to a signed-in browser, for the things a human does: creating and revoking keys, inviting people, claiming a domain, accepting terms. An API key is refused on these, so a leaked key cannot mint itself a successor or change who has access.
Keys are scoped to one organization. Nothing belonging to another organization is reachable
with yours, and a resource you may not see returns 404 rather than admitting it exists.
Requests
Every response carries X-Request-Id. Keep it; it is the fastest way for us to find what
happened, and it appears inside the error body too.
Send an Idempotency-Key when you submit an eval. Repeating the same key returns the original
job instead of queueing a second one, which makes a retry after a timeout safe.
List endpoints take limit and a cursor: GET /v1/jobs returns next_before; pass it back
as before for the next page. Its absence means you have reached the end.
Jobs
Anything that takes real work returns 202 and a job rather than blocking your request.
Poll GET /v1/jobs/{job_id}: queued → running → succeeded | failed, or
cancelled if you cancel it first. A succeeded job carries a typed result; a failed one
carries an error with a code and a message. Every job also has a
trace once it has run.
Poll every couple of seconds. An eval job is given fifteen minutes before it is considered stuck, and is retried up to three times.
Errors
Every non-2xx response, validation failures included, has the same body:
{
"error": {
"code": "quota_exceeded",
"message": "this submission would bring eval_items to 21000 this month; the free plan allows 20000.",
"request_id": "req_z9x8c7v6b5n4m3l2",
"details": {"limit": "eval_items_per_month", "max": 20000, "resets_at": "2026-02-01T00:00:00Z"}
}
}
code is from a closed set, so it is safe to branch on. message is for a human and may
change. details is present when there is something specific to say.
| Code | Status | What to do |
|---|---|---|
invalid_request | 400 | Fix the request. |
validation_failed | 422 | A field is missing or the wrong type; details says which. |
payload_too_large | 413 | Over 5 MB inline. Use an upload and input_ref. |
unauthenticated | 401 | No credential was supplied. |
invalid_token | 401 | The key or session token is not valid. |
key_revoked | 401 | This key was revoked. Create another. |
forbidden | 403 | Valid credential, not allowed to do this. |
session_required | 403 | This route needs a signed-in session, not an API key. |
org_required | 403 | The credential resolves to no organization. |
not_found | 404 | No such resource in your organization. |
conflict | 409 | It already exists. |
invalid_state | 409 | Not a legal transition, such as cancelling a finished job. |
quota_exceeded | 429 | A plan limit. details names it and when it resets. |
rate_limited | 429 | Too many requests this minute. Honour Retry-After. |
internal | 500 | Ours. Retry with the same idempotency key, and quote request_id. |
unavailable | 503 | Retry with backoff. |
Rate limits
Counted per organization, per minute: 60 requests on free, 300 on dev, 1,000 on team. Every response tells you where you stand.
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 41
X-RateLimit-Reset: 1768470000
Retry-After: 18
Retry-After appears on 429. Honour it rather than retrying immediately; repeated failed
authentication is throttled separately and more harshly.
Versions
The contract is versioned in info.version, and info.x-git-sha names the exact build
serving it. Additions (a new endpoint, a new optional field) happen without a version
change; anything that could break a client does not happen silently.