Skip to main content
All public routes are fronted by the gateway and return conventional HTTP status codes. Error bodies follow FastAPI’s convention — a JSON object with a 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/*)
  • 403 on a missing or wrong admin token (detail: "Invalid or missing admin token.").
  • 404 minting a token for a non-existent user id.
  • 422 minting with an invalid scope (valid: bot, tx, browser).
Meetings API (/meetings, /bots, /transcripts/*)
  • 401 on a missing or invalid X-API-Key; 403 when the key lacks the route’s scope.
  • 404 reading a transcript for a meeting that was never started.
  • POST /meetings (plan) → 409 when a non-terminal meeting already exists for that link; 422 on an unrecognizable meeting_url.
  • PATCH/DELETE /meetings/{id}409 once the bot lifecycle owns the record (a planned meeting is only editable while it’s still planned); 409 on PATCH when the new link collides with another active meeting.
  • POST /bots409 when a bot is already active on that link (a planned record on the link is fine — the spawn claims it); 429 past your concurrency limit.
  • A bot that fails to join surfaces in GET /bots/status rather than as an HTTP error on POST /bots — see Troubleshooting.
Agent API (/agent/*)
  • POST /invocations400 if the unit.v1 envelope is non-conformant.
  • POST /events400 if the event.v1 event is non-conformant; 422 if 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 carries violations.
  • done with ok: false — the turn ended unsuccessfully.
  • On a dropped meeting stream, reconnect with the last id: echoed as Last-Event-ID to resume gaplessly.

Rate limits

Two layers gate the gateway, in order:
  1. 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 429 at 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 (or gateway.guard.enabled=false) removes the layer, GUARD_RATE_LIMIT_RPM=0 keeps it installed but stops rate limiting. See Configuration → Gateway edge protection.
  2. 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).
A throttled request returns 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.