detail field:
detail is a human-readable string for client errors, or a list of field errors for request-validation
failures (422).
Status codes
By surface
Authentication / admin (/admin/*)
403on a missing or wrong admin token (detail: "Invalid or missing admin token.").404minting a token for a non-existent user id.422minting with an invalid scope (valid:bot,tx,browser).
/meetings, /bots, /transcripts/*)
401on a missing or invalidX-API-Key;403when the key lacks the route’s scope.404reading a transcript for a meeting that was never started.POST /meetings(plan) →409when a non-terminal meeting already exists for that link;422on an unrecognizablemeeting_url.PATCH/DELETE /meetings/{id}→409once the bot lifecycle owns the record (a planned meeting is only editable while it’s still planned);409onPATCHwhen the new link collides with another active meeting.POST /bots→409when a bot is already active on that link (a planned record on the link is fine — the spawn claims it);429past your concurrency limit.- A bot that fails to join surfaces in
GET /bots/statusrather than as an HTTP error onPOST /bots— see Troubleshooting.
/agent/*)
POST /invocations→400if theunit.v1envelope is non-conformant.POST /events→400if theevent.v1event is non-conformant;422if the event carries no plan.- Workspace reads →
404(detail: "not found") if the path is absent (GET /agent/workspace/file?path=...).
Streaming errors
Streamed endpoints (POST /agent/chat, GET /agent/meeting/stream) open with 200 and report
problems in-band as SSE frames, not as a status code:
rejected— a write was blocked by governance; the frame carriesviolations.donewithok: false— the turn ended unsuccessfully.- On a dropped meeting stream, reconnect with the last
id:echoed asLast-Event-IDto resume gaplessly.
Rate limits
Two layers gate the gateway, in order:- Per-IP edge throttle (pre-auth). An optional fastapi-guard edge layer caps requests per client IP
before the API key is validated — so an IP flooding invalid keys, or rotating many keys from one IP
to defeat the per-user limiter, is answered with
429at the edge and never reaches admin-api. An IP that keeps offending past a threshold is auto-banned for a window. On by default everywhere — code, compose and Helm — so you opt out, not in:GUARD_ENABLED=false(orgateway.guard.enabled=false) removes the layer,GUARD_RATE_LIMIT_RPM=0keeps it installed but stops rate limiting. See Configuration → Gateway edge protection. - Per-user limiter (post-auth). A token-bucket limiter keyed by user id fires after the key is
resolved — it catches one token driving too much traffic across many IPs. Self-hosted: ON by
default (
GATEWAY_RATE_LIMIT_RPS/GATEWAY_RATE_LIMIT_BURST, see Configuration).
429 with a Retry-After header (seconds) — honor it before retrying.
Beyond rate limits, throughput is bounded by your own infrastructure and per-user concurrency
(max_concurrent_bots, set when the user is created). The hosted service additionally applies
plan-based limits.