Phase 1, Workflow Flows & Screen Data Map
Flow 1, Partner recruitment & onboarding (recruit → signed → active)
flowchart TD
classDef ghl fill:#e8f2f8,stroke:#258CBF,color:#12303f;
classDef custom fill:#fff1e2,stroke:#F5821F,color:#12303f;
classDef ext fill:#eef7f0,stroke:#327D7B,color:#12303f;
classDef decision fill:#fdf6e3,stroke:#d9a400,color:#4a3b00;
classDef actor fill:#eceff1,stroke:#607d8b,color:#12303f;
A["Melanie recruits partner in GHL<br/>Referral Partnership Pipeline"]:::ghl
B{"Card reaches<br/>'Made a Move'?"}:::decision
A --> B
B -->|not yet| A
B -->|"yes (trigger)"| C["GHL workflow fires webhook to custom app"]:::ghl
C --> D["Custom app creates partner record,<br/>pre-filled from GHL standard fields:<br/>name / email / phone / company / address"]:::custom
D --> E["Send branded invitation email + personal link"]:::custom
E --> F["Partner opens branded intake page,<br/>confirms details + adds mailing address"]:::actor
F --> G["Review 10% terms → click to sign"]:::actor
G --> H["SignWell opens in a NEW TAB to sign"]:::ext
H --> I{"Signed?"}:::decision
I -->|"no / abandoned"| I1["Reminder emails: day 2, day 7;<br/>day 8 escalation to Melanie.<br/>Melanie notified on each send.<br/>Partner stays 'agreement_sent'"]:::custom
I1 --> F
I -->|"yes → returns to app"| J["Create agreement record, store signed PDF,<br/>capture signer fields"]:::custom
J --> K["Partner sets portal password"]:::actor
K --> L["Prompt: complete Stripe Connect payout setup<br/>(bank details; Stripe collects W-9)"]:::ext
L --> M{"Stripe onboarding<br/>complete now?"}:::decision
M -->|yes| N["Mark payout setup complete;<br/>tag GHL 'Payout Setup Complete'"]:::custom
M -->|"not now"| O["Portal shows persistent 'complete payout setup'<br/>banner; tag GHL 'Payout Setup Incomplete';<br/>payouts held until complete"]:::custom
J --> P["Write back to GHL: tag 'Active Referral Partner',<br/>move card → 'Signed Referral Agreement';<br/>at activation the card moves on → 'Nurturing'"]:::ghl
N --> Q["Welcome splash → partner portal · ACTIVE"]:::custom
O --> Q
P --> Q
Notes: trigger is entering "Made a Move" (manual jumps included). SignWell opens in a new tab and returns the partner to the app to set their password. Stripe payout setup can be deferred; if not done, the portal keeps a persistent prompt and GHL is tagged "Payout Setup Incomplete" until it's finished. Reminder cadence for an unsigned agreement: day 2, day 7, then day 8 escalation to Melanie, with Melanie notified each time a reminder goes out. Card moves are two-step (as built): signing moves the card to "Signed Referral Agreement"; creating the portal password moves it on to "Nurturing" (enrolls nurture).
Flow 2, Referral lifecycle (starts in GHL → linked to partner → conversion)
flowchart TD
classDef ghl fill:#e8f2f8,stroke:#258CBF,color:#12303f;
classDef custom fill:#fff1e2,stroke:#F5821F,color:#12303f;
classDef decision fill:#fdf6e3,stroke:#d9a400,color:#4a3b00;
classDef actor fill:#eceff1,stroke:#607d8b,color:#12303f;
A["Partner emails an introduction<br/>with the prospect CC'd to Melanie"]:::actor
A --> B["Lead created in GHL Sales Pipeline<br/>(Melanie runs her own sales here)"]:::ghl
B --> B2["Melanie marks the lead's 'Referring Partner'<br/>field in GHL (= acknowledgment)"]:::ghl
B2 --> C["GHL workflow syncs it: custom app records<br/>the referral · status: open, NO manual entry"]:::custom
C --> D["Start 180-day window from acknowledgment -<br/>tracked in BOTH GHL and the custom app"]:::custom
D --> E{"Deal closes in GHL?"}:::decision
E -->|"won"| F{"Closed inside the<br/>180-day window?"}:::decision
E -->|"lost / no close"| G["Referral stays open, then expires ·<br/>no payout"]:::custom
F -->|yes| H["Referral → converted (eligible);<br/>close synced to custom app"]:::custom
F -->|"no (outside window)"| I{"Melanie override?<br/>extend / waive"}:::decision
I -->|"no (strict default)"| J["Referral ineligible · no payout"]:::custom
I -->|yes| H
H --> K["Qualify as Business Opportunity (Section 5)<br/>→ money flow (Flow 3)"]:::custom
Notes: the lead lives in GHL for Melanie's sales management, and the referral link is CRM-DRIVEN: Melanie sets a "Referring Partner" dropdown on the lead in GHL and a workflow syncs it into the custom app, there is NO manual referral entry in the admin screen. The custom app tracks referral status (open/closed/converted). The 180-day window is recorded in both systems and reconciled when the GHL deal closes (inside vs outside the window). Because GHL has no custom fields today, tracking the window + partner link in GHL means creating a few GHL custom fields (referring partner, acknowledged date, window expiry), a build task, flagged in open questions.
Flow 3, Money flow (client pays in QBO → commission → Stripe payout, recurring)
flowchart TD
classDef ghl fill:#e8f2f8,stroke:#258CBF,color:#12303f;
classDef custom fill:#fff1e2,stroke:#F5821F,color:#12303f;
classDef ext fill:#eef7f0,stroke:#327D7B,color:#12303f;
classDef qbo fill:#efe9f6,stroke:#7A5AA6,color:#2e2140;
classDef decision fill:#fdf6e3,stroke:#d9a400,color:#4a3b00;
classDef actor fill:#eceff1,stroke:#607d8b,color:#12303f;
A["Client is invoiced and pays -<br/>billing runs in QuickBooks Online"]:::qbo
A --> B{"Invoice marked paid in QBO?"}:::decision
B -->|"paid (QBO webhook)"| C["QBO 'invoice paid' event →<br/>updates custom app + GHL"]:::qbo
B -->|"check not yet entered"| D["Melanie records the check payment<br/>IN QBO, the only place payments<br/>are marked received"]:::qbo
D --> C
C --> E["Custom app: client_payment (received)<br/>+ commission = 10% of gross → payable"]:::custom
E --> F{"Client refunded or cancelled<br/>within 60 days of start?"}:::decision
F -->|yes| G["Clawback (negative entry),<br/>nets against next payout"]:::custom
F -->|no| H["Commission sits in the payout queue"]:::custom
G --> H
H --> I{"Release payout?<br/>Melanie's schedule, within 60 days<br/>(or set auto-release)"}:::decision
I -->|release| J["Stripe pays 10% to the partner's bank"]:::ext
J --> K["Generate fee statement<br/>+ update the partner portal"]:::custom
K --> L{"Another client payment?<br/>(renewal / next month)"}:::decision
L -->|yes| A
L -->|"client relationship ended"| M["Commissions stop accruing"]:::custom
Notes: the "payment received" trigger originates in QuickBooks Online (her billing system), which updates both the custom app (commission becomes payable) and GHL. Checks that QBO can't auto-detect are recorded by Melanie in QBO itself, that entry fires the same QBO event, so everything flows through one door. The admin screen has no manual "mark paid" control; QBO is the single source of truth for money in. Stripe still pays the partner out.
Flow 4, System sync (QuickBooks · GoHighLevel · custom app · Stripe) ← reworked
flowchart LR
classDef ghl fill:#e8f2f8,stroke:#258CBF,color:#12303f;
classDef custom fill:#fff1e2,stroke:#F5821F,color:#12303f;
classDef ext fill:#eef7f0,stroke:#327D7B,color:#12303f;
classDef qbo fill:#efe9f6,stroke:#7A5AA6,color:#2e2140;
subgraph QBO["QuickBooks Online, client billing (money IN)"]
Q1["Client invoice paid /<br/>payment received"]:::qbo
end
subgraph GHL["GoHighLevel, sales + partner ops"]
G1["Referral Pipeline:<br/>'Made a Move'"]:::ghl
G2["Sales Pipeline: lead + deal,<br/>180-day window,<br/>deal close (won/lost + date)"]:::ghl
G3["Contact tags:<br/>Active Partner ·<br/>Payout Setup Incomplete/Complete ·<br/>card stage + nurture"]:::ghl
end
subgraph CUSTOM["Custom app, partner source of truth"]
C1["Start partner onboarding"]:::custom
C2["Referral ledger:<br/>attribution, 180-day window,<br/>commissions, payouts"]:::custom
end
subgraph STR["Stripe, partner payouts (money OUT)"]
S1["Payout to partner's bank"]:::ext
end
G1 -->|webhook| C1
Q1 -->|"invoice paid → commission payable"| C2
Q1 -->|"mark paid on deal"| G2
G2 -->|"deal closed + date (in/out of window)"| C2
C2 -->|"partner signed/active · status ·<br/>payout-setup status"| G3
C2 -->|"release payout"| S1
G2 -. "planned (separate scope): client onboarding creates QBO customer + invoice" .-> Q1
Notes: four systems, minimal wires. QBO drives client-payment detection into both the custom app and GHL; GHL drives the pipeline trigger and syncs deal-close-vs-window back to custom; custom writes partner status/tags to GHL and releases payouts via Stripe. The dashed link (GHL → QBO client onboarding) is not in the current Phase 1 scope, see the scope note in open questions. The referral/commission/payout ledger is not mirrored anywhere; it lives only in the custom app.
Screen data map, what's collected, shown, and interactive
"Pre-filled" = from GHL standard contact fields; "Entered" = the partner types it; "Via SignWell/ Stripe/QBO" = collected inside that external step.
A. Intake form (partner-facing, one time, during onboarding)
| Item | How it's obtained | Required | When |
|---|---|---|---|
| First & last name | Pre-filled from GHL, confirm/edit | Yes | On opening the intake link |
| Email (also the portal login) | Pre-filled, confirm/edit | Yes | " |
| Phone | Pre-filled, confirm/edit | Yes | " |
| Company name | Pre-filled if in GHL, else entered | Yes | " |
| Full mailing address | Entered | Yes (contract) | Intake step |
| Acknowledge the 10% terms | Presented to read (not a field) | Yes | Before signing |
| Signature + signer fields (full name, company, address, phone, email, date) | Captured in SignWell (opens in a new tab) | Yes | Signing step |
| Portal password | Entered (or magic-link) | Yes | On return from SignWell |
| Payout account (bank + W-9/tax) | Via Stripe Connect hosted onboarding (Stripe collects the tax form) | Before first payout (can defer) | End of onboarding, or later from the portal |
B. Partner portal, presented (read-only)
| Shown | Source |
|---|---|
| Their referrals and the outcome of each | referrals |
| Each referral's conversion status + 180-day window status/flags | referrals |
| Lifetime earnings (total paid to date) | payouts / commissions |
| Payout history, amount, date, status, fee statement | payouts |
| Pending commissions (payable, not yet released) | commissions |
| A copy of their signed agreement (PDF) | agreements |
| Payout-setup status + persistent "complete Stripe setup" prompt if incomplete | partners.stripe_onboarding_status |
C. Partner portal, interactive (the partner can do)
| Action | Effect |
|---|---|
| Edit contact details (name, phone, company, address) | Updates their partner record |
| Complete / manage Stripe payout setup (persistent CTA until done) | Via Stripe Connect; flips payout-setup status + GHL tag |
| Change password | Account security |
| (Open, not planned) Submit a new referral via a form | Pending Melanie's call, see open questions |
D. Admin screen (Melanie-facing), views & actions
| Views | Actions |
|---|---|
| All partners + status | (referrals sync in from GHL, no manual entry) Mark deal closed won/lost; set client start date |
| Each partner's referrals, earnings paid, pending | Override a referral's 180-day window (extend / waive) |
| Payout queue: payable + 60-day due dates | Release payouts (single/batch) or set auto-release |
| Flags: window passed, clawbacks, negative balances | Pause / end a partner |
| Per-partner ledger detail |
Deliberately NOT here (Matt, 2026-07-17): no "mark payment received" action, payments (including checks) are recorded only in QBO, and the app picks them up from QBO's event. And no in-app "needs attention" notification feed, notifications go to Slack (next section); the admin screen keeps read-only flags for reference, but Slack is where Melanie gets told something needs her.
Notifications to Melanie (reminder events + more)
Notifications arrive in Slack. Every event that needs Melanie's attention posts to a Slack channel in her workspace, the same pattern Matt runs for MapMatix's own onboarding, proven and cheap to build (one webhook per event). No in-app "needs attention" feed; the admin screen keeps read-only flags, but Slack is the alerting channel. Events that notify: signing reminder sent (day 2 / day 7), day-8 escalation, Stripe payout setup still incomplete, an open invoice that may need a check recorded in QBO, a referral approaching/passing its 180-day window, a clawback recorded, a payout due inside the 60-day contract window. Each notification includes a link to the matching admin screen so it's actionable in one click.