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..."
}
}| Field | Always present? | Description |
|---|---|---|
type | Yes | The error category. One of the values in the table below. |
code | Yes | A stable machine-readable identifier for the specific error. Branch your code on code, not message. |
message | Yes | Human-readable explanation. Safe to surface to end users. |
param | When applicable | The request field that triggered the error (e.g. phone, appointment_at). Omitted when the error doesn't map to a single field. |
request_id | Yes | Mirrors the Dolores-Request-Id response header. Include it when contacting support. |
Error types and HTTP status
type | HTTP status | When it fires |
|---|---|---|
invalid_request_error | 400 | Body / 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_error | 401 | Missing, malformed, or revoked API key. |
permission_error | 403 | API key is valid but lacks access to the requested resource. (Reserved — v1 keys have full project scope.) |
not_found_error | 404 | The referenced resource doesn't exist in this project, or has been soft-deleted. |
rate_limit_error | 429 | Per-key request budget exceeded. See Rate limits. |
api_error | 5xx | Internal 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.
code | Type | Notes |
|---|---|---|
missing_api_key | authentication_error | No Authorization header. |
invalid_api_key | authentication_error | Key is malformed, unknown, or revoked. |
phone_invalid_format | invalid_request_error | Phone must match ^\+\d{7,15}$ (E.164). |
customer_phone_taken | invalid_request_error | Another customer in this project already has this phone. |
appointment_at_invalid | invalid_request_error | appointment_at is not a parseable ISO 8601 timestamp. |
customer_not_found | not_found_error | customer_id in body or path didn't match any non-deleted row. |
appointment_not_found | not_found_error | Same idea for appointments. |
webhook_endpoint_not_found | not_found_error | Same for webhook endpoints. |
url_required / url_invalid / url_invalid_scheme | invalid_request_error | Webhook endpoint URL is missing, malformed, or not http(s). |
event_filter_unknown | invalid_request_error | A value in event_filters doesn't match any known event prefix. |
idempotency_key_conflict | invalid_request_error | Same Idempotency-Key reused with a different request body in the last 24h. |
rate_limited_per_second / rate_limited_per_minute | rate_limit_error | Slow 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:
- Branches on HTTP status first (catch transport-level failures).
- Reads
error.typefor category-level handling (auth failure → re-fetch key; rate limit → backoff; not found → return 404 to your user). - Optionally reads
error.codefor granular UX (e.g. "phone taken" → show a field-specific error in your form). - Falls back to
error.messagefor display when no specific UX exists. - Retries on
5xxwith exponential backoff (and on429, respectingRetry-After). - Never retries on
4xx(except408/429) without changing the request.