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

StatusMeaningOccupies the slot
heldTemporary reservationYes
pending_paymentWaiting for the Pix depositYes
confirmedBookedYes
checked_inThe customer arrivedYes
completedService doneNo
cancelledCancelled (or replaced by a reschedule)No
no_showMarked absent by a personNo
expiredHold not confirmed in timeNo
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 exclusive resources, PostgreSQL rejects overlapping allocations with an exclusion constraint.
  • For pooled resources, 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 /holds and POST /holds/{id}/confirm require an Idempotency-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.