2xx status code and a JSON body with a machine-readable error code, a human-readable message, and the numeric status:
Authentication failures raised before your request reaches the application (at the gateway authorizer) return a bare body without an
error field — { "message": "Unauthorized" } for 401 (no API key) or { "message": "Forbidden" } for 403 (a present-but-invalid key, or a bad/missing bearer token alongside a key). Always branch on the HTTP status code, not only on the body shape.Status codes
| Status | Code | When |
|---|---|---|
400 | INVALID_JSON | Request body is not valid JSON |
400 | VALIDATION_ERROR | A field failed validation (e.g. empty tour name, bad date range, invalid time zone) |
400 | BAD_REQUEST / GRAPHQL_ERROR | The underlying service rejected the input (missing required field, invalid value) |
400 | "400" | Tour creation rejected because the organization has reached its free-tour limit (message: “Organization has reached the free tour limit”). Note the non-semantic error code of "400". |
401 | — | Missing API key/secret, or missing/invalid/expired bearer token. Returned as a bare { "message": "Unauthorized" }. |
402 | SUBSCRIPTION_REQUIRED | Write operation on an organization without an active subscription |
403 | Forbidden | An API key is present but the request fails the gateway authorizer — the key is unknown/invalid/revoked, or the bearer token is missing/invalid alongside a key. Returned as a bare { "message": "Forbidden" }. |
403 | INSUFFICIENT_SCOPE | Your partner credential lacks the endpoint’s required scope |
403 | Unauthorized | The authenticated user is not allowed to access this resource |
404 | NOT_FOUND | No endpoint matches the method + path |
404 | TOUR_NOT_FOUND | The referenced tour has been deleted |
405 | METHOD_NOT_ALLOWED | Path exists but not for this HTTP method (see the Allow response header) |
409 | GRAPHQL_ERROR | The request conflicts with existing data |
413 | PAYLOAD_TOO_LARGE | Request body exceeds 512 KB |
500 | INTERNAL_ERROR | Unexpected server error |
502 | EMPTY_RESPONSE | The underlying service returned no data |
Response body examples
Concrete examples of each error response body:Business rules that can block a request
Beyond authentication and scopes, several rules are enforced before a write reaches Daysync’s services:Subscription requirement (402)
Write operations against an organization that has no active subscription are rejected with 402 SUBSCRIPTION_REQUIRED. Organizations flagged as unlimited-free, trial, or beta are exempt. This applies to writes where the organization is identified by the request — in practice, tour creation (POST /v1/tours).
Deleted tours (404)
Any request that references a tour which has been soft-deleted returns 404 TOUR_NOT_FOUND — for both reads of its children and writes against it.
Input validation (400)
Tour create/edit requests are validated up front:
namemust not be empty and must be ≤ 500 characters,end_datemust not be beforestart_date,timeZonemust be a valid IANA time zone (e.g.Australia/Sydney).
400 with a descriptive message.
Error messages are sanitized
Internal errors (database constraints, SQL details, internal paths) are never returned verbatim. Such cases are collapsed to a generic message and typically a409. Build error handling around the status and error code rather than parsing message text.
Handling tips
- Treat
401as “fix your credentials/token” and403as “you are authenticated but not permitted” — they require different responses (re-auth vs. request more scopes). - On
402, surface a subscription/billing prompt to the user; retrying will not help. - On
5xx, retry with backoff; these are transient or upstream.

