Proposal · v0.32.0

Matches & Programs via OpenRally

TPS mobile <-> Setpoint admin — publish open matches & priced programs to the app

Court bookings and coach lessons already sync both ways between the TPS app and Setpoint admin over OpenRally. Open matches and programs don't — and both features sit parked in Setpoint admin today. This proposal revives them and extends the exact same plumbing: a Setpoint club publishes an open match or a program, TPS players discover and join it (paying on join, like a court booking), and the roster stays in sync on both sides. One new, neutral bookable type on the wire — nothing reinvented.

Awaiting CEO approval2026-06-05Requested by CEO

The gap today

What works and what doesn't

Works today (courts + coaching)

  • Court bookings sync both ways: a TPS player books a Pad Thai court and it reserves in Setpoint admin (and vice-versa)
  • Coach lessons sync both ways: a TPS player books a 1-on-1 lesson and it lands in Setpoint admin (v0.25.0)
  • Cancellations from either side propagate automatically via OpenRally webhooks

Missing today (matches + programs)

  • Setpoint 'open matches' (the operator's 'Events') never reach TPS players — no path to discover or join them
  • Setpoint 'programs' (clinics, leagues, camps) are invisible to TPS — players can't enrol or pay
  • Both features are parked/hidden in Setpoint admin, and their pay-on-join path is unfinished

Why it matters: Open matches and programs are core engagement and revenue for a padel club — the natural next step after courts and coaching. Until they flow over OpenRally, every match or clinic a TPS player wants to join is manual coordination, and the parked Setpoint features deliver no value.

Outcome

What this unlocks

For players

  • Discover Pad Thai open matches and clinics in the app, right alongside courts and coaching
  • Join an open match or enrol in a program individually, paying on join — same checkout as a court booking
  • See spots remaining in real time; join a waitlist when a clinic is full
  • See matches and programs in their TPS booking history; cancel under the same policy as courts

For operators

  • TPS players who join appear automatically on the Setpoint match/program roster — right people, right price, right capacity
  • One source of truth in Setpoint admin for capacity, price, and waitlist — no parallel system to maintain
  • Cancellations and capacity changes stay in sync on both sides automatically
  • Revives two features that already exist in Setpoint but were parked — now earning

For the business

  • Same OpenRally plumbing as courts and coaching — no new mental model, no new infrastructure
  • Proves the protocol scales beyond single-seat bookings to multi-participant, priced offerings
  • One neutral bookable type on the wire keeps OpenRally consumer-agnostic — any future app benefits, not just TPS

The one prerequisite

The hard gate before any paid join

Fix the pay-on-join path before any paid join goes live

Setpoint's join/enrol flow today marks a seat as paid the instant the card intent is created — before the payment actually clears — and nothing reconciles it afterwards. It's harmless right now only because Setpoint runs Stripe in test mode. The moment a TPS player joins with a real card, that becomes real money with no safety net.

Resolution: Phase A rewrites join to a hold-seat -> pay -> confirm lifecycle (a seat only counts once payment clears) and adds the missing reconciliation. This is a hard go/no-go gate: no paid match or program is exposed over OpenRally until it passes.

Already decided

Locked-in choices from the architecture review

#1Architecture review

Is 'Events' a new feature to build?

Decision: No. 'Events' is just the display label for Setpoint's existing Matches feature (an open match). There's no separate entity — we revive Matches and surface it under the Event label.

Why: Verified in the code: the 'Event' badge maps directly to a match. Building a new entity would duplicate what already exists.

#2Architecture review

How do matches and programs appear on the OpenRally wire?

Decision: As ONE neutral, capacity-bearing bookable type (a 'session') carrying an opaque category tag. The protocol never learns the words 'match' or 'program'.

Why: Keeps OpenRally consumer-agnostic — it must never know about TPS or Setpoint's internal feature names. TPS routes on the opaque tag to its own native screens. Dominant industry pattern (generic object + classifier).

#3Architecture review

Where do Setpoint matches/programs show up in the TPS app?

Decision: Natively. A Setpoint open match renders in the existing TPS open-match rail; a Setpoint program renders in the existing TPS events (paid-join + waitlist) rail. No new 'OpenRally' screen.

Why: Native-equivalence: a Setpoint-origin item must look and behave like a TPS-native one. No separate tab, no parallel UX.

#4Architecture review

Who charges the player, given Setpoint runs test Stripe keys and TPS charges real cards?

Decision: TPS owns the charge (real Stripe, in-app). Setpoint records the payment as consumer-settled via OpenRally's payment block. Setpoint never charges the card.

Why: Matches how court + coaching payments already work across the boundary, and keeps the Setpoint live-keys cutover decoupled from this feature.

#5Architecture review

Is this a rebuild of the parked Setpoint features?

Decision: No — it's a revive. The schema and most code are current; the one genuinely unfinished piece is the pay-on-join path. All database changes are additive only.

Why: Verified against the live schema: no drift to chase. A rebuild would fork the match data TPS would consume.

How it works

Two flows, both directions in sync

Flow A — TPS player joins a match or program

  1. 1

    TPS player finds an open match or clinic

    The player browses Pad Thai open matches and programs in the app. OpenRally lists each one with its price and spots remaining, drawn live from Setpoint.

  2. 2

    Their seat is held while they pay

    Tapping 'Join' reserves a seat for a few minutes so nobody else takes the last spot mid-checkout — the same hold mechanism courts and lessons already use.

  3. 3

    Player pays in TPS

    The player pays in the TPS app with a real card (or PromptPay), exactly like a court booking. Setpoint records the payment; it never charges the card itself.

  4. 4

    Roster updates in Setpoint

    On successful payment, OpenRally confirms the seat: the TPS player joins the match/program roster in Setpoint admin, the spot count drops, and the player sees it in their TPS bookings.

Flow B — Setpoint admin creates a match or program

  1. 1

    Operator creates a match or program in Setpoint admin

    The revived Matches/Programs screens — operator sets type, capacity, price, time, court (optional for socials), and waitlist. Nothing new to learn versus today's booking screens.

  2. 2

    It publishes to OpenRally automatically

    The moment it's saved, it appears to TPS as a joinable session with its capacity and price. TPS players see it in the Play feed with 'X spots left'.

  3. 3

    Changes and cancellations stay in sync

    If the operator cancels or capacity changes, OpenRally notifies TPS. Players who joined through TPS see the update in their bookings automatically.

Rollout

Five phases, producer-first, gated on the payment fix

PhaseScopeOperator seesPlayer sees
Phase A — GATESetpoint — fix the pay-on-join path (hold -> pay -> confirm + payment reconciliation). No-go gate for everything paid below.Nothing visible yetNothing yet
Phase BSetpoint — additive producer schema: capacity + seat-hold, court-less matches, external-payment recording, program events, webhook auto-provisioning.Matches/Programs prepared behind the scenes (still hidden)Nothing yet
Phase COpenRally — add the neutral 'session' bookable type (discover, hold, join, cancel, events), modelled on the existing lesson endpoints.Nothing (protocol-only)Nothing yet
Phase DTPS backend — consume sessions: inbound webhook + join/confirm/cancel, writing back to TPS's own match/event tables.TPS-joined players now appear on the Setpoint rosterNothing yet (backend only)
Phase ETPS app — render Setpoint matches in the open-match rail and programs in the event paid-join rail, behind a feature flag.No changeCan discover + join Pad Thai matches and programs end-to-end, same UX as courts

Needs your call

Open questions before we build

Does going live trigger Setpoint's real-Stripe cutover?

TPS charges real cards regardless. The question is whether Setpoint also flips from test to live keys now, or stays in test mode for launch. The payment-path fix (Phase A) is required either way.

Matches and programs together, or matches first?

They share the same engine and the same payment fix. Shipping both together is little extra; matches-first is a smaller first cut.

Confirm the go-ahead to un-park Matches & Programs.

They were parked with only a 'known issues' note — no recorded reason. We'd log the revive decision before building.

Do open matches need a waitlist?

Programs already have one; matches don't today. A waitlist for matches is net-new scope on whichever base we pick.

Programs render via the TPS event rail, not the academy screen — confirm?

The TPS academy 'Program' screen is a dead brochure. The live home for a paid, capacity-bearing program is the TPS event (paid-join + waitlist) rail.

Next

What your approval unlocks

If approved

  1. Record the go-ahead + the decisions above in the decisions log (chore PR, no code)
  2. Start Phase A — the payment-path fix — invisible to operators and players
  3. Ship phases one at a time, each independently reviewable, all gated on Phase A
  4. After Phase E, a demo walks a TPS player joining a Pad Thai match and a clinic end-to-end

If changes requested

  1. Update the decisions + plan to reflect the change
  2. Reshare this page for re-approval before any code is written

Demo

What the post-merge demo will show

Test club: Sandbox Pad Thai Padel (TPS production, connected to a Setpoint facility)

  1. 1Operator creates an open match in Setpoint admin — 4 spots, ฿250/player
  2. 2TPS player opens the app and sees it in the Play feed: '3 spots left'
  3. 3TPS player joins and pays in the app; the Setpoint roster updates within seconds
  4. 4Operator creates a clinic program — 8 seats with a waitlist
  5. 5A 9th TPS player joins and lands on the waitlist automatically
  6. 6Operator cancels the match in Setpoint admin; the TPS players see it cancelled in their bookings