Sports, Fitness & Athletics// diagnostic

Full class, empty floor: where waitlist promotion silently fails

In short

A class that shows full and runs half empty is almost always a timing defect rather than a member-behaviour problem. Measure the interval between a cancellation and a promoted member being able to act on it: where that interval is shorter than travel time plus changing time, no promotion offered in that window can succeed, and the spot was lost the moment the cancellation arrived.

Key takeaways

  • A promotion is only real if the offer lands before travel time plus changing time runs out.
  • Instrument 4 timestamps per cancellation: cancelled, offered, delivered, deadline. The gaps name the fault.
  • An acceptance window that extends past the class start holds a spot nobody can use and re-offers it to nobody.
  • Measure notification delivery, not sends. A promotion loop over a channel members have muted is a lottery.
  • Held spots for staff, trials and guests need an owner and an automatic release time, or they are a permanent capacity cut.

The class is capped at 24, the app shows 24 booked with 9 on the waitlist, and 17 people are on the floor. Nothing about that is a mystery once you look at the clock rather than at the members. Somewhere between a cancellation at 17:52 and a class starting at 18:30 there is an interval in which a promoted member has to be told, decide, travel, change and arrive. When that interval is negative, the promotion loop still runs, still sends, still reports success, and still leaves the spot empty.

So this is a latency diagnosis, not a policy one. Whether to sell more spots than you have is a separate argument settled in hard cap or overbooking for class capacity. What follows assumes the cap is right and asks why the seats under it are not being filled.

The check: four timestamps per cancellation, for one week

Instrument the loop before touching it. Every cancellation on every class for 7 days, with 4 timestamps and 2 derived numbers.

  1. Cancellation accepted. When the member's cancellation was written, to the second, and by which route — app, web, front desk or a message to the coach.
  2. Promotion offer created. When the system decided who to offer it to. The gap between this and the first timestamp is your scheduler lag.
  3. Notification delivered. Not sent — delivered, from the push service or email provider receipt. If you cannot get delivery receipts, that is your first finding.
  4. Acceptance deadline. When the offer expired or was accepted, and by whom.
  5. Derive the actionable window: class start, minus your required lead time, minus the delivery timestamp. Negative means the offer was never usable.
  6. Derive the outcome: accepted and attended, accepted and absent, expired, or never offered at all.

One evening, five empty spots, one recoverable

CancelledOffer deliveredDeadlineActionable minutesOutcome
15:4015:4516:45+135Accepted, attended
17:5218:0119:01-1Expired after the class ended
18:0518:1619:16-16Expired after the class ended
18:22Never — next job ran at 18:30None-22No offer made
Never cancelledNo event firedNonen/aSilent no-show
An 18:30 class capped at 24, with a 30-minute required lead time and a 60-minute acceptance window

Five spots, 1 filled. Two were offered to real people at times those people could not possibly use, and the 60-minute acceptance window then held both spots until well after the class finished — so nobody further down the list was ever asked either. One arrived too late for the scheduler to see. One never generated an event at all.

An acceptance window that outlives the class is not generosity. It is a spot taken off the market at the exact moment it was most needed.

Six causes, ranked by how often they are the whole story

CauseSignature in the dataFix owner
Promotion runs on a scheduleOffer times cluster at fixed minutes past the hourWhoever owns the job scheduler
Acceptance window outlives the classDeadlines after class start; no second offer madeProduct owner, one config change
Notifications not deliveredSends far exceed delivery receipts, or none existMobile and messaging
Cancellation fires no eventFront-desk and coach-message cancellations missing entirelyWhoever owns the desk process
Held spots never releasedCapacity permanently below the nominal capNamed owner per hold type
Capacity counted on bookings onlyFull on paper while check-ins run well below capProduct owner
Match your instrumentation to the cause

Delivery, not sends — the number most clubs never look at

A promotion loop is a notification product. If the message does not land on a locked screen within seconds, everything upstream of it was wasted effort, and the send count in your dashboard will not tell you that it did not.

  • Count members with push actually enabled. Not installed — permitted. If that share is under roughly 60% of your active members, promotion is a lottery and no amount of scheduler tuning changes the odds.
  • Treat a promotion as time-critical on the platform. Ordinary notifications can be batched or held back by the operating system; a spot expiring in 12 minutes has to be sent on the channel that wakes the device.
  • Instrument the second channel. Email arrives in a promotions tab hours later. Text messages land reliably and cost money and irritate people. Pick per urgency, and measure both.
  • Design for one tap from the lock screen. If accepting requires opening the app and logging in again because the session expired, you have added 90 seconds to a window measured in minutes.
  • Assume the network is poor at exactly the wrong moment. Members are in basements, car parks and on trains — the same delivery reality that shapes streaming a match from a ground with one bar of signal.

Held spots and the capacity that was never really there

