Skip to main content
Reconciliation‑first blueprint for shared waitlists & capacity management: prioritized features, data models, payment flows and automation

Reconciliation‑first blueprint for shared waitlists & capacity management: prioritized features, data models, payment flows and automation

A technical brief for the people who actually have to close the books when one waitlist feeds three studios

If you run more than one location and share a waitlist across them, money moves in ways your accounting software was never designed to handle. A student books into a Tuesday 6pm slot technically "owned" by your Eastside studio, gets bumped off the waitlist into a Downtown class because someone cancelled, pays with a pack purchased at a third location, and tips the instructor who's a 1099 contractor floating between sites. Now figure out which location recognizes that revenue, who owes who, and whether your Stripe payout actually matches your books.

Most studios sort it out with a spreadsheet and a monthly headache. This brief is about designing — or selecting — a system where that reconciliation mostly doesn't happen, because the data was structured correctly the first time.

This is written for whoever is making the build-vs-buy call: an owner evaluating platforms, an ops lead configuring one, or an engineer wiring the integration. If you've already read our piece on multi-location scheduling chaos and shared waitlists, this is the financial plumbing underneath that same problem.

The core problem, stated precisely

A shared waitlist decouples where a booking originated from where the service was delivered from where the payment was collected. Those three things used to be the same location. Now they aren't. Every reconciliation nightmare downstream traces back to that decoupling.

The question any capacity-management system has to answer cleanly is: for a given dollar, which location earned it, which location collected it, and does the settlement between them happen automatically?

If the system can't answer that at the record level, you will reconcile by hand forever. The features below are ordered by how much manual reconciliation they eliminate — not by how impressive they sound in a demo.

The reconciliation‑reducing feature checklist, in impact order

Work down this list. Each feature assumes the one above it exists.

  1. Unique, stable identifiers on every entity — bookings, payments, allocations, students, locations, class instances. No reconciliation is possible if you can't join a payment back to the exact booking that caused it. This sounds obvious and is the single most common thing platforms get wrong (they reuse human-readable codes that collide across locations).
  2. An append‑only ledger. Never update financial records in place. A booking moving from Eastside to Downtown should create a new event, not overwrite the old one. You need the full history to explain a number three months later.
  3. An allocation rules engine. This is where you encode "waitlist bumps credit the delivering location" or "pack revenue is recognized by the studio where the class was taken, not where the pack was sold." Rules live in config, not in someone's head.
  4. An auto‑matching engine. Correlates incoming payment settlements (from Stripe, Square, whatever) back to bookings and allocations using correlation keys, within tolerance thresholds. This is what turns a 3-hour month-end into a 20-minute exception review.
  5. Scheduled settlement remittances. If locations are separate legal entities or separate merchant accounts, inter-location balances need to actually move — automated, on a cadence, with a record.
  6. GL mapping. Each allocation type maps to a general-ledger account so your export lands in QuickBooks or Xero already categorized, not as a lump your bookkeeper has to re-sort.

The pattern here is consistent: identifiers and the ledger are foundational. If a platform you're evaluating is weak on items 1 and 2, nothing above item 3 will save you.

The data model you actually need

Keep the model boring and explicit. Four core record types cover most studio scenarios.

Booking record (minimal)

``json { "bookingid": "bkg9f2a71", "studentid": "stu4410", "classinstanceid": "clseastside202506101800", "originlocationid": "loceastside", "deliverylocationid": "locdowntown", "status": "attended", "source": "waitlistpromotion", "createdat": "2025-06-08T14:22:10Z", "settledat": "2025-06-10T19:05:00Z" } ``

The two fields that make this a shared-waitlist model rather than a normal booking model are originlocationid and deliverylocationid. When they differ, you have an inter-location event. Everything else is standard.

Payment record (minimal)

``json { "paymentid": "paybc3d12", "bookingid": "bkg9f2a71", "amountgross": 2200, "currency": "usd", "method": "packredemption", "packid": "pack7781", "collectinglocationid": "loceastside", "processorfee": 0, "tipamount": 400, "tiprecipientid": "inst221", "captured_at": "2025-06-10T19:05:00Z" } ``

