April 26, 2026 · 7 min read
How we estimate no-show risk for reservations without an ML team
RestoWebMaker scores each reservation's no-show risk and emails a weekly summary to the owner — without any ML pipeline. Here's how it actually works.
Independent restaurants get hammered by no-shows. A 6-top that doesn't materialize at 7pm on a Saturday isn't just a missed dinner — it's the worst dinner of the night, because the table sat empty during peak service and you turned away three walk-ins because it was on the book.
OpenTable and Resy try to solve this with deposits, credit-card holds, and overbooking. All three have real cost: deposits scare off the guests you most want, holds are a deliverability mess, and overbooking annoys the guests who do show up. RestoWebMaker takes a different approach: score every reservation, surface the risky ones to the owner, let the owner decide.
We do this with a Haiku-summarized risk model that has no machine learning team behind it — just a couple SQL queries and a small LLM prompt. Here's the architecture.
The signal we actually have
For every reservation, we know:
- Day of week and time of the booking
- Party size
- How far in advance the booking was made
- Whether the guest has a phone number on file
- Whether this guest has any prior reservation history (and if so, their show rate)
- Whether the booking came from the public site or was added by a host (host-added tends to be a more committed guest)
That's thin compared to what an ML team would love to have, but it's enough for a useful score because no-show risk is dominated by a handful of strong patterns. Friday/Saturday primetime large parties booked > 7 days out without a phone number have a no-show rate roughly 4× the average. Repeat guests who showed up for their last booking are roughly 0.6× the average.
The actual implementation
Once a week, a cron job runs against the last 90 days of completed reservations per restaurant and computes a simple frequency table: for each combination of (day-of-week × hour × party-size-bucket), what was the no-show rate? That table — call it the prior — is stored as JSON on the Restaurant.noShowRiskTable column. It's not ML, it's a histogram. But it's the right histogram for that restaurant.
When a new reservation comes in, the public-facing API doesn't do anything fancy — it just looks up the matching cell in the prior table and stores that as the booking's base risk. Then a couple of deterministic adjustments fire:
- Repeat guest with prior shows? Multiply by their personal rate.
- No phone number? Add a flat 8% to the score.
- Booked > 14 days out? Add 5%.
- Same-day or next-day booking from a real account? Subtract 10%.
That's it for the math. The result is clamped to [0%, 95%] and bucketed into Low / Medium / High.
Where Haiku comes in
The math gives a number; the owner needs context. So once a week, right after the prior table is recomputed, we feed Haiku a summary of the highest-risk patterns and ask for two or three sentences of plain-English explanation. The prompt looks roughly like:
You are summarizing reservation no-show patterns for a restaurant
owner. Here is the past 90 days of data, aggregated:
[table of day × time × party-size with show rate]
In 2-3 sentences, identify the 1-2 patterns that drive most of the
no-show risk for this owner. Be specific (call out the exact day
and time). No hedging, no generic advice.The result lands on Restaurant.noShowRiskSummary and appears on the dashboard as a small, useful sentence: "Saturday 7-9pm parties of 6+ skip about 22% of the time, mostly bookings made a week or more out without a phone on file." Owners read that and know where to focus follow-up calls.
What we're explicitly not doing
We don't train a per-restaurant model. The histogram approach is better than a model when you have small data per restaurant (which every indie does). Models will overfit; histograms degrade gracefully — if a cell has only one or two reservations, we fall back to the global cell average.
We don't use Sonnet for the summary. Haiku at claude-haiku-4-5is fast, cheap (~$0.00008 per summary), and entirely sufficient for 2-3 sentences of pattern description. Sonnet's extra capability wouldn't move the needle on a task this constrained.
We don't auto-cancel high-risk bookings. The score is a tool for the owner, not a policy. The fastest way to alienate the 78% of high-risk bookings that do show up is to treat them like suspects.
Cost and footprint
The whole subsystem is one weekly cron, two SQL aggregations, one Haiku call per restaurant, and roughly 200 lines of TypeScript. Total AI spend per restaurant per month: under one cent. Total dev time from spec to prod: about two days. The lift came from picking the right primitive — a histogram — not from picking a fancier algorithm.
If you're a restaurant operator and you want to try it on your own booking history, RestoWebMaker's reservations module includes the no-show summary on every plan that has reservations enabled. The math is the same; the savings show up on the third or fourth Saturday.
Try it yourself
Every template has a live demo with no signup. Personalize one for your own restaurant and see how it looks:
Browse 13 templates →