Error Handling

The DayZero API uses standard HTTP status codes and returns one consistent JSON envelope for every non-2xx response (schema ErrorResponse in the OpenAPI spec).

Error Response Format

json
{
  "error": "not_found",
  "message": "Invoice '01912345-abcd-7000-8000-000000000042' not found",
  "code": "NOT_FOUND_006",
  "request_id": "3f0e7c2a-4b1d-4a8e-9c2f-1d2e3f4a5b6c"
}
Field Description
error Machine-readable category: validation_error, unauthorized, forbidden, not_found, conflict, rate_limited, server_error, service_unavailable
message Human-readable explanation. May change between releases — don't branch on it
code Stable, specific code in the form CATEGORY_NNN (e.g. AUTH_010, NOT_FOUND_006). Branch on this
request_id Correlation ID for this request; also returned in the X-Request-ID response header. Quote it when contacting support
errors Only on 400/422 validation failures: a map of field name → message

Validation errors name the offending fields:

json
{
  "error": "validation_error",
  "message": "Validation failed: due_date: Input should be a valid date",
  "errors": {
    "due_date": "Input should be a valid date"
  },
  "code": "GEN_002",
  "request_id": "3f0e7c2a-4b1d-4a8e-9c2f-1d2e3f4a5b6c"
}

Send Accept: application/json on every request. Errors are only rendered as HTML pages for browser-style text/html requests, but stating JSON removes the ambiguity.

Status Codes

Status error Typical code When
200 — — Request completed
201 — — Resource created
400 validation_error GEN_002, VALIDATION_* Invalid parameters or a business-rule violation; errors names the fields
401 unauthorized AUTH_001, AUTH_008 (missing x-business-id), AUTH_010 (no access to that business) Missing, invalid, expired or revoked token, or wrong business
403 forbidden AUTH_002 Token valid but its scope or role doesn't permit the operation (e.g. a read_only token calling POST)
404 not_found NOT_FOUND_* Resource doesn't exist — or belongs to a different business than x-business-id
409 conflict CONFLICT_* Duplicate or state conflict (e.g. voiding an invoice that has payments)
422 validation_error GEN_002 Request body or parameters failed schema validation; errors names the fields
429 rate_limited GEN_005 Too many requests — back off exponentially and retry the same request
500 server_error GEN_003 Unexpected error — retry idempotent requests with backoff; quote request_id to support
503 service_unavailable GEN_004 Temporary maintenance or dependency outage — retry with backoff

Retry guidance

  • Retry 429, 500, 503 with exponential backoff (start at 1–2 s, cap at ~30 s).
  • Don't retry 400, 401, 403, 404, 409, 422 unchanged — the request itself is the problem.
  • POST requests that create records have no idempotency key yet. Before re-sending one after a timeout or 5xx, check whether the record already exists (list with the filter you would have used) to avoid duplicates.

Common Mistakes

Wrong business context: Getting 404 on a resource you know exists? Check that x-business-id matches the business that owns it.

Missing business header: 401 with code: "AUTH_008" means x-business-id was required and absent. Only GET /api/v1/businesses (and a handful of user-level endpoints) work without it.

Amounts in dollars instead of cents: All monetary values are integers in cents. $150.00 should be sent as 15000; 150.00 fails validation.

Expired token: Tokens with an expiration return 401 once expired. Generate a new one from Settings → Developers.

Read-only token on a write: A token created with the read_only scope gets 403 on every non-GET request.