Bookings and holds
Booking states, the 10-minute hold, Pix deposits and the no-double-booking guarantee.
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, the booking page and the API are competing for the same slot. A hold reserves the slot for 10 minutes (15 when a Pix deposit is required) and expires on its own.
States
| Status | Meaning | Occupies the slot |
|---|---|---|
held | Temporary reservation | Yes |
pending_payment | Waiting for the Pix 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 returns pending_payment with a Pix code. When the payment provider notifies us, the booking becomes confirmed and payment.paid is emitted. A payment that arrives after the hold expired is flagged for refund.