Amounts in minor units (cents) — don't store money as floats, you'll chase rounding ghosts for the rest of your life. Note method: "pack_redemption" carries zero processor fee because the money was collected weeks earlier when the pack was sold. That timing mismatch is a major source of reconciliation confusion, and the model has to represent it honestly.

Allocation record (the important one)

``json { "allocationid": "alc55e9", "bookingid": "bkg9f2a71", "paymentid": "paybc3d12", "earninglocationid": "locdowntown", "collectinglocationid": "loceastside", "revenuerecognized": 1800, "tippassthrough": 400, "ruleapplied": "deliverlocationearnsv3", "glaccount": "4100classrevenue", "interlocationowed": 1800, "createdat": "2025-06-10T19:06:00Z" } ``

This record says: Downtown earned $18 of class revenue, Eastside collected it, so Eastside owes Downtown $18, and $4 of tips passes through to the instructor. The rule_applied field is your audit trail — you can always answer "why did it split this way?"

Settlement / remittance record

Generated on a schedule by netting all interlocationowed amounts between each location pair.

Webhook events to emit

Your capacity system should emit events, not force everything through polling. At minimum:

  1. booking.created
  2. booking.promotedfromwaitlist — fires when origin ≠ delivery, the key inter-location trigger
  3. booking.attended
  4. payment.captured
  5. payment.refunded
  6. allocation.created
  7. allocation.adjusted
  8. settlement.batch_generated

A sample booking.promotedfromwaitlist payload:

``json { "event": "booking.promotedfromwaitlist", "bookingid": "bkg9f2a71", "originlocationid": "loceastside", "deliverylocationid": "locdowntown", "promotedat": "2025-06-09T08:00:00Z", "idempotencykey": "promobkg9f2a71_v1" } ``

The idempotency_key matters more than it looks — waitlist systems retry, and you do not want a double-promotion creating two allocations.

Payment flows: central vs per‑location merchant

This decision determines roughly 80% of your reconciliation workload. There's no universally right answer, but the tradeoffs are sharp.

FactorCentral merchant accountPer‑location merchant accounts
Who collects the moneyOne entityEach location's own entity
Inter-location settlementInternal ledger only (no cash movement)Requires real remittances between accounts
Reconciliation complexityLower day-to-dayHigher — must net and transfer
Tax / legal separationWeak (one entity)Clean per-entity books
Processor fee attributionMust be allocated outNaturally lands per location
Best forSingle legal entity, multiple "brands"/sitesFranchises, partnerships, separate LLCs

Central is far easier to reconcile because money never actually moves between locations — you only track who earned it on an internal ledger. The catch is that if your locations are separate legal entities, a single merchant account creates a tax and liability mess that your accountant will hate.

Per-location keeps each entity's books clean but forces you to actually settle balances. That's where scheduled remittances and netting become non-optional.

Capture timing

  1. Capture at booking

    cleanest for cash flow, worst for no-show and cancellation churn. Every cancel becomes a refund event.

  2. Capture at attendance

    cleaner revenue recognition, but you're holding authorizations or billing after the fact.

  3. Pack/pre-paid

    cash captured long ago; the class event only triggers recognition and allocation, no new capture.

For shared waitlists, pack redemption is the trickiest because the collecting location (where the pack was sold) and the earning location (where the class happened) are almost always different.

Split payments, fees, and tips

  1. Processor fees should be allocated to the collecting location by default — that's the entity whose merchant account absorbed the fee. Spreading fees across earning locations is technically defensible but creates a reconciliation headache with no real upside.
  2. Tips should pass through directly to the instructor's payout record and be flagged as non-revenue. Studios constantly mis-book tips as revenue and overstate earnings as a result.
  3. Split payments (part pack, part card) create two payment records against one booking. Your allocation engine sums them; your matching engine has to handle partial settlements.

