Skip to content

Errors & rate limits

{ "success": false, "error": { "code": "SLOT_TAKEN", "message": "That slot was just taken. Pick another time." } }

Branch on error.code — it is stable. error.message is English and safe to display, but your own localised copy will read better.

StatusCodeCauseWhat to do
404RESOURCE_NOT_FOUNDUnknown slug, room, hold or tokenCheck the identifier
404SITE_NOT_LIVEThe studio unpublished its siteHide booking; show phone / WhatsApp
403STUDIO_NOT_VERIFIEDOwnership not confirmed yetStudio-side; nothing a guest can do
409ROOM_NOT_BOOKABLEOnline booking off, or room unpriced/inactiveRemove it from your room list
StatusCodeCauseWhat to do
400VALIDATION_ERRORMalformed field, bad duration, idempotency key reused with a different bodyFix the payload; re-derive durations from the room
400CAPTCHA_FAILEDCaptcha token missing, expired, already used, solved for another action or on a host that is not the studio’sReset the widget, get a fresh token on the studio’s host with action hold
400PAYMENT_MODE_DENIEDMode not in room.paymentModesOffer only the modes the room allows
400GROUP_SIZE_INVALIDgroupSizeId not offered by the room, or partySize outside 1…capacityOffer only the room’s group sizes; clamp the people counter to capacity
StatusCodeCauseWhat to do
409OUTSIDE_HOURSThe room is closed thenRefetch availability — your span maths drifted
409OUTSIDE_BOOKING_WINDOWBreaks minNoticeMin or maxAdvanceDaysClamp the calendar to the window
409SLOT_TAKENSomeone booked it firstRefetch, keep the guest’s details, offer another time
410HOLD_EXPIREDexpiresAt passed (5 minutes unverified, 12 minutes after creation once verified)Restart from the hold with a new idempotency key
409HOLD_NOT_VERIFIEDConfirming before verifyingSend the guest back to the code step
409HOLD_ALREADY_CONFIRMEDDouble submitShow the existing booking
409CANCEL_TOO_LATEInside the cancellation windowShow the studio’s phone
409INVALID_STATEThe action makes no sense nowRe-read the booking
StatusCodeCauseWhat to do
400CODE_INVALIDWrong or expired codeLet them retype; show attempts left
429TOO_MANY_ATTEMPTS5 wrong codesThe hold is burnt — restart
429RATE_LIMIT_EXCEEDEDToo many requestsBack off, see below
StatusCodeCauseWhat to do
401KEY_INVALIDUnknown or malformed keyCheck the key you stored
401KEY_REVOKEDThe studio revoked the keyAsk the studio for a new key
401KEY_EXPIREDRotated more than 7 days agoSwitch to the replacement key
403KEY_ENV_MISMATCHTest key on production, or live key elsewhereUse the key for this environment
403SCOPE_MISSINGThe key lacks details.scopeAsk for a key with that scope
403KEY_ROUTE_NOT_ALLOWEDKeys cannot call this endpointDo it in the dashboard
StatusCodeCauseWhat to do
409PAYMENTS_UNAVAILABLEStripe not configuredOffer on_site
422PHONE_NOT_SUPPORTEDNot a mobile number in a country we text (premium-rate, shared-cost, landline, VoIP and foreign numbers are refused)Offer channel: "email"
503SMS_UNAVAILABLETexts are paused: provider down, the daily SMS budget is used up, or SMS is switched off for this studioOffer channel: "email" (it keeps working)
503HOLDS_UNAVAILABLEOnline booking is paused for this studioShow the studio’s contact details
500INTERNAL_ERROROur faultRetry once; include X-Request-ID if you report it
ScopeLimit
Reads (GET /public/*)120 req/min per IP
Writes (holds, verify, resend, confirm, cancel)15 req/min per IP
Holds per contact5 per hour
Holds per IP20 per hour
Code resends3 per hold, ≥ 60 s apart
Verification attempts5 per code (a 6th wrong code burns the hold), counted atomically
Unverified holds per studio3 at a time (sandbox sites excepted); 429 RATE_LIMIT_EXCEEDED beyond

Exceeding any of them returns 429 RATE_LIMIT_EXCEEDED.

  • GETs are safe to retry.
  • POST /holds is safe to retry with the same idempotencyKey — you get the same hold back, no second SMS.
  • verify, confirm and cancel are not idempotent. On a network timeout, re-read state (GET /holds/{id} is not public, so use the manage endpoint once a booking exists) before firing again.
  • Back off exponentially on 429 and 5xx. Never retry a 4xx other than 429 without changing the request.