Tour Dash
Report · 4 September 2026

The lead and referral shell

Three new surfaces are now in the app: a board where you buy a lead, a referral pipeline that tracks a deal instead of holding the money, and a posting flow where the rule about who may be paid a percentage is on screen rather than buried in a policy document. It is a shell, which means the screens and the money arithmetic are real and the database behind them is not. This is what it does and where the seams are.

Follows the marketplace plan Status Running locally, nothing deployed
3
New screens, reachable in the sidebar
10
New files, 1,761 lines
0
Database tables added so far
0
Supabase requests from the new pages
01

Where to click

Two new items sit in the sidebar under My Listings, and a third screen is reachable from either of them. All three are agent-only, on the same reasoning that already hides My Listings from a contractor: a contractor neither buys leads nor takes referrals.

ScreenRouteWhat it does
Buy Leads/leadsBrowse leads with the contact hidden, pay to unlock one, dispute it afterwards
Referrals/referralsReferrals you sent, ones sent to you, and an open board, each with a stage track
Post something/sellPick what you are posting and see which kinds your account may post

They also run inside /demo, which is the easiest way to look at them because it needs no login. Try localhost:8080/demo/leads when the dev server is up. The new pages issue no Supabase request of any kind, so putting them in the demo does not weaken its isolation.

02

Money direction, said out loud

Every board in Tour Dash until now showed work you get paid to take. The lead board is the first screen where the person browsing is the one who will pay, and getting that wrong by even a glance is the kind of mistake that costs a signup. So each card carries the direction as an arrow and a phrase, "You pay $45.00" or "You're paid", rather than leaving it to be inferred from context.

The direction is deliberately not a colour. The three service families already use the whole categorical palette, red and green fail under deuteranopia, and amber is reserved for the primary action. An arrow leaving and an arrow arriving separate cleanly for everyone at no cost, which is the same reasoning that made provider work a hollow ring instead of a fourth hue.

The lead board showing six lead cards for New Jersey towns, each with a price band, motivation line, source, consent status, seller name and dispute rate, and a per-card price the buyer would pay.
The lead board. Every card says who pays and how much before you open anything.
03

Buying a lead

The mechanic that makes a lead marketplace work is that you can see enough to price a lead and not enough to work it. Before payment the card carries the town, the price band, how soon they are moving, one line of motivation, where the lead came from and whether the person agreed to be contacted. The name, phone and email are the product, and they unlock on purchase.

A lead dialog before purchase, showing source, capture date, consent status and seller dispute rate, with contact details locked and a breakdown reading buyer pays $45.00, Tour Dash fee $7.40 at 16 percent, seller receives $37.60 after 72 hours.
Before paying. The split is shown up front, including what the seller gets.
The same dialog after purchase, now showing the contact name Priya Raman, a phone number, an email address and a seller note, with a warning that there are 72 hours to dispute the lead.
After paying. The dispute clock is stated rather than buried in terms.

Showing the seller's cut before the button is a decision worth defending. It is the number both sides argue about, and a marketplace that hides it earns a reputation for skimming that is very hard to shake off later.

Three trust mechanisms from the plan are on the screen and doing something. A seller carries a visible dispute rate, and one above 15% renders delisted and unbuyable rather than being quietly filtered out, so an agent can watch the market police itself. A lead with no consent record says so on the card instead of looking identical to one with a consent record. A shared lead counts down its remaining slots and stops selling at its cap.

04

The licensing rule, on screen

This is the part of the plan that most needed to become a screen rather than a paragraph. A referral fee is a share of a commission. In most states it may only be paid broker to broker between licensed people, and RESPA section 8 bans fees paid for referring a settlement service. A lead sold for a flat price is a different object: it is data, the payment is not contingent on anything closing, and anyone may sell one.

So the posting flow shows all three kinds with their direction and their timing, and locks the one this account may not post. The important detail is that the blocked card does not disappear. It explains why, and it names the legal alternative in the same breath. A cold calling shop that finds no way to sell anything simply leaves.

