Appearance
Response & Error Conventions
This page describes the conventions that apply to the standardized response format (API version 2026-06-12).
Which version am I on?
Consumers select the response format using the request header:
X-API-Version: 2026-06-12-> standardized format- missing/unknown version -> legacy format
If you are unsure which format you receive, see Legacy & Migration.
Datetimes
All timestamps are returned in RFC 3339 / ISO-8601, UTC:
json
{ "created_at": "2024-06-15T10:00:00Z" }- Timestamps always carry a timezone designator (
Zfor UTC). - Pure date fields (for example
activation_date) remain date-only:"2024-06-15". - Do not assume the legacy space-separated format (
"2024-06-15 10:00:00").
Booleans
Fields representing a true/false state are returned as real JSON booleans (true / false), never as 1/0, "1"/"0", or "true"/"false".
Boolean fields are identified by these name prefixes:
is_, has_, can_, show_, enable_, skip_
json
{ "is_active": true, "can_send_deletion_request": false }Errors
Every error response — validation, authentication, authorization, not-found, conflict, rate-limit, and server errors — uses a single canonical envelope:
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The given data was invalid.",
"details": [
{ "field": "email", "issue": "The email field is required." }
]
}
}| Field | Description |
|---|---|
code | Stable, non-localized machine-readable string. Branch on this. |
message | Human-readable, localized message. For display only — do not parse it. |
details | Array of field-level errors. Present only for validation errors. |
- The error
Content-Typeis alwaysapplication/json. - Branch your logic on
error.code(and the HTTP status), never on themessagetext.
Error codes
| HTTP status | error.code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | Malformed request. |
| 401 | UNAUTHENTICATED | Authentication required or failed. |
| 403 | FORBIDDEN | Authenticated but not permitted. |
| 404 | NOT_FOUND | Resource does not exist. |
| 405 | METHOD_NOT_ALLOWED | HTTP method not allowed for the route. |
| 406 | NOT_ACCEPTABLE | Cannot satisfy the Accept header. |
| 409 | CONFLICT | State conflict. |
| 410 | GONE | Resource permanently removed. |
| 422 | VALIDATION_ERROR | Field-level validation failed. |
| 428 | PRECONDITION_REQUIRED | A required precondition was missing. |
| 429 | RATE_LIMITED | Too many requests — back off and retry. |
| 500 | INTERNAL_ERROR | Unexpected server error. |
| 503 | SERVICE_UNAVAILABLE | Temporarily unavailable. |
Rate limiting
Throttled requests return HTTP 429 with code: RATE_LIMITED. (In the legacy format this was incorrectly returned as 409 Conflict.)
HTTP status codes
The API uses standard HTTP semantics: 2xx for success, 4xx for client errors, and 5xx for server errors. The status code and the error.code together fully describe an error condition.
Known differences not yet standardized
For transparency, a few values are not yet normalized:
- Field-name casing: some upstream-sourced fields remain PascalCase (e.g.
EventTime) rather than snake_case. - Sweetpay subscription
created_atis date-only (YYYY-MM-DD). - Statistics endpoints return period labels (e.g.
"Jun-2024") that are intentionally not ISO-8601 timestamps.