How To Prevent Double Booking

The race condition, the sync lag, the manual entry, and the four defences that stop two people landing in the same slot.

TA The Appntmnts Team August 8, 2026
Article Scheduling Operations

The Short Version

Double bookings come from four places: two clients racing for the same slot, a conflict arriving late through calendar sync, an appointment typed straight into a calendar, and two services competing for one room. Each needs a different defence, and no integration is fast enough to make the second one disappear entirely.

A double booking is two commitments occupying one piece of time. It is one of the few scheduling failures with no graceful recovery: somebody has to be told their confirmed appointment is not happening, and that conversation costs more goodwill than almost anything else a booking system does.

There are four distinct causes needing four distinct defences. Two clients selecting the same slot seconds apart is a race condition, solved with a hold and an atomic write. A conflict created elsewhere and arriving late is a sync lag problem, which cannot be eliminated, only narrowed and handled. An appointment typed directly into a calendar is a conflict check problem. Two services needing the same room is a resource modelling problem. If your conflicts come from a calendar integration behaving oddly rather than from the booking flow, why calendar sync fails is the more useful piece.

The Four Causes At A Glance

CauseWhat It Looks LikeDefenceCan It Be Eliminated
Race between two bookersTwo confirmations for the same slot, seconds apartSlot hold plus an atomic write with a uniqueness constraintYes
External event arriving via syncA meeting created in another calendar overlaps a booking taken minutes earlierFast sync, re-check at confirmation, clear conflict handlingNo, only narrowed
Manual entry in a calendarAn appointment written straight into the diary, invisible to the booking pageTwo way sync with the calendar treated as a busy sourceMostly
Resource contentionTwo staff both booked into the one treatment roomModel the resource and check it as a separate constraintYes

The Race Condition

Two clients are on your booking page at the same moment. Both load Thursday, both see 14:00 free, both fill in the form. One submits at 14:20:03 and the other at 14:20:05.

A naive system does the same for both: read the bookings table, see nothing at 14:00, write a new row. The gap between the read and the write is where the second booking slips through, because the first row was not there when the second request checked. The window is small, which is why it survives testing and appears in production.

The window is wider than it looks: it starts when the availability list was rendered, which may have been three minutes earlier. Every booking page is showing slightly stale data at all times.

Holding The Slot

The first defence is to reserve the slot when the client selects it, before they finish the form. The hold is a short lived claim that removes the slot from everyone else availability while one person completes their booking, and it expires if they abandon the form.

Hold length is a judgement. Too short and a client filling in a long intake form loses the slot mid typing, which is worse than a rare collision. Too long and abandoned forms make a busy page look empty. Five to fifteen minutes suits most flows, and if a card payment is involved the hold has to outlast the authorisation, not just the form.

The Atomic Write

Holds reduce collisions but do not make them impossible, since two requests can race for the hold itself. The write has to be atomic: the conflict check and the booking creation must happen as one indivisible operation the database enforces, not two statements the application runs in sequence. In practice that means a uniqueness or exclusion constraint covering resource and time, or a transaction that locks the relevant rows before checking.

The application then treats a constraint violation as a normal outcome rather than an error: catch it, tell the client the slot has gone, and re-render availability. A system that produces a five hundred error page when two people collide has the right protection and the wrong handling. The rule to hold onto is that if your conflict check is application code reading and then writing, the race exists no matter how careful the code is. Only the database can arbitrate.

Re-Checking At Confirmation

The third layer is a final conflict check at the moment of confirmation, after the form and any payment step, immediately before the booking is committed. This catches conflicts that appeared during the client session from a source other than another booker, which in practice means a calendar event that synced in while they were typing.

The three layers do different jobs. The hold prevents most collisions, the atomic write guarantees correctness when one happens anyway, and the re-check catches what arrived from outside the booking flow. Skipping any one leaves a real gap.

External Calendar Conflicts And Sync Lag

This is the cause with no complete fix, and it is worth being direct about why.

A booking page learns about events in connected calendars either by polling on a schedule or by receiving a push notification. Polling means a delay of however long the interval is. Push notifications are faster but not instant: the provider says something changed, the receiving system fetches the detail, and both steps take time and can be retried after a failure.

So there is always a window, usually seconds to a couple of minutes, between an event being created in your calendar and the booking page knowing about it. A client picking a slot inside that window gets a genuine conflict, and no amount of engineering removes the window. Anyone claiming instant sync is describing a fast sync.

Designing Around The Lag

