Scopes and errors
Grant least privilege and handle every stable public API error code.
Grant least privilege and handle every stable public API error code.
Every public API operation requires one explicit scope. A valid credential without that scope receives 403 insufficient_scope; it is never silently upgraded to broader access.
Scope reference#
| Scope | Allows |
|---|---|
account:read | Read the authenticated account, plan, entitlements, and wallet summary |
sites:read | List and retrieve sites |
sites:write | Create, update, publish, and unpublish sites |
agents:read | Read agent settings |
agents:write | Update persona, instructions, languages, voice, embed state, and allowed origins |
knowledge:read | List knowledge sources and FAQs |
knowledge:write | Ingest or delete text sources and create, update, or delete FAQs |
conversations:read | List conversations and retrieve their messages |
leads:read | List captured leads |
analytics:read | Read site analytics events |
connectors:read | List connector state without returning secrets |
connectors:write | Create, update, enable, disable, and remove connectors |
domains:read | Read domain state and required DNS records |
domains:write | Connect, verify, update, and remove domains |
broadcasts:read | List broadcasts and delivery state |
broadcasts:write | Create drafts and queue a broadcast send |
notifications:read | List owner notifications |
notifications:write | Mark notifications read or unread |
monetization:read | List monetization products |
monetization:write | Create, update, activate, deactivate, and delete products |
usage:read | Read metered usage events |
webhooks:read | List webhook endpoints and delivery state |
webhooks:write | Create and delete webhook endpoints |
Use read scopes for reporting jobs. Add a write scope only when the integration performs that mutation. For example, a lead export normally needs sites:read and leads:read, not sites:write or agents:write.
Scope failures#
{
"error": {
"code": "insufficient_scope",
"message": "The API key does not grant the required scope.",
"details": { "required": ["sites:write"] },
"request_id": "6c0b2f2e-..."
}
}Create a replacement key with the missing scope and update the server secret. Existing API-key scopes cannot be expanded in place; this makes privilege changes explicit and auditable.
Error envelope#
All API errors use the same top-level shape:
type ApiError = {
error: {
code: string;
message: string;
details?: unknown;
request_id: string;
};
};Branch on error.code, not the English message. Messages can improve without a version change. Log error.request_id and the x-request-id response header with the operation name, status, and retry attempt—but never log the authorization header or request secrets.
Status and code reference#
| HTTP | Stable code | Meaning | Normal response |
|---|---|---|---|
400 | invalid_request | Validation failed or a field is unsupported | Correct the fields in details; do not retry unchanged input |
400 | invalid_json | Body is not a JSON object or contains invalid JSON | Serialize one valid JSON object |
401 | authentication_required | Bearer header is missing | Attach a server-side credential |
401 | invalid_api_key | Key is invalid, expired, revoked, or its owner no longer exists | Replace or rotate the key |
403 | subscription_required | The owner does not have an active paid plan | Restore the subscription before retrying |
403 | insufficient_scope | The credential lacks the operation's scope | Create a least-privilege replacement credential |
403 | account_paused | API access is paused for the account | Resume API access or contact the account owner |
404 | not_found | Endpoint or owned resource was not found | Verify the path and tenant-owned ID |
409 | conflict | Current resource state prevents the action | Read current state before retrying |
415 | invalid_request | Mutation body is not application/json | Send Content-Type: application/json |
429 | rate_limit_exceeded | The key's one-minute plan bucket is exhausted | Wait for Retry-After and apply jitter |
500 | internal_error | The request could not be completed | Retry a safe or idempotent operation and report the request ID |
Resources outside the credential owner's account can return 404 instead of revealing whether another tenant's ID exists.
Validation details#
Field validation returns paths that can be shown next to your own form controls:
{
"error": {
"code": "invalid_request",
"message": "Request validation failed.",
"details": [
{ "path": "content.headline", "message": "String must contain at least 1 character(s)" }
],
"request_id": "6c0b2f2e-..."
}
}Mutation bodies must be JSON objects. Arrays, empty bodies, form data, and JSON sent with a text content type are rejected.
Retry decisions#
- Retry
429after the number of seconds inRetry-After. - Retry
500only for safe reads or writes protected by the sameIdempotency-Key. - Retry the in-progress idempotency
409after itsRetry-After: 2header. - Do not retry
400,401,403,404, or a state conflict until the cause changes.
See Rate limits for backoff behavior and API reference for the required scope on each operation.