Skip to main content
Every error is JSON with the same three parts: a human-readable message, a stable machine-readable code, and — for input problems — errors keyed by attribute:

Error code catalogue

Branch on the status code, the code, and the attribute keys in errors. message text is written for humans and may change; do not parse it. Codes are stable within the current version — new ones may be added, existing ones are not renamed. Every code below is emitted by name by the API — none are aspirational.

401 — Unauthorized

403 — Forbidden

404 — Not Found

405 — Method Not Allowed

409 — Conflict

422 — Unprocessable Entity

Validation Referenced record not found — errors names the attribute; the reference is missing, deleted, or not on this account Clinical notes Appointments & booking Patients & referrals Payments & billing Files Idempotency

413 — Payload Too Large

429 — Too Many Requests

500 — Internal Server Error

503 — Service Unavailable

Every other HTTP status the platform can produce uses the same {message, code} envelope: a 403 from a permission gate is permission_denied, 413 is payload_too_large, 503 is service_unavailable, and code is http_error only when no more specific code applies.

Handling errors well

Retry taxonomy — branch on status, not on whether the call “felt” transient:
  • Log the code, not the message. message is written for a human and may be reworded without notice; code is the stable contract your branching logic and your alerts should key off. Store X-Request-Id alongside it — it’s what support needs to pull the exact request from Developers → API Logs.
  • Don’t parse message to detect a specific failure — the errors object (for 422) and code (for everything) already tell you exactly what happened, in a shape that won’t drift under you.
  • Idempotency keys make retries safe, not fast — see Idempotency keys for the full replay behaviour (24-hour window, same-key-same-body semantics).

Diagnosing

Every response includes an X-Request-Id. The same id appears against the request on the account’s Developers → API Logs page — status, latency and the caller — and support can trace it from there.

Rate limits

X-RateLimit-Limit and X-RateLimit-Remaining are on every response. The limit is per account: every API key on the account shares one REST budget (120 requests a minute), so a burst from one integration spends the same budget as every other. AI connectors have a separate budget of their own and never eat into it.