Since you cannot close the window, narrow it and handle what gets through:

  • Use push notifications where the provider offers them, with polling as a fallback rather than the primary mechanism.
  • Re-check at confirmation with a fresh read of the calendar rather than the cached view, so the window shrinks to the length of one request.
  • Write the booking to the calendar immediately, so a colleague about to book over your appointment sees it as fast as possible.
  • Make the conflict visible rather than silent. If a conflicting event turns up thirty seconds after a booking is confirmed, someone should be told at once, with both entries side by side.
  • Decide precedence in advance. Usually the earlier entry wins and the later one is flagged, though a business may prefer the client booking to win because it has a customer attached.

The operational point: a rare, visible, quickly handled conflict is acceptable. A rare, silent one discovered by the client in the waiting room is not. Design for detection, not only prevention. Google Calendar two way sync covers the sync mechanics.

Manual Bookings Made Directly In A Calendar

The most common real world double booking has nothing to do with concurrency. Someone takes a call, opens their calendar, types the appointment in, and never touches the booking system, so the page offers the slot to the next person who looks. Two way sync fixes this if set up correctly, and these are the details that go wrong:

  • Every calendar the person genuinely uses must be a busy source. A personal calendar holding the school run is a legitimate conflict source even with no work appointments in it.
  • Free versus busy status matters. An event marked free does not block, and most calendar clients default all day events to free, so a day of leave may block nothing at all.
  • Declined and tentative events need a policy. Most businesses ignore declined and treat tentative as busy.
  • The habit matters too. If people also write appointments on paper, no integration reaches them.

Where a team shares a diary this compounds, since the question is not only whether a person is free but which person should take the booking: see managing multiple staff and locations.

Multi Resource Conflicts

Some appointments consume more than one thing. A treatment needs a practitioner and a room, a demo needs a salesperson and a meeting room, and checking only the person leaves the other resource free to be double booked. Treat every constrained thing as a bookable resource with its own availability, and require an appointment to hold every resource it needs for its whole block, buffers included.

Two refinements come up quickly. Resources sometimes have capacity greater than one, such as a room that takes three people, so the check is a count against a limit rather than a yes or no. And resources often have their own availability, such as a room shared with another department three afternoons a week. Both are ordinary constraints once the resource is modelled explicitly, and neither is solved by blocking time in a person calendar and hoping.

How To Test Your Own Setup

  1. Race two browsers. Open the same slot in two private windows and submit both within a second. Exactly one should confirm, and the other should get a clear message and a refreshed list rather than an error page.
  2. Test the hold. Select a slot in one window, confirm it disappears in the other, then abandon it and confirm it returns when the hold expires.
  3. Test the external conflict. Create an event in your connected calendar and time how long the slot takes to disappear. That is your sync lag, worth knowing.
  4. Test free versus busy. Create an all day event, leave it at the default status, and see whether it blocks. Most people are surprised.
  5. Test the resource path and the confirmation re-check. Book two services that share a room at the same time, then select a slot and create a conflicting calendar event before submitting the form. Both should be refused.

Run these after any change to availability rules, buffers or integrations, since a buffer change alters the shape of every block. Setting your availability rules covers how those blocks are constructed.

What Happens When One Gets Through

Have a procedure, because eventually you will need it. Decide in advance which booking is honoured and who makes the call. Contact the person who is moved directly rather than by automated email, offer a specific alternative slot rather than a link, and resolve any money in the same conversation. A conflict handled inside an hour with a phone call is a minor annoyance. The same conflict discovered by the client at your door is not.

How appntmnts.io Handles It

appntmnts.io holds a slot while a client completes the booking form, enforces uniqueness at the database level so a race cannot produce two bookings for one slot, and re-checks conflicts at confirmation against a fresh view of connected calendars. Two way sync with Google, Microsoft 365 and CalDAV means appointments entered directly in a calendar remove the matching slots.

The integrations page lists the supported calendars and how the busy check works, and pricing shows what the free plan includes. If your conflicts come from the sync layer rather than the booking flow, start with why calendar sync fails.

TA

The Appntmnts Team

Scheduling And Calendars, Appntmnts

Share

Put This Into Practice

Get Your Own Booking Page Free

Claim appntmnts.io/yourname, connect a calendar, and share one link. Unlimited bookings, no card at signup, no trial countdown.

Start Free


Free Forever, No Card

Turn This Into A Booking Page In Two Minutes

Claim your link at appntmnts.io/yourname, connect Google, Outlook, Apple iCloud or CalDAV, and let people book real time in your calendar. Unlimited bookings on the free plan, with the limits written out before you sign up.

What You Get At $0

  • A Personal Booking Page With Unlimited Bookings
  • Two-Way Sync With Google, Outlook, iCloud Or CalDAV
  • Automatic Confirmations, Reminders And Timezone Detection
  • Meet And Teams Links Plus The REST API
Compare All Plans