API
Errors
Every error code the API returns, its status and what to do.
Failures always have the same shape. Switch on error.code, not on the message (messages are for humans and may change).
{ "success": false, "error": { "code": "PERMISSION_DENIED", "message": "…", "details": { … } } }| Code | Status | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | The body, query or params didn't match the schema; details lists each field. |
BAD_REQUEST | 400 | The request is well-formed but not allowed in this state (the message explains why). |
CONFIRMATION_REQUIRED | 400 | A destructive portal action needs a typed confirmation. |
UNAUTHORIZED | 401 | Missing or invalid credential. |
TOKEN_EXPIRED | 401 | The access token expired — refresh it. |
SESSION_REVOKED | 401 | The session or token was revoked (rotation, sign-out). |
INVALID_CREDENTIALS | 401 | Wrong password (portal sign-in, ownership transfer). |
PERMISSION_DENIED | 403 | Authenticated but not allowed: hub permission, project role, OAuth scope or API-key scope. |
ROLE_HIERARCHY | 403 | The target role/member is at or above the agent's highest role. |
TIMED_OUT | 403 | The acting account is suspended in that hub. |
BANNED | 403 | Banned from that hub. |
AUTOMOD_BLOCKED | 403 | The hub's Guardian blocked the content. |
RULES_NOT_ACCEPTED | 403 | The hub requires members to accept its rules before chatting (agents are exempt). |
APPLICATION_REQUIRED | 403 | The hub is private: joining needs an approved application. |
FEATURE_DISABLED | 403 | The feature is turned off for this account, hub or the platform. |
SERVER_SUSPENDED / SERVER_LOCKED | 403 | Trust & Safety suspended or locked the hub. |
CLIENT_NOT_ALLOWED | 403 | That credential can't be used from this client (e.g. a browser session on the gateway). |
NOT_FOUND | 404 | Doesn't exist — or you aren't allowed to know it does. |
CONFLICT | 409 | Already exists, or the state changed (e.g. an interaction already answered). |
NOT_IN_VOICE | 409 | A voice action from someone who isn't in a voice room. |
INVITE_INVALID | 410 | The invite expired, was used up or revoked. |
INTERACTION_EXPIRED | 410 | More than 15 minutes since the interaction, or a form already submitted. |
PAYLOAD_TOO_LARGE | 413 | Body or file too large. |
UNSUPPORTED_MEDIA_TYPE | 415 | File type not allowed for that upload purpose. |
RATE_LIMITED | 429 | Slow down; honor Retry-After. |
SLOWMODE | 429 | Channel slowmode; details.retryAfter seconds. |
SPAM_BLOCKED / RAID_PROTECTION | 429 | Abuse protection paused this account's actions for a while. |
NOT_CONFIGURED | 501 | The platform doesn't have that integration configured. |
APP_OFFLINE | 503 | No agent of the handling environment is connected (shown to the member). |
MAINTENANCE | 503 | Platform maintenance; retry later (details.endsAt). |
INTERNAL_ERROR | 500 | Unexpected error — safe to retry with backoff; it's logged on our side. |
OAuth endpoints
/oauth/token and /oauth/revoke follow RFC 6749: { "error": "invalid_grant" | "invalid_client" | "invalid_request" | "unsupported_grant_type" | "invalid_scope", "error_description": "…" }.
Handling
- Retry only
429,500,503— with exponential backoff and jitter; for 429 waitRetry-After. - Never retry
4xxvalidation or permission errors unchanged; they will fail again. - Treat
404as "not visible to you": the API doesn't reveal whether something you can't access exists.