DoloresDocs
Conventions

Errors

Uniform error envelope, type and code taxonomy, request_id correlation.

Every non-2xx response from the Dolores API uses a single shape so error handling is uniform across every endpoint.

The error envelope

{
  "error": {
    "type": "invalid_request_error",
    "code": "phone_invalid_format",
    "message": "Phone must be in E.164 format (e.g. +15551234567).",
    "param": "phone",
    "request_id": "req_01HXY7P3K9..."
  }
}
FieldAlways present?Description
typeYesThe error category. One of the values in the table below.
codeYesA stable machine-readable identifier for the specific error. Branch your code on code, not message.
messageYesHuman-readable explanation. Safe to surface to end users.
paramWhen applicableThe request field that triggered the error (e.g. phone, appointment_at). Omitted when the error doesn't map to a single field.
request_idYesMirrors the Dolores-Request-Id response header. Include it when contacting support.

Error types and HTTP status

typeHTTP statusWhen it fires
invalid_request_error400Body / query is malformed, fails validation, or violates a unique constraint. The code field narrows it (phone_invalid_format, appointment_at_invalid, customer_phone_taken, …).
authentication_error401Missing, malformed, or revoked API key.
permission_error403API key is valid but lacks access to the requested resource. (Reserved — v1 keys have full project scope.)
not_found_error404The referenced resource doesn't exist in this project, or has been soft-deleted.
rate_limit_error429Per-key request budget exceeded. See Rate limits.
api_error5xxInternal Dolores error. Safe to retry with exponential backoff.

Common error codes

These are the codes you're most likely to see in day-to-day integration. The list isn't exhaustive — new codes may appear as endpoints evolve, so prefer type-level branching for fallback handling.

codeTypeNotes
missing_api_keyauthentication_errorNo Authorization header.
invalid_api_keyauthentication_errorKey is malformed, unknown, or revoked.
phone_invalid_formatinvalid_request_errorPhone must match ^\+\d{7,15}$ (E.164).
customer_phone_takeninvalid_request_errorAnother customer in this project already has this phone.
appointment_at_invalidinvalid_request_errorappointment_at is not a parseable ISO 8601 timestamp.
customer_not_foundnot_found_errorcustomer_id in body or path didn't match any non-deleted row.
appointment_not_foundnot_found_errorSame idea for appointments.
webhook_endpoint_not_foundnot_found_errorSame for webhook endpoints.
url_required / url_invalid / url_invalid_schemeinvalid_request_errorWebhook endpoint URL is missing, malformed, or not http(s).
event_filter_unknowninvalid_request_errorA value in event_filters doesn't match any known event prefix.
idempotency_key_conflictinvalid_request_errorSame Idempotency-Key reused with a different request body in the last 24h.
rate_limited_per_second / rate_limited_per_minuterate_limit_errorSlow down; respect Retry-After.

Correlating with support

Every successful response and every error includes a request_id. Snapshot it into your logs alongside any inbound API call. When you open a support ticket, share the request_id — it's the fastest way for us to find the request in our logs.

Handling errors

A robust client:

  1. Branches on HTTP status first (catch transport-level failures).
  2. Reads error.type for category-level handling (auth failure → re-fetch key; rate limit → backoff; not found → return 404 to your user).
  3. Optionally reads error.code for granular UX (e.g. "phone taken" → show a field-specific error in your form).
  4. Falls back to error.message for display when no specific UX exists.
  5. Retries on 5xx with exponential backoff (and on 429, respecting Retry-After).
  6. Never retries on 4xx (except 408/429) without changing the request.