Three sources of pain, three handling rules:

Reconciliation automation techniques that actually work

The goal isn't to reconcile faster. It's to only touch the records that don't reconcile themselves.

Correlation keys. Every payment carries the booking_id. Every processor settlement line gets matched back via a key you control. If your processor strips metadata, you're reduced to matching on amount plus timestamp — which is fragile. Insist on metadata passthrough during setup.

Tolerance thresholds. Don't flag a $0.01 variance for human review. Set a threshold (somewhere under $0.50 or 0.5% of the transaction) where the system auto-matches and books the difference to a rounding account. Review only what exceeds it.

Batching and netting. Instead of 40 tiny transfers between Eastside and Downtown, net them to one daily or weekly figure. If Eastside owes Downtown $220 and Downtown owes Eastside $185 on the same day, one $35 transfer settles both.

Idempotency and retries. Every financial event needs an idempotency key. Webhook deliveries fail and retry constantly. Without idempotency, a single retried payment.captured double-books revenue and your month-end won't tie out.

The end-to-end flow in order:

  1. Student is promoted off the shared waitlist → booking.promotedfromwaitlist fires.
  2. Class is attended → booking status flips, allocation.created runs the rules engine.
  3. Pack redemption or card capture produces a payment record.
  4. Processor settles the batch → auto-matching engine correlates settlement lines to payments via booking_id.
  5. Matched within tolerance → auto-cleared. Outside tolerance → exception queue.
  6. Nightly, netting job computes inter-location balances → settlement.batch_generated.
  7. GL-mapped export pushes to accounting, pre-categorized.

Insist on metadata passthrough from your processor so settlement lines retain booking_id for reliable auto-matching.

Only step 5's exception queue needs a human. That's the whole point.

A diagram of the end-to-end flow can help align engineering, ops, and accounting teams.

Process diagram

The end-to-end flow above shows how automation reduces the set of records needing manual review to a tiny exception queue.

Auditability and governance

Append-only isn't just good hygiene — it's what lets you answer a partner's "why did I only get paid $1,800 on that class?" without reverse-engineering anything. Every allocation references the rule version that produced it. When you change a revenue-split rule, you version it (deliverlocationearns_v3), you don't edit the old one, and historical allocations keep pointing at the version that was live when they were created.

  1. Immutable event log with timestamps and actor IDs
  2. Versioned allocation rules, never edited in place
  3. Role-based access

    who can change split rules vs. who can only view

  4. Every adjustment is a new signed record, not an overwrite
  5. Exportable audit trail per location, per period

This matters most when locations are owned by different partners. Trust between co-owners erodes fast when the numbers can't be explained.

Edge cases you will absolutely hit

These aren't hypothetical — they're the recurring failure modes in shared-capacity setups.

  1. Cross-location refund. Student paid at Eastside, took the class at Downtown, then refunds. The refund has to reverse both the collection at Eastside and the earned allocation at Downtown. Reversing only one side is the classic bug.
  2. Mid-month rule change. Bookings before and after the change must settle under different rules. Rule versioning handles this; a single mutable config does not.
  3. Instructor floating across sites with tips. Tip collected at the collecting location, instructor paid centrally. Tip passthrough must not touch location revenue.
  4. No-show on a pack. Pack was already paid, no class delivered. Does the credit burn or return? That's a policy decision — but the system has to record which decision was applied.
  5. Partial pack + card, then refund. The refund must apply proportionally across both payment records. Decide the order of operations and encode it.

These edge cases are recurring and must be modeled explicitly to avoid disputes.

