Errors
Always send Accept: application/json so errors come back as JSON.
Status codes and envelopes
| Status | When | Body shape |
|---|---|---|
400 |
OAuth grant errors | { "error": "invalid_grant", "error_description": "..." } |
400 |
Missing organization context | { "success": false, "error": "organization_required", "message": "..." } |
401 |
Missing/invalid/revoked token | { "success": false, "error": "Unauthenticated", "message": "Invalid token" } |
403 |
Insufficient permission or scope | { "message": "This action is unauthorized." } |
404 |
Resource not found — or not accessible from your organization | { "message": "No query results for model [...]" } |
422 |
Validation failure | See below |
429 |
Rate limit exceeded | { "message": "Too Many Attempts." } + Retry-After header |
5xx |
Server error | { "message": "Server Error" } |
Validation errors (422)
{
"message": "The customer id field is required. (and 1 more error)",
"errors": {
"customer_id": ["The customer id field is required."],
"items": ["The items field is required."]
}
}
errors maps each invalid field to an array of human-readable messages.
Parsing advice
Envelopes are not uniform across status codes (a known trade-off, kept stable for backwards compatibility). Parse defensively:
- If the body has
errorsas an object of field → messages, treat it as a validation error. - Else if it has a string
errorfield, treaterroras a machine-readable code andmessageas display text. - Else fall back to
message.
A 404 on a resource you believe exists usually means the resource belongs to a different organization than the one your token/header selects — the API deliberately does not distinguish "doesn't exist" from "not yours".