Work out what a first version of cancellation recovery should be for Everwood: how a newly open appointment reaches someone who wants it, without dark patterns, indefinite holds, or a race that leaves most of the people who were told disappointed.
I treated the cancellation trigger as the free part and the notification as the feature. Reading our own booking flow next to the research reversed that. Cancellation reaches the same terminal state through four different doors that do not behave the same way, so the moment an offer would hang on does not exist yet.
An offer is a promise about something you do not control. The event behind it and the hold in front of it decide whether it is true when someone opens it, and neither of those is the notification.
The Build
Nothing shipped from this research. There is no waitlist in Everwood, no recovery flow, and no code written toward one. The roadmap node this came from is still marked idea. What changed is what I think a first version is, and it is not the part I went in expecting to design.
The customer problem is easy to picture. A salon has a two o'clock appointment on Thursday. At nine in the morning the customer cancels. The chair is now empty, it will stay empty, and somewhere there is a person who wanted exactly that time and will never hear that it opened. Recovery today means a person with a phone and a list, working through voicemails while the hours tick past.
The research I started from proposed the obvious shape. Watch for a cancellation, match the open time against people who wanted it, tell them, let one of them claim it. I agree with that shape. I now think almost all of the work sits in two places nobody talks about, and almost none of it is in the notification.
The Problem
The matching is the easy part, and I say that after checking rather than assuming it. We know how long each service takes. We know which staff member can perform it. We know how much notice a business wants before a new booking, because that is already a setting a business controls, and it already defaults to an hour. Pointing that at a newly open hour and getting a list of people who fit is ordinary work.
The two hard parts sit on either side of the match. One is the event behind the offer. The other is the gap between sending an offer and someone claiming it.
Start with the event, because it is the one I got wrong.
Cancelled is a real state for us and a well behaved one. It is terminal, which means nothing moves out of it, and the shared cancellation path only acts on an appointment that is still pending or confirmed. Press cancel twice and the second press changes nothing. That is a solid foundation, and I had assumed it was the whole foundation.
It is not, because a cancellation can arrive through several different doors. A customer can cancel by talking to our booking assistant, once they have proved the appointment is theirs. A business can cancel from its own dashboard. A staff member can decline a request that was waiting for them. We can cancel from our internal tooling. Every one of those ends in the same terminal state.
They do not behave the same way on the road there. Cancel through the assistant and the customer gets an email telling them the appointment is off. Cancel from the dashboard and the customer gets nothing at all.
So the product has a status it can trust and an event it does not have. If I ask our system who is cancelled, I get a clean answer. If I ask when a slot became sellable, there is no single place that knows, and no single moment I could hang an offer on. Every description of this feature, ours included, opens with a phrase like when a durable cancellation occurs, as though that were the free part.
Assumption vs Reality
The trigger is already built, because cancelled is already a status, and the interesting design questions are about matching and messaging. The dangerous failure is double booking: two people get an alert, both tap it, both end up in the same chair on Thursday afternoon.
Both were wrong, in opposite directions. A status is a fact about the world right now. An event is a claim that something changed at a particular moment. A waitlist runs on the second one, and we only have the first. That gap is not a small piece of plumbing to add at the end. It is the feature. The double booking fear turned out to be the one thing I did not need to worry about. Our booking system refuses to put two appointments on the same staff member at the same time, and it refuses at the database rather than in application code. Two claims racing each other do not produce two appointments. The loser is rejected by storage, and in a group booking the whole group rolls back rather than landing half written. We paid for that guarantee a long time ago. So the risk is not a corrupted calendar. The risk is nine emails.
The Fix
What changed is the design, not the code. Three things I now believe.
Build the event before building the audience. One recorded moment, produced wherever a cancellation actually happens, that means this time is now available and here is what it fits. It is unglamorous, it is the least demo friendly part of the feature, and everything else is decoration on top of it. Doing it also forces a question worth answering on its own: our two main cancellation paths treat the customer differently today, and only one of them says anything. That inconsistency is invisible while nothing depends on it. A waitlist depends on it.
Safety and fairness are different problems, and we have only solved the first. Refusing to double book protects the calendar. It does nothing for the person who opened an email, tapped through, and found the appointment gone. That person did nothing wrong and still lost, and all our current guarantee gives them is a clean error message.
Square is instructive here, because they have run this at scale. Their waitlist notifies every client whose stated availability matches when a cancellation or reschedule happens. Then, on top of that, they will not tell the same customer about a new opening if they already told them within the past six hours, and they suppress openings that fall inside the notice period a business requires. Both of those are guards bolted onto a broadcast. You do not build a six hour quiet period unless people were being messaged more often than they could stand.
So offer to fewer people rather than faster. One person at a time, a real hold on the slot while they decide, and a short window before it moves on. That is slower on paper than a blast. It also means every offer we send is true when it arrives, which is the only version of speed the customer experiences.
Here is the strongest argument against everything I just said. A blast to everyone who wants that time will fill the slot faster than a queue will, some of the time it will fill a slot a queue would have left empty, and the owner watching an empty chair does not care that the eight people who missed out felt bad about it. That objection is real, and on the first Thursday it is correct.
I am choosing the slower design anyway, because the two options are only equal for one cancellation. The blast spends something every time it runs, and what it spends is the credibility of the next message. A queue spends time, and time is the part we can get back by tuning the window.
That reasoning rests on a hypothesis, not a finding, and I would rather name it than bury it. My belief is that a customer who loses a race twice stops opening the emails, and that a waitlist people ignore is worse than no waitlist. There is no Everwood data behind that. What would test it is offer acceptance over time, measured against how many offers each person has already lost. If acceptance holds up for repeat losers, the blast is the better design and I should drop this argument.
The channel question answers itself. We do not send text messages anywhere in the product today, so email and the customer's own account are what we have. Text also carries consent obligations and carrier registration work we have not taken on, and I would rather not take it on for a feature nobody has validated. Worth noticing too: one of the platforms with the most automated waitlist puts it behind their higher plans and requires text messaging to use it. That is a fair trade for them. It is not a free feature for anyone.
An offer is a promise about something you do not control. Everything else follows from that sentence. The promise is only as good as the event behind it, which is why the trigger matters more than the matching. It is only as good as the state of the world when the reader opens it, which is why a hold matters more than delivery speed. And it goes to more people than can possibly be satisfied, which is why the design question is not who gets the slot but what happens to everyone who does not. Before sending anyone a message about state, ask three things. What event produced this. Is it still true when they open it. What happens to the people who act on it and lose.
For a salon owner, this is about the empty chair on Thursday afternoon. Today, if a customer cancels, Everwood has nothing that helps you fill it. No waitlist, no alerts, no list of people who wanted that time. You find out by looking at your calendar, and refilling it means picking up the phone. I would rather say that plainly than describe a feature we have not built. What I am designing toward is narrower than what most scheduling software advertises. The common version texts everyone who wanted that day, and the fastest tap wins. It fills the slot. It also means your regulars get told about an appointment they cannot have, and the ones who lose learn to ignore the next message. You end up with a tool that works well the first month and gets quieter every month after. The version I want offers the slot to one person, holds it while they decide, and moves on if they do not. Fewer messages, each one true. One thing you should know about how cancellations work now, because I found it while reading our own system. If you cancel an appointment from your dashboard, we do not send your customer an email about it. If the cancellation happens through the booking assistant, we do. Nobody has reported this, and it may well be the right behaviour, since you probably cancelled after speaking to them. I am flagging it because the moment a waitlist exists, the difference between those two paths stops being a detail.
Builder Notes
A research pass on cancellation recovery that turned into an audit of what a cancellation actually is inside our product. Nothing was built. No waitlist work has started.
Technical Note 1
Shipped today toward this: nothing. The roadmap node is an idea, and there is no waitlist anywhere in the product.
Technical Note 2
Cancellation is terminal and guarded at the source. The shared cancellation path only acts on an appointment that is still pending or confirmed, so a duplicate cancel cannot fire recovery twice. That is idempotency we get for free, and it is the piece most designs bolt on afterward.
Technical Note 3
Cancellation is reachable from four surfaces: the customer through the booking assistant after verifying the appointment is theirs, the business from its dashboard, a staff member declining a pending request, and our own internal tooling. All four land on the same terminal state. Only the assistant path emails the customer.
Technical Note 4
Double booking is prevented by the database, not by application checks alone. Our own check runs first, and behind it sits a constraint that rejects any overlapping appointment for the same staff member. A concurrent booking that races past the application check is refused by storage, and a group booking that hits it rolls back as a whole rather than landing partly written.
Technical Note 5
We have already met the failure this feature invites, in a different part of the product. A read decides something is still active, a write assumes it, and a state change commits in between. The fix there was to reacquire the row under a lock at the moment of writing, and to repeat the state condition in the write itself. A slot offer is the same shape and can borrow the same answer.
Technical Note 6
Minimum notice is already a business setting with an hour as its default, so the rule that suppresses a same day offer exists before the feature that needs it does.
Technical Note 7
Square, from their own documentation: everyone on the waitlist whose stated availability matches is notified when a cancellation or reschedule happens, a customer is not told about a new opening if they were told within the past six hours, openings inside the required notice window do not generate offers, and a client can register up to five availability preferences.
Technical Note 8
Acuity, from their own documentation: the waitlist alerts clients by text when a preferred time opens after a cancellation or reschedule, and the client books it themselves from a link. It is limited to their higher plans, to one to one appointments, and to accounts with text messaging enabled.
Technical Note 9
Customers already have accounts and can see their bookings there, which makes the account a real second channel. What they cannot do from it is cancel, so a self service cancellation goes through the assistant.
Technical Note 10
Cut from the research and recorded in the ledger rather than the note: every industry figure for revenue lost to cancellations, and the competitor table covering six other platforms. The figures had no traceable source. The competitor rows were claims about other people's software with nothing primary behind them, so they did not survive the evidence gate. Two are interesting enough to check properly later, in particular the platform that reportedly sends the alert to staff instead of the customer.
Technical Note 11
Open and undecided: a cancelled group booking frees several slots at once, and whether those are offered as one block or split apart changes both the matching and the fairness.
Before / After
- A waitlist is a matching and notification feature
- The cancellation trigger is free, because cancelled is already a status
- The dangerous failure is two people claiming the same slot
- Speed of the offer decides whether the slot refills
- The design question is who gets the slot
- A waitlist is a promise keeping feature wearing a notification costume
- The trigger is the feature, and we do not have it yet
- The dangerous failure is telling nine people about an appointment one of them can have
- Truth of the offer decides whether anyone believes the next one
- The design question is what happens to everyone who does not get the slot
What I'd Do Differently
I would have looked at where cancellations come from in our own product before reading how anyone else notifies people about them.
I spent the first stretch of this research comparing notification models, which was the wrong end of the problem, and I found the real one by asking a duller question: what exactly happens, in our system, when someone cancels. The answer was four doors, two behaviours, and no shared moment.
The general version is worth keeping. Any time a feature is described as when this happens, do that, find out how many places in your product can make that happen, and whether they agree with each other. If they do not, the feature is not the part after the comma.