Skip to main content

Booking Intents

A BookingIntent tracks the lifecycle of a multi-step booking attempt, enabling slot holding and progressive data collection before creating an appointment.

Booking intents are useful when your booking flow spans multiple steps or screens. Rather than collecting all information upfront and creating an appointment in a single call, you can create a booking intent early in the flow, hold the selected time slot so it isn't taken by someone else, and gradually attach data as the user progresses. When the user confirms, you complete the intent and an Appointment is created.

Lifecycle

A booking intent moves through these statuses:

pending ──▶ slot_selected ──▶ completed

└──▶ abandoned
StatusDescription
pendingThe initial status. No time slot has been selected yet.
slot_selectedA time slot has been selected (start_at and end_at are set). The slot may be held depending on the hold policy.
completedThe booking intent has been completed and an Appointment has been created.
abandonedThe booking intent has been explicitly abandoned. Any active slot hold is released.

Status transitions happen automatically. When you update a booking intent to include start_at and end_at, the status transitions from pending to slot_selected. You don't set the status directly — it is derived from the state of the intent.

Slot holding

Slot holding prevents double-booking by temporarily blocking a time slot while the user completes the booking flow. When enabled, the selected slot is blocked from availability for other callers until the hold expires or the intent is completed or abandoned.

Hold settings are configured via the booking_policy.hold object (see BookingIntentBookingPolicy):

FieldTypeDescription
booking_policy.hold.enabledbooleanWhether slot holding is enabled. When omitted at creation, falls back to the service's hold policy.
booking_policy.hold.durationstringThe hold duration as an ISO 8601 duration string (e.g., PT10M for 10 minutes). Falls back to the service's hold policy.
hold_untilstringRead-only. The UTC datetime when the current hold expires. Set automatically when a slot is selected with holding enabled.

When a service is set on the booking intent, the service's default hold policy (the "Hold selected slots" setting under Booking Policy) is automatically applied unless booking_policy.hold is explicitly provided when creating or updating the booking intent.

When a hold expires, the slot becomes available to other callers again. The booking intent itself remains in slot_selected status — an expired hold does not abandon the intent.

Progressive client data

The client_data field lets you collect client information incrementally across multiple updates. Each update merges the provided fields with existing values rather than replacing them entirely.

For example, you might collect the client's name on one screen and their email on the next:

# Step 1: Collect name
curl -X PATCH /v1/booking_intents/:id \
-d '{"client_data": {"first_name": "Jane", "last_name": "Doe"}}'

# Step 2: Collect email
curl -X PATCH /v1/booking_intents/:id \
-d '{"client_data": {"email": "jane@example.com"}}'

After both updates, client_data contains all three fields. When the booking intent is completed, this data is used to create or match a Client and populate the resulting appointment.

The available client_data fields are: first_name, last_name, email, phone, locale, time_zone, and reference_id.

Form submissions

The submissions field lets you accumulate form responses during the booking flow. Each entry contains a form_id (the form's object ID) and a responses map of field answers. When a service has forms attached, these submissions are collected progressively and validated at completion time.

Each update merges by form_id — providing a submission for an existing form replaces that entry, while new form IDs are appended:

# Step 1: Submit responses for the first form
curl -X PATCH /v1/booking_intents/:id \
-d '{
"submissions": [
{"form_id": "frm_abc123def456", "responses": {"field_1": "Answer A"}}
]
}'

# Step 2: Submit responses for a second form
curl -X PATCH /v1/booking_intents/:id \
-d '{
"submissions": [
{"form_id": "frm_def456abc123", "responses": {"field_2": "Answer B"}}
]
}'

After both updates, the intent's submissions array contains entries for both forms.

Workflow

The workflow property is a server-computed object that provides step routing and completability signals for the booking flow. It appears on single-intent responses (get, create, update, complete) but is null on list responses.

Rather than inferring flow state from raw fields like status, locked_fields, and client_data, the frontend can use workflow to determine which step to show, which steps are available, and whether the intent can be completed.

FieldTypeDescription
resume_stepstringThe step the UI should show on load or resume. One of: booking, info, form:<form_id>, confirmed, defunct.
available_stepsstring[]Ordered list of steps the user can visit. Derived from the service's forms and locked_fields.
can_completebooleanWhether the intent has enough valid data to complete right now.
can_change_slotbooleanWhether slot fields (start_at, end_at, time_zone) are unlocked and can be modified.
is_defunctbooleanWhether the intent is in an unrecoverable state and should render a terminal UI.
defunct_reasonstring | nullWhy the intent is defunct. One of: abandoned, no_available_steps, slot_expired, status_invalid. Null when not defunct.

When is_defunct is true, available_steps is cleared to an empty array and resume_step is set to defunct.

