Errors
Every error code, what caused it and what to do next.
Every response outside 2xx is one JSON object. That includes requests refused
before they reach an endpoint: an unconvertible parameter (?page=abc), a
missing parameter or header, malformed JSON or a value of the wrong type.
{
"code": "SLOT_UNAVAILABLE",
"message": "The requested time slot is no longer available. Pick another start from GET /api/v1/availability/starts.",
"field": "scheduledStart"
}Branch on code, never on message
code is the stable contract. message is an English sentence for logs and
support. It can change wording at any time and never echoes parser internals or
your raw input.
A wrong Content-Type answers 415 and a method the path does not support
answers 405 (with an Allow header), both with the code INVALID_INPUT.
An unknown path answers 404 NOT_FOUND, but only to a valid key. Without one,
every /api/v1 request is 401 INVALID_API_KEY first, whether the path exists
or not.
Request size
Every /api/v1 request body is capped at 64 KB. A larger body answers
413 FILE_TOO_LARGE ("The request body is too large (max 64 KB).") before it
is read. The one exception is POST /images with a secret key, a multipart
upload with a 60 MB limit.
The field an error is about
An error that concerns one input of the request also carries field: the name
of that input as the reference spells it. It is a body path such as paidAt,
customer.email or billing.ico, a query parameter such as fromDate, or a
header such as Idempotency-Key. When the error is not about a single input,
field is absent.
{ "code": "INVALID_INPUT", "message": "paidAt is required when issuing a voucher through the API.", "field": "paidAt" }Keep branching on code. Use field to point at the input in your own UI.
message is an English sentence for developers and logs, so do not show it to
your customers verbatim.
Strict request bodies
Every /api/v1 write body is checked against the endpoint's schema. A property the
endpoint does not define (a typo, or a field of another endpoint) is
400 UNKNOWN_FIELD, with field naming it as a path:
{ "code": "UNKNOWN_FIELD", "message": "...", "field": "customer.foo" }Nothing is stored and the Idempotency-Key is not used up, so fix the body and
retry. The one free-form object is metadata of POST /orders. PATCH /studio
refuses the fields that change in the admin only (bank account, currency, allowed
domains and the like) the same way instead of ignoring them.
Every code
- INVALID_
API_ KEY401 Missing, malformed, unknown or revoked key, or the studio is deleted.
Check the key in the portal or, for a studio key, in the studio's admin. A wrong key and an unknown key look the same on purpose.
- INSUFFICIENT_
SCOPE403 The key lacks the scope this endpoint needs. A publishable key on a scoped endpoint ends here too.
Give the secret key the scope, or call from your server.
- PLAN_
REQUIRED403 A studio is below the Pro subscription, or a live key of an organization's business was used before live mode was turned on.
For a studio, subscribe to Pro and the key works again. For an organization, turn on live mode in the portal, or use a test key.
- BILLING_
SUSPENDED403 A live organization key whose organization was suspended because the monthly charge stayed unpaid for 14 days. Test keys keep working.
Live mode comes back with a new payment.
- ORIGIN_
NOT_ ALLOWED403 A browser request from a domain the key's owner has not allowed.
Add the domain to the allowed websites: in the portal under Settings → Allowed websites, or with allowedDomains on PATCH /businesses/{id}. A studio does it in its admin.
- SECRET_
KEY_ IN_ BROWSER403 A secret key was sent from a browser (the request carried an Origin header).
Use a publishable key in the browser. Keep the secret key on your server.
- DEMO_
STUDIO403 A write reached the public demo studio. Nothing was stored.
Use a test studio for writes.
- NOT_
FOUND404 The resource does not exist or belongs to another studio, or the path does not exist.
Check the id and the path.
- INVALID_
INPUT400 A parameter, header or body field is missing, malformed or out of range. 405 and 415 use this code too.
The message names the field.
- UNKNOWN_
FIELD400 A write request body contains a property this endpoint does not define (a typo, or a field of another endpoint).
fieldnames it, for examplecustomer.foo. Nothing was stored and the Idempotency-Key is not used up.Remove or rename the field named in
fieldand send the request again, with the same Idempotency-Key if you like. - INVALID_
SUBJECT400 The subject sent with an order or booking link does not fit the studio's subject type: unknown type, missing required attribute, wrong value type, a choice outside its options, or an unknown attribute.
Read field (the attribute key, or subject.type) and compare with GET /subject-types.
- CONFLICT409
The request is valid but the studio's state forbids it.
Read the message, it says which state blocks the request.
- STUDIO_
NOT_ PUBLISHED409 The studio is not published, so it has no booking data to read and takes no bookings. GET /availability/starts, GET /availability/calendar and POST /orders answer this, with a publishable or a secret key.
Publish the studio in the admin, or use the test studio: a test studio is always bookable.
- RATE_
LIMITED429 A burst or storefront limit was hit.
Wait Retry-After seconds, then retry.
- QUOTA_
EXCEEDED429 The monthly quota of a secret key is used up.
Retry after Retry-After seconds (the next UTC month) or make fewer calls. The quota cannot be raised.
- IDEMPOTENCY_
IN_ PROGRESS409 Another request with the same Idempotency-Key is still running.
Retry shortly with the same key.
- IDEMPOTENCY_
KEY_ MISMATCH422 The same Idempotency-Key came with a different body.
Use a new key for a new request.
- API_
KEY_ DAILY_ VOUCHER_ CAP_ EXCEEDED422 POST /gift-vouchers only: this key would pass its daily cap on voucher value.
Wait for the studio's next day, or ask the owner to raise the cap. Retrying unchanged does not help.
- CANNOT_
PUBLISH_ WITHOUT_ SERVICES409 A publish call (POST /catalog/publish or POST /publish) on a studio with no active service. Nothing was published.
Create or unarchive a service, then publish again.
- CANNOT_
PUBLISH_ WITHOUT_ CONTACT409 A publish call (POST /storefront/publish or POST /publish) on a studio with neither a phone nor a public e-mail. Nothing was published.
Add a phone or a public e-mail to the studio, then publish again.
- FILE_
TOO_ LARGE413 POST /images only: the file is over the upload limit of 60 MB.
Upload a smaller file.
- SLOT_
UNAVAILABLE409 POST /orders only: the window was taken. Nothing was created.
Ask GET /availability/starts again and let the customer pick another start.
- TERM_
IN_ PAST409 POST /orders/{id}/reviveonly: the booked term has passed or starts too soon to be honoured, so the order staysCANCELLED.Send the revive request again with a new
scheduledStartthat lies far enough in the future. - TERMS_
VERSION_ STALE409 The studio published new terms and conditions after you loaded GET /studio, so the
acceptedTermsVersionyou sent is no longer current. Nothing was created and the Idempotency-Key is not used up.Read GET /studio again, show the customer the new terms, and resend the order with
acceptedTermsVersionset to the newterms.version. - SERVICE_
KNOWN_ CARS_ ONLY422 A selected service is sold only for a car the studio already knows (
orderAudienceKNOWN_CARS) and the order does not prove one: noknownCarToken, an invalid or expired one, or a car without a completed order. Nothing was created.Send a valid
knownCarTokenfrom the studio's return reminder, or drop that service from the order. - ADDON_
SOLD_ ONLY_ WITH422 A selected add-on is sold only together with certain services (
soldOnlyWithServiceIds) and none of them is in the order. Nothing was created.Add one of the services the add-on is sold with, or drop the add-on from the order.
- PENDING_
ORDERS_ LIMIT429 The studio confirms orders manually and already has 10 unconfirmed orders from the API, so the publishable key's next order is refused. Secret keys and studios that confirm automatically have no such cap.
Wait until the studio confirms or cancels some of the pending orders, then retry. Retry-After does not apply; do not loop.
- PDF_
UNAVAILABLE503 The voucher PDF could not be rendered right now.
Retry.
- INTERNAL_
ERROR500 Something went wrong on our side.
Retry shortly.
Retrying
Retry INTERNAL_ERROR and PDF_UNAVAILABLE shortly. Wait Retry-After seconds
on RATE_LIMITED and QUOTA_EXCEEDED. For writes, retry with the same
Idempotency-Key so the write happens once; see
Idempotency. Do not retry the other codes
unchanged: the request has to change first.