Bookings and holds
Booking states, the 10-minute hold, deposits and the no-double-booking guarantee.
Updated:
Why a hold
A WhatsApp conversation can take minutes: the customer picks a time, then you ask their name, then maybe a deposit. Meanwhile other customers and the API are competing for the same slot. A hold reserves the slot for 10 minutes (15 when a deposit is required) and expires on its own.
States
| Status | Meaning | Occupies the slot |
|---|---|---|
held | Temporary reservation | Yes |
pending_payment | Waiting for the deposit | Yes |
confirmed | Booked | Yes |
checked_in | The customer arrived | Yes |
completed | Service done | No |
cancelled | Cancelled (or replaced by a reschedule) | No |
no_show | Marked absent by a person | No |
expired | Hold not confirmed in time | No |
held ──confirm──► confirmed ──check-in──► checked_in ──► completed
│ └─(deposit)─► pending_payment ──paid──► confirmed
└─expire──► expired confirmed ──cancel──► cancelled
Guarantees
- Every resource a booking occupies is stored as an allocation with a time range.
- For
exclusiveresources, PostgreSQL rejects overlapping allocations with an exclusion constraint. - For
pooledresources, capacity counters can never exceed the limit (checked atomically). - A multi-resource booking is written in a single transaction: if any resource clashes, nothing is booked.
POST /holdsandPOST /holds/{id}/confirmrequire anIdempotency-Key: retrying a request never creates a second booking.
If a slot was taken while the customer was deciding, you get 409 with code: "slot_taken" and a list of alternatives.
Reschedule and cancel
- Reschedule creates a new booking and cancels the old one (linked by
rescheduled_to). Reminders move automatically. - Cancel frees the slot and cancels pending reminders.
- No-show is never automatic: the system suggests it, a person confirms it.
Deposits
If the service has deposit_cents, confirming a hold leaves the booking as pending_payment and the slot stays blocked. Charging the deposit online is coming soon: until it ships, the booking does not move to confirmed on its own and payment.paid is not emitted.