Errors
Standard error envelope, HTTP status codes, and the request_id you'll need for support.
All Cocobox API errors return a JSON body with the same shape, no matter which endpoint you hit:
{
"status": 401,
"error": "unauthorized",
"message": "Invalid or missing API key.",
"request_id": "req_01HF2X7Y9KQRABCDE12345"
}
| Field | Meaning |
|---|---|
status | HTTP status code, mirrored in the body for clients that drop the response status. |
error | Machine-readable error slug — stable across versions. Use this for branching logic. |
message | Human-readable explanation. Do not branch on this. |
request_id | Globally unique id of the request. Include it when you contact support. |
HTTP status codes
| Status | When it happens |
|---|---|
| 200 | Success. |
| 201 | Resource created (API key creation, etc.). |
| 204 | Success with no body (e.g. DELETE /api-keys/:id). |
| 400 | Malformed JSON, missing required field, unknown field, bad enum value. |
| 401 | Missing key, malformed key, key not found, key inactive, expired Auth0 token. |
| 402 | Subscription credit pool empty for the chosen model. |
| 403 | Scope violation (model not allowed, tools disabled, role insufficient). |
| 404 | Unknown resource (model id, key id, MCP session). |
| 409 | Conflict (e.g. trying to share a connection with someone who already has higher access). |
| 413 | Request body too large. Currently 1 MiB. |
| 422 | Validation error (e.g. sql_run received a non-read-only query without write permission). |
| 429 | Rate-limited. See Retry-After header. |
| 500 | Cocobox internal error. Safe to retry with exponential backoff. |
| 502 | Upstream provider (Bedrock) unavailable. Retry. |
| 504 | Upstream timeout. Retry. |
Stable error slugs
Branch on error, not on message or status.
| Slug | Status | Meaning |
|---|---|---|
unauthorized | 401 | Missing or invalid key / token. |
key_inactive | 401 | Key exists but is_active = false. |
scope_violation | 403 | Model not in allowed_models or other scope rule failed. |
tools_disabled | 403 | MCP request with tools_enabled = false. |
not_found | 404 | Resource doesn’t exist or you can’t see it. |
session_expired | 404 | MCP session id unknown or expired. |
validation_failed | 400 / 422 | Request body or arguments didn’t validate. |
rate_limited | 429 | Per-minute or burst limit reached. |
budget_exhausted | 429 | Per-key request budget reached. |
insufficient_credits | 402 | Workspace out of credits for paid models. |
connection_unreachable | 502 | Cocobox couldn’t dial your database. Check host / port / SSH. |
query_too_dangerous | 422 | A guard refused the query (DML on a read-only role, schema migration on prod, etc.). |
provider_error | 502 | Bedrock returned an error. Body includes the upstream code under details. |
internal_error | 500 | Unexpected. Retry; if it persists, file a ticket with the request_id. |
Retries
5xx,429,502,504are safe to retry.400,401,403,404,409,422are not — fix the request.- Honor
Retry-Afteron429. Otherwise back off exponentially with jitter.
Sample error responses
Missing key
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"status": 401,
"error": "unauthorized",
"message": "Authorization header is required.",
"request_id": "req_01HF2X7Y9KQRABCDE12345"
}
Out of credits
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"status": 402,
"error": "insufficient_credits",
"message": "Workspace credit pool is empty for paid models.",
"request_id": "req_01HF2X7Y9KQRZZZZZ12345",
"details": {
"model": "sonnet",
"required_credits": 1,
"available_credits": 0
}
}
Rate-limited
HTTP/1.1 429 Too Many Requests
Retry-After: 8
Content-Type: application/json
{
"status": 429,
"error": "rate_limited",
"message": "Burst limit of 50 req/min exceeded.",
"request_id": "req_…"
}
Tool guard
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"status": 422,
"error": "query_too_dangerous",
"message": "Statement is not read-only and the API key lacks write permission.",
"request_id": "req_…",
"details": { "statement_kind": "UPDATE" }
}