Errors
Every error uses one envelope. The status code says what class of problem it
is. The type field says which problem it is, and type is the field to
branch on. Do not match on message — the text is written for a person and it
changes.
{ "error": { "type": "validation_failed", "message": "The request was not valid.", "param": "starts_at" }}param is present only on a 422 that names one field. Every other key in the
body is described under 402 below.
The error types
Section titled “The error types”type is stable. A new type can appear, so treat an unknown type as the
class its status code names.
| Status | type |
What happened |
|---|---|---|
| 401 | unauthorized |
The key is missing, or it is not valid. |
| 402 | plan_limit_exceeded |
A plan cap stopped the request. |
| 403 | secret_key_required |
The route needs a secret key. You sent a publishable key. |
| 403 | origin_not_allowed |
The Origin header is not on the allowed list of this publishable key. |
| 403 | forbidden |
The credential is valid. The action is not permitted. |
| 404 | not_found |
No such record, or the record is not on this workspace. |
| 409 | slot_unavailable |
Somebody took that time first. |
| 409 | conflict |
That resource already exists. |
| 409 | booking_not_pending |
The booking is not awaiting confirmation. |
| 409 | no_show_not_applicable |
Only a past confirmed booking can be marked as a no-show. |
| 422 | validation_failed |
The request is malformed. param names the field. |
| 422 | verification_failed |
The 6-digit code was refused. |
| 422 | too_late_to_cancel |
The cancellation window is closed. |
| 422 | outside_working_hours |
The time is outside the working hours of that calendar. |
| 422 | idempotency_mismatch |
The Idempotency-Key was used before, with a different body. |
| 429 | rate_limited |
Too many requests. See the rate limits guide. |
| 500 | internal_error |
A fault on our side. |
| 503 | transient_failure |
Two writes contended. Retry the request. |
Retry a 503 and a 429. Do not retry a 4xx: the same request gets the same
answer.
402 — a plan limit refused the request
Section titled “402 — a plan limit refused the request”A 402 means a plan cap stopped the request. The fields in the body depend on
the key you used.
| Key | The body contains |
|---|---|
Publishable (pk_) |
type, message |
Secret (sk_) |
type, message, dimension |
Account (ak_) |
type, message, dimension, limit, used |
This is deliberate. A publishable key runs in a browser, where a stranger can read the response. Such a caller must not learn the plan state of the business.
A secret key gets the dimension and no numbers. Caps are counted at the account, and a secret key reaches one workspace. The numbers tell a key issued for one workspace about its siblings.
dimension is one of three wire values:
| Value | The cap |
|---|---|
bookings |
Bookings in the current billing period. |
staff |
Active calendars. The dashboard calls these “calendars”. |
workspaces |
Active workspaces on the account. |
Two fields that are absent, not zero
Section titled “Two fields that are absent, not zero”used can be absent, and an absent used is not 0.
The booking cap reports no count, so used is absent on a bookings refusal.
The cap also refuses when usage is above it, which happens after a plan
downgrade. The cap is therefore not a substitute for the count. An account at
900 bookings can read “100 of 100” if you make that substitution.
dimension can also be absent. A 402 with no dimension names no cap, so
there is nothing to count against and no number to show. Show the message and
stop.
{ "error": { "type": "plan_limit_exceeded", "message": "This plan limit has been reached.", "dimension": "staff", "limit": 5, "used": 5 }}Two 403 types that read alike
Section titled “Two 403 types that read alike”secret_key_required and origin_not_allowed both come from a publishable
key, and they need different fixes.
secret_key_required means the route is a server route. Send a secret key from
your server.
origin_not_allowed means the browser sent an Origin header that this key
does not list. Add the origin to the key, or use the hosted booking page.
CAUTION: The origin list is not a security boundary. GET /v1/public/workspaces/{slug}
gives the publishable key to anybody who holds the slug, by design. The list
stops a third party from embedding your key in their own site. It does not stop
a script.
Notes on two more
Section titled “Notes on two more”A 404 also covers “this record belongs to another workspace”. We do not
answer 403 there, because a 403 confirms that the record exists.
verification_failed collapses four causes: a wrong code, an expired hold, too
many attempts, and an unknown booking. They are not told apart on purpose. A
response that told them apart is an oracle for a 6-digit code.