Waitlist
Join the queue, honest position quotes, the five-minute table offer, acceptance, expiry and place-keeping — for staff and guests.
The waitlist is a same-day queue for parties without a reservation while the venue’s service is running. It has two sides that share one rulebook: a party joins the queue and gets an honest, explicitly approximate estimate; the host offers a real table by putting a five-minute hold on it through the same allocator that confirmed bookings use. A queue entry never occupies a table — only an actual offer does.
Who can join, and the guards
Joining records the party’s name (at most 160 characters), party size (1–200) and one contact channel — an email address or an E.164 phone number; a party with neither is refused (“A queue party needs an email or a phone number.”). The command is replay-safe: submitting the same request key twice returns the same entry instead of queueing twice. The guards, in the order the server checks them:
| Guard | Refusal when it fails |
|---|---|
| Public joins only reach published venues | plain not-found — an unpublished venue does not exist to the public |
| The venue’s queue is switched on (a venue setting — see Venues, slugs & publication) | RULE_VIOLATION |
| Party size at most the venue’s queue cap (default 8) | VALIDATION: “The queue accepts parties of at most {n}.” |
| A service window is running right now | RULE_VIOLATION — the queue is same-day and live-service only |
| One active entry per contact per venue-day | WAITLIST_DUPLICATE: “This contact is already in the queue for today.” |
A duplicate is not an error to fight: the existing entry is where the party already stands. Leaving and rejoining, or a staff correction, is the path when details were wrong.
The position quote
The join answer reports position and wait as one honest, labelled estimate, for example: “position 3 (2 ahead), quoted ~50 min (approx)”. The math is exact about what it counts:
- The position counts only earlier same-day parties still active in the queue (waiting or holding an offer) whose party size could take the same kind of table as yours — a couple queueing behind a party of ten is not counted as blocking that couple.
- The wait estimate is that count plus one, times the turn length of the service window running at that moment.
- The answer always carries the “approx” marker, and the queueing email states the position once, at join time: “Queue position when you joined: {n}”. Nothing later re-promises it — the table-ready email is the moment that matters.
The queue token
Joining produces a one-time queue token, shown once: a 43-character secret
that dies at the venue-local midnight after the business date (a clock-shift
night never shortens it below a minute of validity). Exchanging the token
once opens a 60-minute session scoped to that one queue entry. Queue tokens
and booking links are separate credential spaces on purpose: a booking link
does not open a queue entry and a queue token does not open a booking —
both answer the same generic TOKEN_INVALID when a secret is wrong, used,
expired or replaced, with nothing revealed about which.
Pilot reality: this pilot has no public queue page for guests. The join command itself is public, but the venue’s team is who puts parties in the queue at the desk, and the queue token changes hands in person at the same moment. Guests hear from the venue by email; the emails are described in Messages, confirmations & reminders, and during the unpaid pilot they are recorded rather than sent because no email provider is connected.
Staff: offering a table
Offering is a host-and-above floor act. The host picks a waiting entry and
one to four target tables (a single table the party fits, or an approved
combination — the same target rules as a table move), and the system does
the rest: it finds the next usable slot for the party, resolves the
governing accepted terms, and creates exactly one five-minute hold — a
real HELD reservation on the real tables through the shared allocator. One
live offer per entry, one offer per reservation, ever. The hold carries the
guest-facing email “Your table is ready — {venue}”, which says: “Please
confirm within 5 minutes — the offer expires at {venue-local stamp}.” and
“Confirm through your queue link so we can seat you.”
Refusals are honest: a deposit-backed service cannot be held for free during
the pilot (PAID_BOOKING_UNAVAILABLE — see
Payments (pilot posture)); a table already planned
or physically occupied answers CONFLICT / OCCUPANCY_CONFLICT with the
current placement named; a stale picture of the entry answers
WAITLIST_STALE — reload the queue and retry with the new version; an
entry that is not waiting (or not today’s) answers WAITLIST_OFFER_CONFLICT
or RULE_VIOLATION.
Acceptance: five minutes
Accepting converts the same held record from HELD to CONFIRMED — no
second booking is ever created, and the tables stay exactly the ones
offered. The party’s own queue session can accept, and staff can accept on
their behalf at the desk; acceptance is replay-safe per request key. Letting
the window lapse is not a punishment: see below.
Expiry, withdrawal and place-keeping
Before every offer and every acceptance the system runs an explicit sweep of
lapsed offers in its own committed transaction. An expired offer releases
its tables, expires its held reservation with the reason
WAITLIST_OFFER_EXPIRED, and returns the entry to waiting keeping its
original place — the join timestamp is never touched, so an expired offer
costs a party their five minutes, not their position. A host withdrawing a
live offer does the same: hold released, entry back to waiting, place kept.
The guest-visible answers are WAITLIST_OFFER_EXPIRED — “The offer expired
and its table was released.” — and WAITLIST_OFFER_CONFLICT — “This offer
is no longer open.” — for an offer that was already accepted or withdrawn.
Staff can run the sweep by hand (“Sweep expired offers”, replay-safe); an
empty sweep is a normal answer.
| Code | Meaning | What to do |
|---|---|---|
WAITLIST_DUPLICATE |
this contact already holds an active entry today | wait for the existing entry, or leave the queue first |
WAITLIST_STALE |
the entry changed since your read | reload the queue and retry with the current version |
WAITLIST_OFFER_CONFLICT |
the offer is no longer open | reload: accepted means seated-bound; withdrawn means back in the queue with place kept |
WAITLIST_OFFER_EXPIRED |
the five minutes passed | nothing lost — the place was kept; watch for the next offer |
RULE_VIOLATION |
the queue switch, service window or day forbids it | read the situation; join again when service is running |
PAID_BOOKING_UNAVAILABLE |
the service requires a deposit | offer a free-service table instead |
TOKEN_INVALID |
the queue token is unknown, used, expired or replaced | ask the venue for a fresh one |
The full code catalogue is in Error codes & fixes; the floor side of seating an accepted offer is in Arrivals, seating & visits.