Skip to main content
Submits a held booking. Depending on the business’s settings, this creates either an order or a lead, both marked Pending review. The endpoint atomically revalidates the hold, resources, services, and price before it creates the record.
This endpoint is rate limited to 5 requests per 10 minutes per IP address and 100 requests per 10 minutes per tenant after Turnstile succeeds. It requires a valid Cloudflare Turnstile token. Send the request body as JSON (Content-Type: application/json).
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.
If you retry while the first request is still being processed, the second returns 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.
Generate the key once, before the customer presses submit — not inside the retry. A key generated per attempt defeats the purpose.

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

Returns 200 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_token
2

Origin and tenant domain

origin_not_allowed, origin_registry_unavailable
3

Body and idempotency key

invalid_body, invalid_idempotency_key
4

Workspace lookup

not_found — unknown token, or booking disabled
5

Completed idempotent replay

Returns the original successful response without creating another record
6

CAPTCHA

captcha_required, captcha_failed, captcha_unavailable
7

Rate limit

too_many_requests — checked against durable IP and tenant quotas only after CAPTCHA succeeds
8

Required fields

missing_customer, missing_required_customer_fields, no_services, hold_required, missing_vehicle
9

Atomic booking commit

offer_expired, slot_unavailable, booking_in_progress
CAPTCHA is verified before your field validation. While you’re building the integration without a working Turnstile token, every malformed request answers captcha_failed — you won’t see missing_customer or no_services until the CAPTCHA passes. Get Turnstile working first, then debug your payload.

Errors

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.