For an external browser integration, register the website hostname first. Render the CRM-provided Turnstile site key with action
booking_submit. The server verifies both the action and the hostname returned by Cloudflare. See CAPTCHA requirement.Path parameters
string
required
Your booking page token (booking slug or lead token).
Headers
string
A UUID that identifies this booking attempt. Send the same key when retrying a request that timed out, and the booking is created once rather than twice.The value must be a UUID — a malformed key is rejected with
400 invalid_idempotency_key. If you omit the header, the server generates a key internally, which protects nothing across retries: a retried request without the header creates a second booking.409 booking_in_progress rather than creating a duplicate. Treat it as “wait and check”, not as a failure — back off briefly, then retry the same key.
Request body
string
required
A valid Cloudflare Turnstile token from the approved CRM booking widget on your registered hostname. The widget must use action
booking_submit.object
required
The customer’s details.
number[]
required
One or more service IDs from the config endpoint. Send the same selection used to obtain the offer and hold; the held service snapshot remains authoritative at commit time.
string
required
The unexpired token returned by Hold or release a slot. A slot’s
start time or offer_token cannot replace it.object
Vehicle details. Required when the workspace business type is
auto; optional for other business types.number
Optional membership plan ID to enroll the customer in at booking time.
string
Optional notes from the customer (max 1000 chars).
Response
Returns200 OK on success.
boolean
true when the booking was created.string | null
The business’s custom confirmation message to show the customer, if configured.
string
What was created — an order or a lead. Which one you get depends on the business’s booking settings, not on your request, so read this rather than assuming. See Order vs. lead creation.
number
The identifier of the created order or lead.
string
The initial review state. New public bookings return
pending_review.boolean
true for an order that reserves the held time; false for a lead that records the requested time and releases the hold.Retrying with the same
Idempotency-Key after a success replays the original response — the same resource_id and resource_type come back, and nothing new is created.Example
Order of validation
Checks run in a fixed order, and the first failure returns immediately. This matters when you’re debugging: a request with several problems reports only the earliest one.1
Token format
invalid_token2
Origin and tenant domain
origin_not_allowed, origin_registry_unavailable3
Body and idempotency key
invalid_body, invalid_idempotency_key4
Workspace lookup
not_found — unknown token, or booking disabled5
Completed idempotent replay
Returns the original successful response without creating another record
6
CAPTCHA
captcha_required, captcha_failed, captcha_unavailable7
Rate limit
too_many_requests — checked against durable IP and tenant quotas only after CAPTCHA succeeds8
Required fields
missing_customer, missing_required_customer_fields, no_services, hold_required, missing_vehicle9
Atomic booking commit
offer_expired, slot_unavailable, booking_in_progressErrors
Get config
Services, pricing, and settings.
Get availability
Get short-lived offers for open slots.
Hold a slot
Create the hold required by this endpoint.