Introduction
A whole-house rental has exactly one unit of inventory. If two people try to book the same dates at the same moment, only one of them can get through. And it isn't just the official site — a booking can land from an OTA like Airbnb at the very same time.
This article is about the order of operations that lets a booking reach confirmation without another channel snatching the dates mid-payment. The hard part isn't the technology; it's the judgment call about what to do first.
Order Decides Everything
There's a Reason for Each of the Three Steps
Between the guest pressing Book and landing on the payment screen, the server does three things, in this order.
Don't trust the figure that was on screen — confirm it again with the channel manager
Create a provisional booking "with an availability check". Only now is the room actually secured
Only once the room is held do we build the payment provider's screen
There's a clear reason for that sequence. Take the card first and you can end up in the worst possible state: you're holding someone's card details and you don't have the room. In this order, the worst case is that you held a room unnecessarily — and it releases itself after a while.
"Check, Then Hold" Isn't Fast Enough
If you do it yourself in two steps — check whether it's free, then hold it if it is — a booking can arrive from an OTA in the gap between them. Even if that gap is a few hundred milliseconds, anything that can happen eventually will.
So we used the channel manager's own capability to check availability and create the provisional booking in a single operation. If there's no availability, the provisional booking simply isn't created and an error comes back. No gap to slip through.
Let whoever owns the inventory do the locking
Assemble "check, then lock" yourself and there will always be a gap. Asking the system that holds the source of truth to do the check and the lock as one operation is the only reliable way.
Holds Need an Expiry
30 Minutes Plus a 5-Minute Grace Period
If a hold is placed and payment never happens, the room is unsellable forever. So we set the payment screen to expire after 30 minutes, and made the hold expire at 35 — 30 plus a five-minute grace period.
Expiry is tracked in our own database, and a scheduled job that runs every ten minutes releases holds that have lapsed. We also receive expiry notifications from the payment provider, but those are strictly a backup.
Never let an external notification be your only lifeline
We once had notifications from the payment provider fail more than a hundred times in a row. If the design had required a notification before a room could be released, every booking during that window would have been lost. Keeping our own expiry as the primary mechanism, with external notifications as the secondary, is the safe arrangement.
React Immediately to the Back Button
There's no need to hold a room for 35 minutes when the guest has already hit Back from the payment screen. As soon as we detect they've returned, we invalidate the payment screen on the spot and release the hold immediately.
If "changed my mind" shows up in inventory right away, the next guest can book those dates.
Tidy Up Duplicates From the Same Person
A guest searches "August 1–2", backs out, then searches "August 1–3" instead. Totally normal behavior — but build it naively and one person ends up holding two rooms, competing with themselves for inventory.
So before creating a new hold, we release any older hold belonging to the same email address. And if the same person proceeds to book the same dates a second time, we send them back to their existing payment screen rather than creating a new one.
Assume Some People Have Bad Intentions
Cap How Many Holds Can Be Active at Once
Hammering the Book button, or scripting a flood of booking attempts, would let someone tie up inventory and effectively shut the business down. So we capped it: a maximum of five holds active at once from the same source.
The key detail is counting holds currently active, not attempts made. If people who completed a booking normally, or who simply changed their mind, stay on the counter, you end up blocking perfectly legitimate returning visitors.
Don't Give Up Just Because the Response Looks Wrong
When an API response doesn't match the expected shape, the normal move is to error out and stop. But with booking registration, the booking may well have succeeded and only the response is broken. Treat that as an error and the guest is told "it failed" while the room stays held.
So when we can't parse a response, we re-fetch the list of bookings for those dates and check whether one carrying our own booking reference exists. If it does, we treat it as a success.
Never Retry a Booking Registration
Automatically resending a failed request when the connection is flaky is a common and sensible trick. We never do it for booking registration. If the first attempt actually succeeded, you end up with two identical bookings.
Operations that only read data are safe to retry; operations that create something are not. Drawing that line clearly is unglamorous but genuinely important.
Wrapping Up
Three things mattered most in preventing double-bookings.
- Protect through ordering — secure the inventory first, take the card second; the reverse makes the damage far worse
- Let the source of truth do the locking — ask for the availability check and the hold as one operation, leaving no gap
- Own the expiry yourself — keep external notifications as a backup and make your own scheduled job the primary
The payment side is covered in "Stripe Payment Design for Lodging", and detecting changes after booking is covered in "Reliable Sync with Webhooks Plus Hourly Reconciliation".
Stripe Payment Design for Lodging — Choosing Not to Charge at Booking
What comes after the hold: saving the card and charging it automatically later.
Reliable Sync with Webhooks Plus Hourly Reconciliation
How we detect changes and cancellations after a booking is confirmed.
Sync Design Between a Channel Manager (Beds24) and Our Own Site
For the whole sync architecture, start with this hub article.