API reference
How to read the reference — base URLs, authentication, the error envelope, SSE conventions and where changes are announced.
The reference is generated from hand-authored OpenAPI 3.1 documents that mirror the code route by route. A parity test in the repo fails whenever a public route exists without documentation, or a documented route no longer exists, so what you read here is what the servers register.
Three services, three base URLs
| Service | Base URL | Reference | Auth |
|---|---|---|---|
| Gateway (inference + platform) | https://ai.codai.ro | Gateway | Authorization: Bearer codai_… |
| Resolve (verified bug fixes) | https://resolve.codai.ro | Resolve | same codai_ key |
| Auth (OpenID Provider) | https://auth.codai.ro | Auth | OAuth 2.1 / OIDC |
Try-it panels in each operation send real requests from your browser. Paste a key you are happy to spend a few cents with; the panels never store it.
Authentication
Every gateway and Resolve call carries a codai API key in the bearer header. Keys start with codai_, are shown once in the hub and are revocable there. /v1/messages also accepts the Anthropic-style x-api-key header. Ephemeral tokens from POST /v1/tokens are accepted only by the realtime WebSocket — a raw key in a query string is refused.
Apps that sign users in obtain a scoped key through the OIDC connect flow instead of asking for one.
The error envelope
The gateway answers every failure with one JSON shape:
{
"error": {
"message": "Invalid request body",
"type": "invalid_request_error",
"code": "bad_request",
"details": { "fieldErrors": { "messages": ["Required"] } }
}
}| Status | code | When |
|---|---|---|
| 400 | bad_request | Body or query failed validation — details holds the field errors |
| 401 | invalid_api_key | Missing, revoked or paused key |
| 402 | subscription_inactive | The subscription behind the key is inactive |
| 403 | forbidden | Model allow-list, role or ownership check failed |
| 404 | not_found | No such resource, or it belongs to someone else |
| 409 | lease_held, seq_conflict, task_not_confirmable, host_offline | Optimistic-concurrency conflicts on sessions, tasks and hosts |
| 413 | payload_too_large | Request body over 12 MB |
| 429 | rate_limit_exceeded, quota_exceeded | Rate limit / spend cap, or task quota — read Retry-After |
| 502 | upstream_error | Every upstream lane failed after failover |
| 503 | search_unavailable | The search service behind /v1/tools/search is down |
| 500 | internal_error | Unexpected fault; already reported to us |
Resolve and Auth are different services with their own, simpler shapes: Resolve returns { "error": "<string>" }, Auth returns the OAuth { "error", "error_description" } pair. Each reference page documents its own.
Streaming conventions
- SSE endpoints answer
text/event-stream. Chat Completions, Messages and Responses use their wire format's framing; the usage chunk on a Chat Completions stream is always emitted, whateverstream_optionssays. - codai's own streams (agents, sessions, hosts) use named
event:frames documented on each operation, plus: pingcomments as heartbeats where noted. - No stream emits
id:lines andLast-Event-IDis not read. Resume with the cursor the endpoint offers (afteron sessions) or accept a full replay (agents). - Cost headers on a stream read
x-codai-cost-estimate: pending; the metered cost lands later onGET /v1/receipt.
Versioning and changes
The gateway is unversioned beyond the /v1 prefix. Additive changes (new fields, new headers, new endpoints) ship without notice; anything that removes or renames is announced in the changelog first. Field names are returned exactly as the code sends them — a few objects mix camelCase and snake_case, and the reference reflects that rather than hiding it.