Locked fields affect which steps appear in available_steps:

  • Locking slot removes the booking step.
  • Locking client_data removes the info step.
  • Locking submissions removes all form:<form_id> steps.

Requirements

The requirements property provides per-section completeness status, enabling the frontend to show progress indicators or prevent navigation to the complete step without parsing error details. Like workflow, it appears on single-intent responses but is null on list responses.

FieldTypeDescription
bookingobject{ complete: boolean } — whether the booking step is complete (provider and slot are set).
infoobject{ complete: boolean } — whether the info step is complete (required client fields are present, or a client is linked).
submissionsarrayPer-form completeness: [{ form_id: string, complete: boolean }], in the same order as the service's forms.

A booking intent is ready to complete when all applicable requirements are satisfied, which is also reflected by workflow.can_complete.

Completing a booking intent

To complete a booking intent, call the Complete booking intent endpoint. Completion requires:

  • A time slot must be set (start_at and end_at).
  • A service must be assigned.
  • All required client data must be provided (or a client must be linked).
  • All required form submissions must be present (if the service has forms).

You can check workflow.can_complete to determine whether the intent is ready to complete, and inspect requirements to see which sections still need data.

The complete endpoint accepts the same update fields as the update endpoint (service_id, provider_id, start_at, end_at, client_data, submissions, etc.). These fields are applied atomically before the intent is completed, so you can finalize data and complete in a single request.

On success, the booking intent transitions to completed status and the resulting Appointment is available in the appointment field of the response.

Abandoning a booking intent

Call the Abandon booking intent endpoint to explicitly abandon a booking intent. This releases any active slot hold and transitions the status to abandoned.

Abandon a booking intent when the user exits the booking flow without completing it, so the held slot is freed up for other callers.

Authenticated vs. public API

Booking intents are available through two API surfaces:

  • Authenticated API — Uses your API key. Full control over all fields. Returns the complete BookingIntent object.
  • Public API — No API key required. Intended for client-side use. Restricted capabilities and returns a simplified PublicBookingIntent object that omits sensitive and admin-only fields.

A common pattern is to use the authenticated API on your server to create a booking intent with pre-configured settings and locked fields, then hand the intent ID to the client to complete the flow via the public API.

CapabilityAuthenticated APIPublic API
Create / update / completeYesYes
AbandonYesYes
List / getYesGet only
Set submissionsYesYes
Set booking_policyYesNo
Set locked_fieldsYesNo
Set metadataYesNo
Set slot_prevalidatedYesNo
Link client_idYesNo
Returns full objectYesNo

Locked fields

The authenticated API can lock specific fields so that the public API cannot modify them. This is useful when your server pre-selects values (like a service or provider) that the end user should not be able to change.

Set locked_fields when creating or updating a booking intent via the authenticated API:

curl -X POST /v1/booking_intents \
-d '{
"service_id": "srv_1234567890",
"locked_fields": ["service_id", "provider_id"]
}'

The lockable fields are: service_id, provider_id, slot, client_data, and submissions. The slot value locks all time-related fields (start_at, end_at, time_zone) as a group.

When updating via the authenticated API, locked_fields replaces the existing set entirely.

Auto-assigning a provider

Set auto_assign_provider to true when you want the system to automatically select an available provider for the chosen time slot. This is useful when your flow lets the user pick a time without choosing a specific provider.

Auto-assignment is evaluated when a time slot is selected, so the held slot is reserved for a specific provider's schedule and remains bookable at completion. If a provider_id is also specified, the explicit provider takes precedence.

Webhook events

Four webhook events are emitted during the booking intent lifecycle:

EventDescription
booking_intent.createdA new booking intent was created.
booking_intent.updatedA booking intent was updated.
booking_intent.completedA booking intent was completed.
booking_intent.abandonedA booking intent was abandoned.

See the Webhooks guide for details on registering webhooks and handling events.

Key properties

PropertyTypeDescription
idstringThe unique identifier (e.g., bi_d025a96ac0c6).
statusenumThe current status: pending, slot_selected, completed, or abandoned.
start_atZonedDateTimeThe start time of the selected slot.
end_atZonedDateTimeThe end time of the selected slot.
hold_untilstringWhen the current slot hold expires (UTC).
client_dataobjectProgressive client data collected during the booking flow.
submissionsarrayForm responses accumulated during the booking flow.
workflowBookingIntentWorkflowServer-computed step routing and completability state. Present on single-intent responses only.
requirementsBookingIntentRequirementsPer-section completeness status. Present on single-intent responses only.
locked_fieldsarrayFields locked from update via the public API.
appointmentAppointmentThe resulting appointment (set when completed).

See the BookingIntent schema for complete details.