Mike's note asks for a second marketplace inside Tour Dash. Today an agent pays someone to cover work. He wants agents to also sell what they cannot use: a referral, a lead, an open house slot. And he wants the people who generate leads for a living, the lead-gen companies, cold callers, VAs, ISAs, wholesalers and investors, selling into the same app. This is the plan for how that gets built, what it breaks, and the order to do it in.
Every listing in Tour Dash runs the same loop. An agent posts a job, a contractor
applies, the agent approves, and the agent pays. The money direction is not a setting. It is
written into the code as a rule, in supabase/functions/stripe-checkout/index.ts:
if (listing.poster_id !== user.id)
return json({ error: "Only the agent who posted this showing can book it." }, 403);
Whoever posts the listing is the one who pays for it. That single line is the reason none of what Mike described can be built as a new service type. A referral, a lead and an open house slot all run the other way: the person who posts is the person who gets paid, and the person who responds is the one reaching for a card.
Everything below follows from flipping that.
It is tempting to treat all of this as "listings that pay backwards", but the three products behave differently enough in money terms that collapsing them would produce a broken checkout. They differ on who pays, when, and whether the payment is certain.
The dashed line is the hard one. A referral is a promise to pay months later, out of money that does not exist yet.
| Shape | Who pays | When | Certain? | Typical size |
|---|---|---|---|---|
| Job | Poster | At booking, held until completion | Yes | $50 |
| Lead | Taker | At purchase, held 72 hours | Yes | $10 to $150 |
| Referral | Taker | At closing, 30 to 120 days out | No | $2,000 to $15,000 |
The lead sale is a normal checkout with the arrow reversed, which is why it ships first. The referral is a different product wearing a marketplace costume, and section 05 deals with it separately.
Mike's second sentence opens the platform to people who are not licensed agents: lead-gen companies, cold callers, VAs, ISAs, wholesalers, investors. That is a good instinct for supply, and it is also the part most likely to cause a real problem if it is built the obvious way.
Real estate referral fees are broker to broker. In most states an unlicensed person cannot legally be paid a share of a commission for sending business, and RESPA section 8 bans fees paid for the referral of settlement services. So the version of this where a cold caller gets 25% of a closing is not a feature we can ship, however much everyone would like it.
Who you are does not decide the pricing model. Whether you hold a licence does.
A licensed agent may sell a referral, priced as a percentage of a commission and paid at closing. Anyone may sell a lead, priced as a flat fee and paid on purchase, because a flat fee for data and marketing work is not contingent on a transaction closing and is not a referral fee.
That one rule lets every seller Mike named onto the platform, and it is enforceable in
code: account_type plus a verified licence decides which listing kinds you are
allowed to post.
This needs a lawyer's sign-off before launch rather than mine, and the questions to put to them are in section 09. It is a constraint on the design, not a reason to stop.
Tour Dash already has an open-house service type, and it points one
way: a listing agent pays somebody to sit the house for three hours on a Sunday. That is a job
and it works today.
Mike is describing the other direction, and it is a genuinely different trade. A listing agent with a busy open house is sitting on door traffic they cannot personally convert, and a hungry buyer's agent will pay for the right to host it and keep the leads that walk in. Agents already do this over text. Nobody has put a price and a payment on it.
So direction belongs on the listing, not on the service type. The same open house is a job when the poster wants it covered and a lead sale when the poster wants it bought. One dropdown at creation, two very different listings out the other end.
A referral is a promise about money that does not exist yet. Stripe cannot hold a card for 90 days, there is no charge to authorise, and nothing in the app can force two agents to report a closing they would both rather settle privately. On a $2,500 referral fee the incentive to go around us is large and the window to do it is months long.
So the first version does not try to escrow a referral. It does the paperwork and keeps the record:
Both agents sign an agreement in the app with the terms frozen, meaning the percentage, the scope, and an expiry date. The receiving agent moves the referral through stages as it progresses, from accepted to engaged to under contract to closed. On closing, the app generates the broker to broker invoice with the fee already calculated. Payment runs on ACH rather than cards, because 2.9% of a $2,500 fee is $73 of pure waste.
What keeps people honest is not the software. It is that the referring agent decides whether they ever send a second one, and that a closed and unpaid referral is visible on a profile. The exported agreement is also a document a broker can act on, which is more than a text thread gives anyone today.
My recommendation is that we take nothing on referrals at launch. Referral volume is worth more to us as proof the network works than as revenue, and we can price it once we know how many actually close.
A lead marketplace with no quality bar becomes a dumping ground for recycled Zillow leads inside a month, and the agents who get burned twice never come back. The trust design is not a later polish item, it is the thing that decides whether this works at all.
Four mechanisms, all cheap to build:
Buyers see the lead redacted before they pay. City, price band, timeline, source and a motivation summary are public. Name, phone and email unlock on purchase. A lead you can read for free is a lead nobody buys.
Every lead carries provenance. Where it came from, when it was captured, and whether the person consented to be contacted. This is partly a quality signal and partly a TCPA and Do Not Call problem, since we would be selling phone numbers for cold outreach.
The buyer gets 72 hours to dispute a lead as dead, wrong, or duplicated, with
the refund coming out of funds we are still holding. The hold and release machinery already
exists in stripe-release, built for showings, and it works here unchanged.
Phone numbers are hashed and checked across the whole table, so the same lead cannot be quietly sold to five agents in three markets. Selling one lead many times is the specific failure that kills these marketplaces, and it has to be blocked at insert rather than caught in review. Sellers carry a visible dispute rate over their last 50 sales, and anyone above 15% comes off the storefront.
The existing fee curve is anchored on a $50 showing and tapers as the job gets
more expensive, which is right for showings and wrong for both new shapes. A lead sells for a
tenth of a showing and a referral for fifty times one, so a single formula cannot serve all
three. src/lib/fees.ts should grow two more curves rather than overloading
platformFeeCents.
| Shape | Fee | On a typical sale | Take rate |
|---|---|---|---|
| Job, unchanged | $5.00 + 10% | $10.00 on $50 | 20% |
| Lead | $2.00 + 12% | $5.00 on $25 | 20% |
| Lead | $2.00 + 12% | $20.00 on $150 | 13% |
| Referral | Nothing at launch | $0 on $2,500 | 0% |
A $10 minimum on leads keeps the flat component sane, and card processing at 2.9% plus 30 cents is heavy on small amounts, so buying a pack of ten leads has to be one charge rather than ten. That is a checkout detail worth getting right early, because it is awkward to retrofit once purchases are one row per card charge.
When we do price referrals, 5% of the referral fee capped at $500 is the shape I would test. On a $2,500 fee that is $125 against roughly $5 of ACH cost.
The listing, application and appointment spine is reusable. What changes is the direction of the money, one new kind of account, and two tables for the things a job listing has no columns for.
Listings gain a kind, defaulting to the current behaviour so no existing row needs touching:
alter table listings add column listing_kind text not null default 'job'
check (listing_kind in ('job','lead','referral'));
Contact details go in their own table rather than as columns on the lead,
because Postgres row level security works on rows and not columns. A policy on
lead_contacts that requires a paid purchase row is a clean four line rule.
Hiding columns on a shared table is not.
In stripe-checkout, the poster check becomes a branch. On a job the
caller must be the poster. On a lead or a referral the caller must not be the poster, and the
poster has to have stripe_payouts_enabled before their listing can go live at
all. That last part means Connect onboarding, which today only providers ever see, has to open
to agents as well.
One naming note, in the spirit of the rule that the UI says contractor while the
code says provider. On a lead sale the payments.provider_id column holds an agent,
not a contractor. The column means "the party being paid" and it should be documented that way
rather than renamed, because renaming it touches the webhook, the ledger, the wallet and every
policy that references it.
Profiles gain a third account_type of
lead_vendor, plus licence verification fields that gate referral posting. A lead
vendor is neither an agent nor a provider: they hold no licence and they never cover a
showing.
Phase 0 is the payment direction, and it unblocks everything else. Branch the
checkout guard, open Connect onboarding to agents, add listing_kind, and prove a
reversed payment end to end on a throwaway listing before any new surface is designed.
Phase 1 is the lead marketplace between agents. Redacted browse, paid unlock, the 72 hour dispute window, duplicate blocking. Agents on both sides, so no licensing question is open yet, and the whole thing runs on machinery that already exists.
Phase 2 opens the vendor side. The lead_vendor account type, bulk
upload so a cold calling shop can post fifty leads without fifty forms, a vendor storefront
with a visible dispute rate, and the consent record on every row.
Phase 3 is the referral rail: signed agreements, pipeline stages, the broker invoice, ACH. Last, because it carries the licensing exposure and earns nothing at launch.
A marketplace with nothing in it is a dead page, and the lead side has a cold start problem the showing side never had. An agent who opens an empty lead board does not come back to check whether it filled up later.
Phase 1 should not ship to everyone at once. It should ship to one metro with supply already lined up, which most likely means a lead-gen company or a cold calling shop Mike can get on the phone before we write the vendor upload.
Does a lead sell once or several times?
Exclusive leads are worth far more and sell far slower. Shared leads move but the agent who bought one is competing with two others by the afternoon. This changes the price, the schema and the pitch, so it is worth deciding before Phase 1 rather than during it.
Do we have supply for the first metro?
A lead-gen company or cold calling shop willing to post into an empty marketplace is worth more right now than any feature in this document.
Who verifies licences, and are we paying for it?
There is no national licence lookup, it is per state. Manual review is fine for the first hundred agents and unworkable after that.
Is a three day wait for lead money acceptable to sellers?
The 72 hour dispute window is what makes the market trustworthy for buyers, and it is what makes a vendor with volume grumble about cash flow. There is a version where vendors in good standing get paid immediately, but it should be earned rather than default.
Do we charge on referrals at launch?
My recommendation in section 05 is no. Worth an explicit decision rather than a default.
Nothing in here is built yet. Phase 0 is roughly a day of work and is the only part that has to happen before any of the rest can be evaluated honestly, so that is where I would start once the questions above have answers.