When a lightweight approach is fine (and when it isn't)

You can skip most of this if: you're a single legal entity, two locations, under a couple hundred cross-location bookings a month, and you're on a central merchant account. A well-structured spreadsheet and a monthly review genuinely works at that scale. Building a ledger engine would be over-engineering.

You need the full model if: locations are separate legal entities or partnerships, you're running real per-location merchant accounts, cross-location promotion is common, or you have outside partners who get paid on splits. At that point manual reconciliation isn't just slow — it becomes a source of disputes.

Who should not attempt a custom build: a studio without engineering support trying to hand-roll an append-only ledger. The data model here is straightforward to specify and genuinely hard to operate safely. If that's your situation, your energy is better spent grading platforms against the feature checklist above than trying to build one.

A realistic before/after

A three-location studio group — separate LLCs, shared waitlist, roughly 300–350 cross-location bookings a month — was closing its books about ten days late every month. The owner and a bookkeeper spent the better part of two full days matching Stripe payouts to bookings and working through which location earned what. Partner disputes over splits came up most months.

After moving to a per-location merchant setup with an allocation rules engine, auto-matching within a $0.50 tolerance, and weekly netted remittances, the manual work dropped to somewhere around 20–30 minutes a week. Most of that was a handful of out-of-tolerance refunds. Close moved from day ten to roughly day three. The partner disputes mostly stopped — not because the numbers changed, but because every allocation could finally be explained down to the rule that produced it.

The improvement wasn't speed for its own sake. It was that reconciliation stopped being an argument.

Integration and export contracts

Whatever you build or buy should expose:

  1. A read API for bookings, payments, allocations, and settlements, filterable by location and date range.
  2. Webhooks for the event types listed earlier, with idempotency keys and signed payloads.
  3. A GL-mapped CSV export for accounting import.

A workable remittance CSV header:

`` settlementid,periodstart,periodend,fromlocationid,tolocationid, grossowed,feesallocated,tipspassthrough,nettransfer,glaccount,status ``

Insist that the platform lets you define the GL mapping. A system that hard-codes its own chart of accounts will fight your bookkeeper every single month.

KPIs to know it's working

Track these and you'll know whether your reconciliation is actually improving or just moving around:

  1. Auto-match rate — % of payments cleared without human touch. Target north of 95% once tuned.
  2. Exception queue size — count and dollar value of unmatched items per period.
  3. Days-to-close — calendar days from period end to reconciled books.
  4. Inter-location dispute count — how often partners question a split.
  5. Settlement latency — time from class delivery to inter-location balance settled.
  6. Variance written off — total dollars booked to rounding and tolerance accounts (should be tiny and stable).

A simple dashboard showing auto-match rate trending up and exception dollars trending down is usually enough to tell whether the system is earning its keep.

Migration and rollout checklist

Don't flip this on all at once. Order matters.

  1. Freeze and snapshot. Export current bookings and payments, then reconcile the existing month fully before migrating. Start from a known-clean baseline.
  2. Assign stable IDs to every location, student, and class instance. Resolve any human-readable code collisions first.
  3. Configure rules in a sandbox and replay last month's real bookings through the allocation engine. Compare output to your hand-done numbers.
  4. Run in shadow mode. For one full cycle, let the system generate allocations and settlements without acting on them. Diff against manual. Investigate every mismatch.
  5. Turn on auto-matching, keep settlements manual. Verify the exception queue behaves before you let real money move.
  6. Enable netted remittances once a cycle of auto-matching has held up.
  7. Wire the GL export and have your bookkeeper confirm categories land correctly before trusting the import.
  8. Set tolerance thresholds conservatively, then loosen as confidence grows.

Shadow mode is the step people skip and regret. Running the new logic in parallel against known-good numbers is the cheapest insurance you'll ever buy on a financial system.

The real win here isn't automation for its own sake. It's that a shared waitlist stops creating ambiguity about who earned what. Once origin, delivery, and collection each have their own field — and every allocation points back to the versioned rule that produced it — the monthly scramble turns into a short review of a handful of exceptions.

The locations keep sharing capacity, students keep getting promoted into open spots, and the money sorts itself out underneath without anyone having to argue about it.

Built for Yoga Studios Tailored specifically for yoga class management & studio workflows
Save Time Simplify class bookings, instructor scheduling & daily operations
Delight Members Faster booking experiences and smooth class attendance
Grow Revenue Boost member retention and maximize class capacity