Help
/
API
Errors
Every error response — including auth failures, rate-limit
responses, and validation errors — has this shape:
{
"error": {
"code": "insufficient_scope",
"message": "This action requires the 'bookings:create' scope",
"details": { "required_scope": "bookings:create" },
"request_id": "req_…"
}
}
-
code — stable machine
identifier. Switch on this, not on message.
-
message — human-readable
explanation. Wording may change.
-
details — endpoint-specific
extra fields (e.g. the offending parameter name, or the
required scope). May be empty {}.
-
request_id — unique per
request. Quote this when reporting an
issue.
The HTTP status is set per RFC 7231 — switch on it for routing
(5xx ⇒ retry, 4xx ⇒ fix the request)
and use code for granular handling.
Common error codes
Authentication (401)
| Code |
Meaning |
invalid_token |
Missing, malformed, or unknown bearer token. |
token_expired |
Token is past its expiry. |
token_revoked |
PAT or OAuth token has been revoked. |
These responses also include
WWW-Authenticate: Bearer realm="42min", error="…"
— useful for clients that follow the RFC 6750 challenge
protocol.
Authorization (403)
| Code |
Meaning |
insufficient_scope |
Token is valid but missing the required scope.
details.required_scope names which one.
|
forbidden |
Generic refusal — auth and scope are fine, but the actor
isn't allowed to do this.
|
Validation (400 / 422)
| Code |
Meaning |
validation_error |
Request body failed validation. |
invalid_request |
The request itself is malformed (bad headers, bad
shape).
|
invalid_query_param |
A query parameter is missing or malformed.
details.param names which one.
|
attendee_email_invalid |
attendee.email failed format check. |
invalid_if_match |
If-Match is not a valid integer. |
field_immutable |
A PATCH tried to write a field that can only change via
dedicated endpoints (e.g. start).
details.fields lists them.
|
event_type_disallows_reschedule |
The event type has disableRescheduling.
|
Conflict / state (409)
| Code |
Meaning |
slot_unavailable |
Requested slot is no longer free. |
event_type_inactive |
Event type's status is not on. |
booking_in_past |
Booking starts (or started) in the past. |
booking_already_cancelled |
Reschedule attempted on a non-confirmed booking. |
version_conflict |
If-Match doesn't match the current booking
version.
|
idempotency_in_progress |
Same Idempotency-Key still being processed.
Retry-After: 1.
|
idempotency_key_conflict |
Same key, different body within the 24h window. |
pat_name_taken |
Another PAT in this account has the same name. |
pat_limit_exceeded |
Account has 42 active PATs already. |
Not found (404)
| Code |
Meaning |
not_found |
Generic. |
event_type_not_found |
Event type doesn't exist or is in another account.
|
booking_not_found |
Booking doesn't exist, was hard-deleted, or its UID is
malformed.
|
interaction_not_found |
OAuth consent interaction expired or doesn't exist.
|
We deliberately collapse "wrong tenant" into
not_found — a 404 doesn't leak the existence of
resources in other accounts.
Precondition / payload (412 / 413 /
415 / 428)
| Code |
Meaning |
missing_if_match |
PATCH without If-Match. (428 Precondition Required.)
|
missing_idempotency_key |
Write without Idempotency-Key.
(400.)
|
invalid_idempotency_key |
Idempotency-Key longer than 255 chars.
(400.)
|
Rate-limiting (429)
| Code |
Meaning |
rate_limited |
Bucket exhausted. Retry-After header set;
see Rate limits.
|
OAuth (400 / 401)
| Code |
Meaning |
invalid_client |
Bad client_id / client_secret,
or unknown client.
|
invalid_grant |
Bad auth code, bad refresh token, PKCE failure,
redirect-URI mismatch, or replay detected.
|
invalid_scope |
Requested scope not allowed for this client, or unknown
scope name.
|
invalid_redirect_uri |
redirect URI is not registered or fails scheme rules.
|
unsupported_grant_type |
grant_type is not
authorization_code or
refresh_token.
|
unsupported_response_type |
response_type is not code.
|
invalid_client_metadata |
DCR request rejected (scope not in allowlist, etc.).
|
access_denied |
User declined on the consent screen. Returned via
redirect, not a JSON body.
|
Server (5xx)
| Code |
Meaning |
internal_error |
Something went wrong server-side. Quote
request_id to support.
|
slot_lock_timeout |
Could not acquire the per-slot lock when
creating/rescheduling a booking.
Retry-After: 1. (503.)
|
Recommended client handling
-
5xx or network failure — retry
with the same Idempotency-Key after a brief
delay (exponential backoff, capped). Writes are safe to
retry as long as the key stays the same.
-
429 — wait for
Retry-After or X-RateLimit-Reset,
then retry.
-
401 token_expired — refresh
(OAuth) or rotate (PAT). For OAuth, if the refresh attempt
itself returns invalid_grant with "session has
been revoked", start the authorize flow from scratch.
-
409 version_conflict —
re-GET and reapply your patch on the new
version.
-
409 slot_unavailable — the
slot was taken between your check and your create. Pick
another slot (the booking page or
/v1/slots will reflect the new state).
-
4xx other — surface to the
user; usually a bug in the request.
Always log request_id from
error.request_id (or
meta.request_id on success) — it's the fastest
way for support to trace what happened.
Last updated
May 14, 2026.