The posting screen offering a job, a lead and a referral. Each carries badges for direction and timing. The referral card is dimmed with a padlock and a note reading: verify your real estate licence to send referrals, until then you can sell the same contact as a flat-fee lead.
The gate. The referral card is locked, says why, and offers the flat-fee route instead.
One switch is fake on purpose

"Pretend my licence is verified" is a toggle on that page, and it exists only so the gate can be demonstrated without a database column and a verification queue behind it. In the real thing the value comes from the profile. It is labelled as pretend rather than dressed up, because a demo control that looks like a real one is how a shell gets mistaken for a product.

The rule itself lives in one function, postableKinds in src/lib/marketplace.ts, so nothing has to re-derive it. A lead vendor gets leads only. An agent gets jobs and leads, and referrals once their licence is verified.

05

Referrals are tracked, not held

There is no pay button on the referral screen, and that is the design rather than an omission. A referral settles out of a commission that does not exist yet, 30 to 120 days out. Stripe cannot authorise a card that long, so an escrow here would be a promise we could not keep.

What the screen holds instead is the agreement, the stage and the invoice. The percentage, the scope and an expiry date are frozen when both sides sign, the receiving agent moves the referral along as it progresses, and a closed referral offers to generate the broker to broker invoice. Progress is drawn as a five step track rather than a status word, because the question anyone actually has about a referral is whether it is still moving.

The referrals screen with tabs for sent to me, I sent, and open board. One card shows a Stamford Connecticut seller referral at 25 percent, marked both signed, with a five segment progress track filled to four and labelled under contract, and a note that the fee is estimated on a $21,500 commission.
A referral in progress. Four of five stages reached, and the fee shown against an estimated commission.

The fee is rendered from referralFeeCents, so the number on the card is the number the invoice would carry. It is an estimate because the commission is one, and the screen says so.

06

What is real and what is not

Worth being blunt about, because the screens look finished and a photograph of one in a meeting will outlive this paragraph.

Real. The screens, the interaction, and the money arithmetic. The lead fee curve is a new function in src/lib/fees.ts sitting beside the existing job curve rather than overloading it, and it survives the repo's own guard: a build step compares src/lib/fees.ts against the copy the Edge Function uses and fails if they drift, so the browser can never quote a fee that Stripe would not charge. The whole thing typechecks and builds.

Not real. There are no tables. Leads and referrals come from src/lib/marketplace-sample.ts, no card is charged, nothing is written, and a refresh puts the board back. Every one of the three screens carries a preview strip saying exactly that, which comes out the day the tables land.

Deliberately, purchases are held in memory rather than local storage. A purchase that survived a reload would start to feel like a real one.

07

Where the seams are

The shell was built so that making it real is a swap rather than a rewrite. Every screen reads its data through hooks in src/hooks/useMarketplace.ts, and those hooks already have the shape their React Query replacements will have.

useLeadBoard()   -> lead_listings + lead_purchases
useLeadContact() -> lead_contacts, behind a paid-purchase policy
useReferrals()   -> referral_agreements

When those tables exist, three function bodies change and no page is touched. The contact details need their own table rather than columns on the lead, because Postgres row level security works on rows and not columns; a policy that requires a paid purchase row is four lines, while hiding columns on a shared table is not a thing you can do cleanly.

The one piece that is not a swap is the payment. The purchase call in useLeadBoard stands in for a Stripe Checkout session that charges the person who did NOT post the listing, which is the opposite of every charge the app makes today. stripe-checkout still rejects any payer who is not the poster, so that guard has to branch before any of this can take money. That remains the first real piece of work, and it is unchanged from the plan.

08

What I left out

Nothing here is committed or deployed. It runs on the dev server, and the five questions at the end of the plan are still the ones that decide what Phase 1 actually becomes, particularly whether a lead sells once or several times, which changes the schema rather than the styling.

The marketplace plan Stripe Connect wired in View the application →