New-Lead β†’ Client β€” Build Plan

Epic / Feature / Task map Β· what's already coded vs net-new Β· build-order slices Β· βœ… BOARD CUT 2026-07-31: Epic #30013 Β· Features #30014–#30019 Β· Slice-1 PBI #30020 (Tasks #30021–26) Β· #29915 re-parented β€” DevOps is now the record; this page is the design reference Β· 2026-07-31
Promotion: Local β†’ Dev β†’ Test β†’ Prod Β· Sandbox = parallel Zift/payment testing enclave (not a promotion step). "On Dev" β‰  shipped. Β· All items out-of-band (root iteration, no sprint).

The spine β€” pluggable front door β†’ one shared onboarding tail

Four ways in, one experience out. The app-invite β†’ install β†’ activate tail is entry-point-agnostic β€” it fires on the contact-group flip that every purchase path hits (#29915), so a buyer gets the same app onboarding no matter which door they came through.

β‘  Public-calendar widgetclicks a class online
β‘‘ Landing-page checkoutJourneys / FPT Pages
β‘’ QR walk-inscans at front desk β†’ phone
β‘£ Manual Add-Membershipstaff in admin
↓  all feed the same tail  ↓
Capture + offerlead Β· pick trial/membership
β†’
FPT Pay embedverify + AVS β†’ token
β†’
Trust-checkoutFunnelService: contact + sub + charge
β†’
App invite β†’ SMS-OTPactivate β†’ app β†’ booked

Doors β‘ β‘‘β‘’ are self-serve (buyer's phone β†’ capture + FPT Pay embed). β‘£ manual join enters at Trust-checkout. All four converge on the app-invite tail. QR = same hosted widget, just a location-scoped URL on a poster/front-desk card β€” near-zero backend.

Epic β†’ Features β†’ Tasks

DONE coded & proven EXISTS already in prod code WIP-BRANCH branch exists NET-NEW to build PARKING not v1 REPO where it lives

EPIC: New-Lead β†’ Client "Super Onboarding" experience

Feature 1 Β· Checkout spine NET-NEW (small)

Validated token + lead + journey β†’ contact + subscription + charge. The highest-risk, highest-proof piece β€” mostly reuse.

NEW
Trust-checkout endpoint billing/checkout/purchase-verified β€” extract shared pipeline from ZiftCheckoutFunction, minus the server-side re-verify MONO public-fpt-functions~1 helper + ~10-line endpoint (see build spec Β§I)
NEW
ValidatedPaymentMethod β†’ AccountVerificationResponse mapper (Token / ExtendedAccountType / AccountAccessory) MONO~10 lines β€” FunnelService.Checkout stays unchanged
NEW
[pay].EmbedConfig table β€” publishable key β†’ LocationId + gym's Zift account (replace the in-memory demo map) PAYDBmakes verify + charge hit the SAME merchant account
NEW
Sign/attest the ValidatedPaymentMethod so checkout can't be fed a forged "verified" PAYharden-later (a forged token just declines at the sale anyway)
DONE
FPT Pay verify + token in the iframe (/api/verify β†’ ValidatedPaymentMethod) PAYfeature/saq-hardening Β· proven on dev + Zift sandbox this week
EXISTS
FunnelService.Checkout β€” contact + subscription + charge MONOFpt.Standard/Services/FunnelService.cs β€” reused as-is
WIP
Auto app-invite after checkout β€” PBI #29915 "Auto-send booking-app invite on membership start" Β· Committed Β· on Dev only (NOT Prod) Β· Antonio MONOfeature/sprint145/29915-Auto-App-Invite (+ bug/sprint145/29915-fix-ctor-merge) Β· wired in ZiftCheckoutFunction Β· parentless today β†’ re-parent under this Epic (keeps its state) β€” link, don't recreate

Feature 2 Β· Embeddable widget PROTOTYPED

Public calendar β†’ slide-out (desktop) / inline (mobile) signup, gym-skinned, embeddable on the gym's own site.

DONE
Interactive prototype: both variants, live sessions, live-payment proven, mobile, iframe-resizer, tenant picker SWA mockclient-onboarding Β· new-lead-signup-mock.html β€” the reference to build against
NEW
Productionize the widget into the public site (calendar + slide-out/inline, responsive, gym-skin) PUBfeature/new-lead-signup-embed (branch cut, empty)
NEW
Wire widget β†’ FPT Pay verify() β†’ trust-checkout endpoint PUB
NEW
Embed loader + iframe-resizer license decision (v4 MIT / v5 commercial / homegrown) PUB
NEW
QR walk-in entry β€” location-scoped QR β†’ same hosted widget (poster / front-desk card / kiosk mode) PUBnear-zero: QR encodes the hosted URL + location key; optional "kiosk/walk-in" query flag
EXISTS
Public sessions API + calendar (/api/public/locationsSessions) MONOPUBreused as-is

Feature 3 Β· Booking step NET-NEW

The one genuinely new capability β€” auto-reserve the clicked class after a successful checkout.

NEW
Reserve the selected class instance post-checkout (kiosk/booking service) MONO
NEW
Capacity / waitlist handling for the auto-book MONO

Feature 4 Β· "Booking Widget" journey type β€” the builder NET-NEW

Christian's design (2026-07 Slack, confirmed): a new journey type = lead capture + booking + checkout in the existing New-Journey modal. The gym's session + offer selections live in the journey (step Payload JSON β€” same pattern as the #29915 invite toggle, no schema change), and the journey generates the widget link. Verified in code: the public calendar has NO visibility flag today (usp_getClassesByDates returns every session) β€” journey-scoped selection avoids touching it entirely, and gives multi-widget-per-gym for free.

NEW
New journey type "Booking Widget" in the New-Journey modal (lead + booking + checkout) MONOSPAselections stored in FunnelSteps Payload JSON β€” proven pattern, no schema
NEW
Session selection β€” pick which classes show (Christian: "not all gyms will want all their sessions available there") SPA
NEW
Offer selection β€” which trials/memberships are sellable (billing-memberships filter) SPA
NEW
Widget-key-aware sessions endpoint β€” server-side filter to the journey's selected classes (client-side filtering would leak unselected sessions in the network tab) MONO
NEW
Journey generates widget link/key + copy-paste embed snippet SPAMONO

Feature 5 Β· Onboarding hand-off (activation β†’ app) NET-NEW / SPEC'D

After the sale: get the member into the app, passwordless. Detailed in the Build Spec.

NEW
iliv.fit β†’ mobile.fitprotracker.com migration β€” config flip + redirects + email templates (Phase 0) MONOPUBSPAfully spec'd in iliv-fit-migration Β§A–F
NEW
/kiosk/registrationClass route-parity / redirect on the new domain PUB
NEW
SMS-OTP activation + code-based login (OTP-login, no password) MOBILEMONO
NEW
"Register" β†’ "Activate" wording (i18n) + Apple App Privacy label audit (drop Health, add Payment/Device) MOBILE

Feature 6 Β· SAQ / PCI hardening IN-PROGRESS

The payment foundation everything above rests on. Its own track β€” the enclave that Feature 1/2 consume.

WIP
FPT Pay enclave (proxynize + verify + charge), CSP, F-1…F-8 findings PAYPUBfeature/saq-hardening (Pay + Public + monolith) Β· ⚠ NO DevOps work item exists β€” branch-only; needs its own Epic/PBI to formalize
NEW
Mobile app raw-PAN/CVV capture β†’ move to the hosted iframe MOBILErides the FPT Pay migration

Parking-lot β€” not v1 LATER

PARK
Contact-not-owned-by-location refactor (multi-location under one identity) MONODBown epic β€” v1 codes against current model
PARK
Social login (Google/Facebook/X) Β· Waiver-as-in-app-checklist

Build order β€” calendar-first (decided 2026-07-30)

1
The spine β€” prove money + CRM end-to-end (dev)

Feature 1 core. Point the mock's PAY&BOOK at verify() β†’ trust-checkout β†’ a real dev contact + subscription + app-invite lands in app.fitprotracker-dev.com. This is where it becomes real.

2
Builder β€” the "Booking Widget" journey type

Feature 4: new journey type in the existing New-Journey modal β€” pick sessions β†’ pick sellable offers β†’ journey generates the widget link (Christian's design). Selections in journey Payload JSON, filter enforced server-side. Prereq for any real calendar β€” gyms must control what publishes.

3
New session route (side-by-side) + widget + auto-book

Features 2+3 β€” the widget is one embed script with three hosts: a NEW route on the public site (existing /sessions/calendar untouched β€” true side-by-side), the gym's own website (snippet), and the QR target. New route = thin host page for the same script gyms will paste β€” dogfoods the embed first. Auto-reserve the clicked class post-checkout. Landing pages / journey checkout not touched at all. β†’ PILOT with one gym; old calendar's Join button retires only after the new route wins.

4
Onboarding hand-off

Feature 5 — with the calendar no longer pointing at iliv.fit, run the iliv.fit→mobile migration + SMS-OTP activation / OTP-login. (Note: register/reset/waiver links also ride ClientAppDomain — calendar unblocks but doesn't finish this.)

βˆ₯
Parallel wins (no dependency on the above)

Promote #29915 auto-invite Dev→Test→Prod (built + verified, purely pipeline) · SAQ iframe on the existing checkout (Feature 6) · both improve production while the core is under construction.

How this gets organized in DevOps

LevelItemIterationAction
EpicNew-Lead β†’ Client "Super-Onboarding" (umbrella)root FitProTracker Β· out-of-bandβž• create (owner: Antonio) β€” like Epics #28623 / #29816 which already sit at root
PBI#29915 Β· Auto app-invite (on Dev only)unchangedπŸ”— re-parent under the Epic β€” parent + iteration are independent; keeps Committed state
PBISAQ / FPT Pay enclave (Feature 6)root Β· out-of-bandβž• create (branch-only today β€” no item exists)
PBITrust-checkout endpoint + embed wiring (Slice 1 / Features 1–2)root Β· out-of-bandβž• create when Slice 1 starts
PBIBooking / admin-builder / hand-off (Features 3–5)root Β· out-of-band⏳ cut per-slice later, not now

Out-of-band = Iteration Path stays at root FitProTracker, never …\Sprint N. Antonio (CEO/dev) runs this off the sprint cadence to stay nimble; progress is tracked by environment promotion (Devβ†’Testβ†’Prod), not sprint burndown.

DevOps items β€” verified read-only 2026-07-30

IDType Β· StateTitleSprint Β· OwnerRelation to this epic
#29915PBI Β· Committed Β· Dev onlyAuto-send booking-app invite on membership start (+ simplify invite email)Antonio Β· parentlessβœ… is Feature 1's auto-invite β€” re-parent under the Epic; on Dev, not yet Prod
#29928Bug Β· CommittedBooking App β€” "Sync Email" fails (email-mismatch)146 Β· Jennilynexpected fixed in prod well before this epic β€” no action
#29715 / #29499Task/PBI Β· DoneSessions_CTE perf rewrite144 Β· Christian⚠ NOT our spine β€” admin margin grid, different from the public calendar
β€”none foundSAQ / FPT Pay hardening—❗ no work item β€” tracked only as feature/saq-hardening; needs its own Epic/PBI

Consolidated working directory β€” done 2026-07-30

Repo (in C:\FPT\client-onboarding\)BranchCarries
FitProTracker (monolith)feature/client-onboarding Β· +2#29915 auto-app-invite (both commits cherry-picked; also on Dev via PR 23629)
FitProTracker.Publicfeature/client-onboarding Β· +2FPT Pay iframe checkout (SAQ F-1/F-2) cherry-picked from saq branch; widget work lands here
FitProTracker.Front-Officefeature/client-onboarding Β· +2#29915 location + per-journey toggles (cherry-picked; also on dev via PR 23630)
FitProTracker.Mobilefeature/client-onboarding Β· =mainfresh (was 21 behind β€” pulled current); SMS-OTP / Feature 5 work lands here
FitProTracker.Payfeature/client-onboarding Β· +8full FPT Pay enclave incl. the 5 previously-uncommitted files (now committed + pushed to saq branch too)

Old branches (feature/saq-hardening, feature/sprint145/29915-*, feature/new-lead-signup-embed) stay on origin as history. C:\FPT\saq-hardening\ is safe to retire β€” verified nothing in the new container worktree-depends on it (monolith/Front-Office parent into the primary C:\FPT\ clones). Move its loose SAQ/FPT-Pages HTML artifacts out before deleting.

Before we open any DevOps items (avoid the stale-backlog trap)