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, so switch on it for routing (5xx ⇒ retry, 4xx ⇒ fix the request) and use code for granular handling.

Common error codes

Authentication (401)

CodeMeaning
invalid_tokenMissing, malformed, or unknown bearer token.
token_expiredToken is past its expiry.
token_revokedPAT 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)

CodeMeaning
insufficient_scopeToken is valid but missing the required scope. details.required_scope names which one.
forbiddenGeneric refusal: auth and scope are fine, but the actor isn't allowed to do this.

Validation (400 / 422)

CodeMeaning
validation_errorRequest body failed validation.
invalid_requestThe request itself is malformed (bad headers, bad shape).
invalid_query_paramA query parameter is missing or malformed. details.param names which one.
attendee_email_invalidattendee.email failed format check.
attendee_phone_requiredattendee.sms_opt_in is true but attendee.phone is missing.
attendee_phone_invalidattendee.sms_opt_in is true but attendee.phone isn't a valid international number (starting with +).
invalid_if_matchIf-Match is not a valid integer.
field_immutableA PATCH tried to write a field that can only change via dedicated endpoints (e.g. start). details.fields lists them.
event_type_disallows_rescheduleThe event type has disableRescheduling.

Conflict / state (409)

CodeMeaning
slot_unavailableRequested slot is no longer free.
event_type_inactiveEvent type's status is not on.
booking_in_pastBooking starts (or started) in the past.
booking_already_cancelledReschedule attempted on a non-confirmed booking.
version_conflictIf-Match doesn't match the current booking version.
idempotency_in_progressSame Idempotency-Key still being processed. Retry-After: 1.
idempotency_key_conflictSame key, different body within the 24h window.
pat_name_takenAnother PAT in this account has the same name.
pat_limit_exceededAccount has 42 active PATs already.

Not found (404)

CodeMeaning
not_foundGeneric.
event_type_not_foundEvent type doesn't exist or is in another account.
booking_not_foundBooking doesn't exist, was hard-deleted, or its UID is malformed.
interaction_not_foundOAuth consent interaction expired or doesn't exist.

We deliberately collapse "wrong tenant" into not_found, since a 404 doesn't leak the existence of resources in other accounts.

Precondition / payload (412 / 413 / 415 / 428)

CodeMeaning
missing_if_matchPATCH without If-Match. (428 Precondition Required.)
missing_idempotency_keyWrite without Idempotency-Key. (400.)
invalid_idempotency_keyIdempotency-Key longer than 255 chars. (400.)

Rate-limiting (429)

CodeMeaning
rate_limitedBucket exhausted. Retry-After header set; see Rate limits.

OAuth (400 / 401)

CodeMeaning
invalid_clientBad client_id / client_secret, or unknown client.
invalid_grantBad auth code, bad refresh token, PKCE failure, redirect-URI mismatch, or replay detected.
invalid_scopeRequested scope not allowed for this client, or unknown scope name.
invalid_redirect_uriredirect URI is not registered or fails scheme rules.
unsupported_grant_typegrant_type is not authorization_code or refresh_token.
unsupported_response_typeresponse_type is not code.
invalid_client_metadataDCR request rejected (scope not in allowlist, etc.).
access_deniedUser declined on the consent screen. Returned via redirect, not a JSON body.

Server (5xx)

CodeMeaning
internal_errorSomething went wrong server-side. Quote request_id to support.
slot_lock_timeoutCould not acquire the per-slot lock when creating/rescheduling a booking. Retry-After: 1. (503.)
  1. 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.
  2. 429: wait for Retry-After or X-RateLimit-Reset, then retry.
  3. 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.
  4. 409 version_conflict: re-GET and reapply your patch on the new version.
  5. 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).
  6. 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), which is the fastest way for support to trace what happened.