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 /meetings/{id}→409once the bot lifecycle owns the record (a planned meeting is only editable while it’s still planned);409when the new link collides with another active meeting.DELETE /meetings/{id}deletes a plan before spawn or deletes transcript/recording artifacts after the lifecycle is terminal; it returns409while the meeting is in flight.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.DELETE /bots/{platform}/{native_meeting_id}→200with a body ({"status":"stopping","meeting_id":…}), not204.- A bot that fails to join surfaces in
GET /bots/statusrather than as an HTTP error onPOST /bots— see Troubleshooting.
/user/calendar*) — see Calendar API → Error summary.
Agent API (/agent/*) — not served on the hosted service or on Kubernetes; every route
answers 502 {"detail":"upstream unreachable: ConnectError"} there
(vexa-platform#324). On a deployment that does
run agent-api:
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.
200 and close with zero bytes —
no frame at all, because agent-api is not deployed. Do not read a successfully-opened stream as a
healthy one (vexa-platform#324).
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. ON by default: a
bucket of
GATEWAY_RATE_LIMIT_BURST(120) tokens refilled atGATEWAY_RATE_LIMIT_RPS(40)/s, off only whenGATEWAY_RATE_LIMIT_DISABLEDis set. 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.