Errors
Conventional HTTP status codes plus a small JSON envelope. No HTML, ever.
Shape
Every error response is JSON with an error string. Validation errors add a structured issues array straight from Zod. Some endpoints add a third key (details) for context — we always document it on the relevant endpoint page when they do.
{ "error": "Invalid JSON" }{
"error": "Validation failed",
"issues": [
{ "path": ["email"], "message": "Invalid email", "code": "invalid_string" }
]
}Status codes
| Code | Name | When we return it |
|---|---|---|
| 200 | OK | Successful GET or PATCH. |
| 201 | Created | Resource created. The body contains the new record. |
| 202 | Accepted | Webhook ingested; processing will happen asynchronously. |
| 204 | No Content | Successful DELETE. |
| 400 | Bad Request | Malformed JSON, missing required query, illegal enum value. |
| 401 | Unauthorized | Missing, malformed, expired, or revoked bearer token. |
| 403 | Forbidden | Token is valid but its role lacks the required capability. |
| 404 | Not Found | Resource doesn't exist or the actor can't see it. |
| 409 | Conflict | Unique-key conflict — e.g. duplicate email on a contact. |
| 422 | Unprocessable Entity | Body parsed but Zod validation failed. |
| 429 | Too Many Requests | Per-token or per-account rate limit exceeded. |
| 500 | Internal Server Error | Unhandled exception. Reported to our error sink. |
| 501 | Not Implemented | Endpoint exists but isn't wired for your tenant (e.g. EHR webhooks for a vendor without signed callbacks). |
Retries
5xx and 429 responses are safe to retry with exponential backoff. 4xx errors other than 429 are caller bugs and won't get better by trying again — fix the request first.
Idempotency
Mutating endpoints are not idempotent by default. If you need at-most-once semantics (creating the same appointment twice would be bad), check our planned support for the Idempotency-Key header — coming soon. For now, look up the record before retrying a POST after a timeout.