Before blaming the loop, count the seats. A class capped at 24 often has fewer than 24 sellable places because of holds nobody remembers creating: a coach's guest, 2 trial spots the sales desk asked for, a bike reserved for a member returning from injury, a place kept back for staff. Each is defensible. Together they can remove a fifth of the room permanently.

  • Give every hold an owner and an automatic release time. A hold with no release time is a permanent capacity cut wearing a temporary label.
  • Release trial holds on a schedule that matches how trials actually book, which is usually same-day and rarely a week ahead.
  • Derive capacity from the physical resource, not a number typed into a class record — the modelling problem solved in pitches, courts and lanes as bookable units.
  • Reconcile the nominal cap against sellable places weekly. Where they differ, the difference is the report, not an error to quietly correct.
  • Watch for classes cancelled late because no coach was assigned, which dumps a whole cohort into the promotion loop at once — the failure that publishing a coach rota that survives contact with the week exists to prevent.

The promotion policy, with an explicit cut-off

One policy, expressed against the clock rather than as a set of defaults. The cut-off is the part almost every configuration is missing.

  1. Set required lead time per class and venue from your own check-in data. Everything below is relative to it.
  2. Earlier than 2 hours before start: offer to 1 member at a time in list order, with a 30-minute acceptance window. Fairness is affordable while there is time.
  3. Between 2 hours and the lead-time boundary: shrink the acceptance window to the minutes remaining minus the lead time, and once that drops below about 20 minutes, offer to several members at once with first acceptance winning.
  4. Inside the lead-time boundary: stop offering. Flip the class to walk-in, show the true remaining count in the app, and let anyone already in the building take the place.
  5. At the start time: release every spot not checked in, and let the coach admit from the door. Bookings become check-ins here, and the 2 numbers were never the same measurement.
  6. Never let an acceptance window cross the class start. Expire it at the boundary and re-offer while re-offering is still worth something.
  7. Log every offer, delivery, acceptance and expiry with actor and timestamp, so next quarter's version of this question is a query rather than an argument.

Decision tree

  1. Offer times cluster at fixed minutes. Make promotion event-driven off the cancellation, not a polled job. This is usually a same-week change and it recovers the largest share of spots.
  2. Deadlines fall after the class starts. Cap the acceptance window at the lead-time boundary. One configuration change, and it is the cheapest fix on this list.
  3. Sends look healthy and delivery receipts do not exist. Fix measurement before behaviour; you cannot tune a loop whose output you cannot see.
  4. Front-desk and coach-message cancellations are missing. The event is not firing because the cancellation never entered the system. That is a desk process with a software gap behind it.
  5. Check-ins run consistently below a cap that is always full on paper. You are counting bookings as capacity. Start counting attendance, then decide separately whether to sell against the difference.
  6. Everything above is clean and classes still run short. The remaining gap is genuine no-shows without cancellation, which is a policy question rather than a timing one.

Most of this is a fortnight of work on the loop you already have: an event on cancellation, a bounded window, delivery receipts and a cut-off. Where the booking layer itself cannot express any of that, it becomes a small product build around the existing system rather than a replacement of it. The rest of the silo — capacity, registers, allocation — sits under scheduling, attendance and the facility, within our sports, fitness and athletics practice.

Frequently asked questions

Short answers to the follow-ups this page tends to raise.

Why does a gym class waitlist not release spots when people cancel?

Usually it does release them, but too late to be usable. The offer is created by a scheduled job rather than by the cancellation event, the notification takes another minute to land, and by then the promoted member cannot travel and change before the class starts. Measure the minutes between cancellation and delivered offer against class start minus travel and changing time; where that number is negative, the loop is working exactly as configured and the spot was never recoverable.

How long should a waitlist acceptance window be?

Long enough to be answered, and never long enough to cross the class start. Two hours out, 30 minutes is reasonable. Thirty minutes out, the window should be a few minutes and the offer should go to several members at once. A fixed 60-minute window applied close to the start holds the spot past the point of use and prevents anyone further down the list from ever being asked.

Should a late cancellation still free up the spot?

Yes, but stop trying to promote into it once the offer can no longer be acted on. Inside your required lead time, release the place to walk-ins and show the real remaining count in the app instead of continuing to notify people who cannot arrive in time. Whether the late-cancelling member is charged is a separate policy question and should not be tangled with the release mechanism.

How do we tell a promotion timing problem from members simply not turning up?

Split the empty places by whether a cancellation event ever fired. Places vacated by a cancellation with a negative actionable window are a timing defect you can fix in software. Places where nobody cancelled at all are no-shows, and no promotion loop can help because the system had no idea the seat was free. Clubs routinely find both and spend all their effort on the wrong one.

  • waitlists
  • class booking
  • notifications
  • capacity
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

Working on something in this space?

Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.

Start the conversation