The cancellation fill engine: put someone in the slot that just opened
Someone cancels at short notice and the slot stays empty, though others would have taken it. People who could not get a slot leave their name and the times that suit them. When a booking drops out, the freed slot is offered to them by itself.
A waiting list that offers the freed slot by itself, with no ring-around and no double-booking
Paste it into your AI assistant — ChatGPT, Claude, Gemini, whichever you use. It first asks what tools you already have, then rebuilds this blueprint to fit them.
- A booking tool that fires webhooks and has a write API — Easy!Appointments and Cal.diy both qualify, a paper diary does not
- n8n reachable from the internet, so the booking tool's webhook and the claim link can both arrive
- Real waiting-list data: the days and hours each person can actually come, not 'any time'
- A mailbox or SMTP relay that can send as your domain, with SPF and DKIM set, or the offers land in spam
- SMS sender registration if you use SMS: A2P 10DLC in the US, an approved sender ID in parts of the EU
Version history
Reminders stop the no-show. They do nothing for the client who cancels honestly at 08:40 for a 09:00. That slot is a separate problem with a separate fix, and every booking platform sells the fix as a paid extra.
The architecture
the same flow as text
├─ who, which service, which provider, which days and hours
└─ stored in NocoDB, visible as a plain table
[ Booking tool ] Easy!Appointments or Cal.diy
│ webhook fires when an appointment is saved or deleted
▼
[ n8n — the fill engine ]
├─ 1. did a slot actually free up? compare to the last known state
├─ 2. match on service + provider + the person's day/hour window
├─ 3. drop anyone already offered a slot this week
├─ 4. offer to ONE person, with a single-use claim link and a timer
├─ 5. timer runs out → next person on the list
└─ 6. link tapped → book through the booking API → kill every other offer
The engine never holds the calendar. It reads the booking tool and writes back
through the API. One writer, no split brain.
Two containers you probably already run, plus one table. The design decision worth defending: the waiting list lives outside the booking tool. A waiting-list entry is intent, not a booking. It has its own lifecycle, it sits there for weeks, and you need to be able to delete it without touching anyone's calendar. Bolt it into the booking tool as a fake appointment and you will spend a year explaining ghost entries to your staff.
Paste it into your AI assistant — ChatGPT, Claude, Gemini, whichever you use. It first asks what tools you already have, then rebuilds this blueprint to fit them.
The problem
Start with the number, not the tool. Count your short-notice cancellations for one month. Six a week at a €55 average ticket is roughly €1,430 a month of empty chair. Fill half of them and you have recovered around €715 a month for an afternoon of setup. If your count is one a fortnight, stop reading and go build something else. That is the whole business case, and it is your number, not mine.
The manual version of this is the ring-around: your receptionist works down a list of people who once said "call me if anything opens up", makes six calls, gets two voicemails, and the slot is 40 minutes closer by the time anyone answers. Everyone selling salon and clinic software knows this, which is why waitlist automation sits behind the mid or top tier on every platform. Their marketing pages claim 60–70 % of cancellations get recovered. Treat that as marketing, because none of it is independently measured. Measure your own fill rate after four weeks and believe that number instead.
This blueprint is the other half of the no-show reminder engine. That one prevents the loss. This one recovers it. They run on the same box and share the same booking tool.
The one real decision: how you offer the slot
There are two mechanics and people argue about them as if it were taste. It is not, it is arithmetic on the clock.
Broadcast. Message everyone who matches, first to claim wins. Fastest fill. The cost is that everyone else gets "sorry, gone", and after the third one they stop opening your messages. Your list dies quietly.
Ladder. Offer to the single best match, give them a window, then move to the next. Nobody is disappointed, but it needs time you may not have.
The rule: let the time-to-slot decide. More than 12 hours out, run the ladder with a 60–90 minute window. Between 3 and 12 hours, ladder with a 20-minute window. Under 3 hours, broadcast, because no ladder finishes in time. Either way the claim link is single-use and exactly one workflow writes the booking. Two people tapping in the same second is not a freak event when you broadcast to thirty people.
Tool choices — and why
Save (create or update) and Delete. There is no "cancelled" event. A cancellation reaches you as a Delete if the record was removed, or as a Save if someone flipped a status field. So your first n8n node cannot trust an event name — it has to hold the last known state of that appointment and compare. Second catch, reported in 2017 and still true: creating an appointment over the API needs a customerId, so the workflow looks the customer up by email first and creates them only if they are new. Build both of these on day one, not after the first double-booking.Setup outline
- Build the table first. Columns: name, contact, service, preferred provider, days available, hours available, "how far can you travel at short notice", added-on date, last-offered date, status. Working when: your receptionist can add someone in under 20 seconds.
- Publish the NocoDB form view as your public sign-up, and link it from the booking page for the case "no slots available". Working when: a test submission lands as a row.
- Point the booking tool's webhook at an n8n webhook URL, with the secret header set. Working when: booking a test appointment produces a run in n8n.
- Write the freed-slot detector: store the last known state per appointment, compare, and only continue when a slot actually opened. Working when: a normal edit produces no offer and a cancellation produces one.
- Write the matcher: service, provider, day and hour window, minus anyone offered a slot in the last seven days. Rank oldest-on-the-list first. Working when: a cancelled Tuesday 09:00 colour returns only people who said Tuesday mornings and colour.
- Build the offer step with a single-use token per offer, an expiry timestamp, and a claim webhook that checks-and-sets that one row before it books anything. Working when: tapping the same link twice books once and shows "already taken" the second time.
- Wire the ladder timers per the rule above, and the broadcast branch for slots under three hours away. Working when: nobody claiming inside the window rolls to the next person by itself.
- Run it on your own diary for a week before you point it at real clients. Then measure the fill rate for four weeks and write the number down.
Pitfalls — what goes wrong when you build this
Verified repos
Easy!Appointments — GPL-3.0, 4,197 stars, last push 27/05/2026
n8n — Sustainable Use License (free self-hosted internal use), 188,877 stars, last push 20/05/2026
NocoDB — Sustainable Use License, 63k stars, release 2026.05.0 on 08/05/2026