Skip to main content

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"
  }
}
FieldTypeDescription
codestringMachine-readable error code (see table below)
messagestringHuman-readable description
detailsobjectAdditional context (field-level validation errors, retry timing, etc.)
requestIdstringUnique request identifier for tracing and support

Error codes

HTTP StatusCodeWhen
400INVALID_ARGUMENTBad request body, missing required field, invalid filter value
400VALIDATION_ERRORSerializer validation failure. details contains per-field errors
401UNAUTHENTICATEDNo Bearer token, expired token, invalid token
403PERMISSION_DENIEDValid auth but insufficient role or scope
404NOT_FOUNDResource doesn’t exist or is not accessible
405METHOD_NOT_ALLOWEDHTTP method not supported on this endpoint
429RATE_LIMITEDRate limit exceeded. details.retryAfter contains seconds to wait
500INTERNAL_ERRORUnexpected server error
502UPSTREAM_ERRORAn 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.