Errors
The unified error model — envelope shape, codes, and HTTP statuses.
Every error the API returns shares one envelope, so you can handle failures uniformly across endpoints.
The error envelope
A failed request — here, a lookup of a missing record — returns a body like this:
{
"success": false,
"statusCode": 404,
"message": "Record \"abc123\" not found.",
"code": "SCHEMA_RECORD_NOT_FOUND"
}| Field | Description |
|---|---|
success | Always false for errors. |
statusCode | The HTTP status, mirrored in the body. |
message | A human-readable explanation. 5xx messages are masked to "Internal server error" outside development. |
code | A stable machine-readable error code (see below). Absent for errors that don't map to a registered code, such as request-body validation failures. |
When request-body parsing fails, message is always "Validation failed" and no code is set. The envelope then includes an errors array, where each entry is a { path, message } pair pointing at the invalid field — path is an array, since it can point into nested fields:
{
"success": false,
"statusCode": 400,
"message": "Validation failed",
"errors": [{ "path": ["limit"], "message": "Expected number, received string" }]
}Validation that happens at the schema level — a value that doesn't match its attribute's type or constraints — returns SCHEMA_VALIDATION_FAILED instead.
Common error codes
All codes come from a single platform-wide registry. The ones you'll encounter most often:
| Code | HTTP | Meaning |
|---|---|---|
SCHEMA_VALIDATION_FAILED | 400 | Input failed schema validation. |
SCHEMA_INVALID_OBJECT_NAME | 400 | The object name is malformed. |
SCHEMA_INVALID_ATTRIBUTE_NAME | 400 | An attribute name is malformed. |
SCHEMA_FORBIDDEN | 403 | The actor lacks permission. |
SCHEMA_PROTECTED_OBJECT | 403 | The object is system-protected. |
SCHEMA_SYSTEM_ENTITY_IMMUTABLE | 403 | A system entity cannot be modified. |
SCHEMA_RECORD_NOT_FOUND | 404 | No record with that id. |
SCHEMA_OBJECT_NOT_FOUND | 404 | No object with that name. |
SCHEMA_ATTRIBUTE_NOT_FOUND | 404 | No such attribute. |
SCHEMA_ROLE_NOT_FOUND | 404 | No such role. |
SCHEMA_PLAN_NOT_FOUND | 404 | No schema plan with that id. |
SCHEMA_TIMEOUT | 408 | The operation timed out. |
SCHEMA_DUPLICATE_OBJECT | 409 | An object with that name already exists. |
SCHEMA_DUPLICATE_ATTRIBUTE | 409 | An attribute with that name already exists. |
SCHEMA_CONFLICT | 409 | The request conflicts with current state. |
DOCUMENT_MIME_NOT_ACCEPTED | 400 | The file matches no entry of the document attribute's accepts list (MIME glob, exact MIME type, or extension). |
DOCUMENT_MULTIPLE_NOT_ALLOWED | 400 | The document attribute doesn't accept multiple files. |
DOCUMENT_SIZE_EXCEEDED | 400 | The file exceeds the document attribute's maxFileSize. |
DOCUMENT_NOT_FOUND_FOR_OPERATION | 404 | The target document doesn't exist. |
The platform derives the HTTP status from the error code; any code without an explicit mapping falls back to 500.
Rate limits
Rate limiting is disabled by default. When enabled, the default is 100 requests per minute per client IP. Two exceptions: file uploads are limited to 20 per minute, and the schema endpoints are not rate-limited.
Exceeding a limit returns a 429 in the same envelope, without a code field:
{ "success": false, "statusCode": 429, "message": "ThrottlerException: Too Many Requests" }Treat a 429 like a transient failure: back off and retry.
Handling errors
Branch on code for precise handling. Fall back to statusCode for coarse categories: 4xx means the request is wrong — fix it before retrying; 5xx is server-side and safe to retry with backoff. The two exceptions are 408 and 429, which are safe to retry as-is after backing off.