Errors & limits
Error format
Every error has the same shape, and code is stable across releases. Branch on code, not on the message.
json
{
"error": {
"code": "APPROVAL_REQUIRED",
"message": "This action needs your approval.",
"requestId": "req-8f2c…",
"details": { "approvalId": "…" }
}
}message is safe to show to people. Quote requestId when you contact support.
| Code | HTTP | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | No valid session. Sign in again. |
FORBIDDEN | 403 | Signed in, but not allowed (role, workspace, or missing client header). |
POLICY_DENIED | 403 | A workspace policy blocks this tool or connector. |
NOT_FOUND | 404 | Doesn't exist, or isn't yours. |
VALIDATION_FAILED | 400 | The request is invalid. details.issues lists each field. |
CONFLICT | 409 | Clashes with existing data (e.g. a slug already taken). |
INVALID_STATE | 409 | Not possible right now (e.g. cancelling a finished run). |
CONNECTION_REQUIRED | 409 | Connect the account first. details.toolkitSlug says which. |
APPROVAL_REQUIRED | 409 | Needs approval. details.approvalId identifies it. |
IDEMPOTENCY_CONFLICT | 409 | An idempotency key was reused with different input. |
RATE_LIMITED | 429 | Too many requests. Back off and retry. |
UPSTREAM_FAILED | 502 | The model or integration provider returned an error. |
INTEGRATION_UNAVAILABLE | 503 | The integration provider isn't available. |
UPSTREAM_TIMEOUT | 504 | The provider timed out. |
INTERNAL_ERROR | 500 | Something went wrong on our side. |
Validation errors list every problem:
json
{ "error": { "code": "VALIDATION_FAILED", "message": "Request validation failed.",
"details": { "issues": [{ "path": "name", "message": "Too small: expected string to have >=1 characters" }] } } }Limits
| Limit | Value |
|---|---|
Sign-in and register (/auth/*) | 20 requests per minute |
| Everything else | 600 requests per minute |
| Request body | 15 MB (room for a 10 MB attachment in base64) |
| Message length | 20,000 characters |
| Attachments | 5 per message, 10 MB each |
| Run streaming | One stream stays open up to 30 minutes; reconnect to continue |
| Model and tool usage | The workspace plan's monthly token budget; runs stop with RATE_LIMITED when it's used up |
Pagination
Lists that can grow take limit (1–100, default 50) and cursor, and return:
json
{ "items": [ … ], "nextCursor": "50" }Pass nextCursor back as cursor until it is null.
Idempotency
Tool execution (POST /integrations/tools/{toolSlug}/execute) accepts an idempotencyKey (8–200 characters). Repeating a request with the same key and input returns "status": "duplicate" instead of running the tool twice; the same key with different input fails with IDEMPOTENCY_CONFLICT.
