Error envelope
All non-2xx REST responses return a consistent JSON envelope:
{
"error": {
"code": "NOT_FOUND",
"message": "Site not found.",
"details": {},
"requestId": "550e8400-e29b-41d4-a716-446655440000"
}
}
| Field | Type | Description |
|---|
code | string | Machine-readable error code (see table below) |
message | string | Human-readable description |
details | object | Additional context (field-level validation errors, retry timing, etc.) |
requestId | string | Unique request identifier for tracing and support |
Error codes
| HTTP Status | Code | When |
|---|
| 400 | INVALID_ARGUMENT | Bad request body, missing required field, invalid filter value |
| 400 | VALIDATION_ERROR | Serializer validation failure. details contains per-field errors |
| 401 | UNAUTHENTICATED | No Bearer token, expired token, invalid token |
| 403 | PERMISSION_DENIED | Valid auth but insufficient role or scope |
| 404 | NOT_FOUND | Resource doesn’t exist or is not accessible |
| 405 | METHOD_NOT_ALLOWED | HTTP method not supported on this endpoint |
| 429 | RATE_LIMITED | Rate limit exceeded. details.retryAfter contains seconds to wait |
| 500 | INTERNAL_ERROR | Unexpected server error |
| 502 | UPSTREAM_ERROR | An upstream service is temporarily unreachable |
Validation errors
When a request fails validation, the details object contains per-field errors:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed.",
"details": {
"name": ["This field is required."],
"timezone": ["Invalid timezone: Mars/Olympus."]
},
"requestId": "a1b2c3d4-..."
}
}
Non-field validation errors appear under non_field_errors:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed.",
"details": {
"non_field_errors": ["Start date must be before end date."]
},
"requestId": "a1b2c3d4-..."
}
}
Rate limiting
When rate limited, the response includes a Retry-After header and timing in details:
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again later.",
"details": {
"retryAfter": 30
},
"requestId": "a1b2c3d4-..."
}
}
See Authentication: Rate limits for per-client limits.
Request tracing
Every API response includes an X-Request-Id header:
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
- If you send an
X-Request-Id header in your request, the same value is echoed back. This lets you correlate requests across your own systems.
- If you don’t send one, the API generates a UUID for you.
- The same ID appears in the error envelope as
requestId.
When reporting issues, include the X-Request-Id from the failing response. This lets us trace your request through server logs instantly.
GraphQL errors
GraphQL responses follow the standard GraphQL error format within the errors array. They are not wrapped in the REST error envelope:
{
"errors": [
{
"message": "Variable '$siteId' is required.",
"path": ["buildings"],
"extensions": { ... }
}
],
"data": null
}
The X-Request-Id header is still present on all GraphQL responses.