Errors
Errors return a non-2xx status with a JSON body shaped like:
{
"detail": "session not found"
}
Status codes
| Code | Meaning |
|---|---|
| 400 | Bad request (e.g. unknown r_level filter, malformed JSON) |
| 401 | Auth missing or invalid |
| 403 | Auth valid but you don't own this resource |
| 404 | Resource doesn't exist |
| 409 | Conflict (e.g. creating a client slug that already exists — admin-only) |
| 422 | Validation error from Pydantic (body shape doesn't match) |
| 500 | Internal — please tell us |
Idempotency
POST /v1/sessions with the same (your client, external_id) returns the existing session — safe to retry.
POST /v1/sessions/{id}/messages is not idempotent on retry. If you retry a 5xx, you may end up with a duplicate turn. Use sequence numbers in your own system if duplicates would be a problem.
Scoring failures
If our scoring model is briefly unavailable, message append still succeeds but returns:
{
...
"scores": {},
"prs": 0.0,
"r_level": "R0",
"score_error": "no loadable completed checkpoint for prod model 'tfidf-lgbm-v2'"
}
In this case the turn is persisted but un-scored — we'll backfill later. Treat score_error as a signal to retry or escalate if it persists.