phase 2 · listen
Lead scoring and merging
Go deeper
A qualified lead is one real person who fits the ICP and shows fresh intent. This page covers how signals about the same person merge into one lead, the lead as an object, how a lead is scored and tiered, a library of the lead types worth knowing, and what to record so the score can be learned instead of guessed.
detector · the rule that watches → signal · one person caught → lead · signals merged by person, scored → playbook · what to do next
1 — Fit times intent
Fit says they could buy. Intent says they might buy now. A lead needs both; either one alone is a list entry, not a lead.
2 — One person, one lead
Three signals from the same founder on X, Reddit and your site are one lead with three signals, not three leads.
3 — Freeze the score
Store the features as they were when the score was computed. That snapshot is what the next scorer trains on.
what it is, in industry terms
A qualified lead is fit plus intent, attached to a person.
Sales teams call fit "ICP fit" or "firmographic fit" and intent "intent data" or "buying signals". Marketing-qualified and sales-qualified leads are older names for the same split: someone who matches, and someone who matches and is ready. Here the readiness comes from triggers, so a lead is only as good as its freshest signal.
Person — One human, after identity merging. Handles, domains and emails that belong to them hang off the person, not the lead.
Fit — Which ICP fit lines they match, and that no "not if" line or lead rule (customer, never, competitor) applies.
Intent — The signals that caught them: trigger, detector, strength and age in hours.
Score — One number for ordering today's list, from a versioned scorer, with the features it used frozen beside it.
Tier — Hot, Warm or Watch. The tier decides who gets a move today; the score orders people within a tier.
State — Where they are: new, reviewed, in a campaign, replied, joined, paid, or never contact. Plus the contact policy's clock.
LEAD · EXAMPLE — VIBEDRAFT
- Fit
- 3 of 3 public-pain lines hold: live SaaS, sells to people, says the pain in public
- Intent
- "launched 2 weeks ago, 3 signups, no idea where buyers are" on r/SaaS · 9h old · strong; replied to your post on X · 2 days old
- Score
82· scorerv1.0- Tier
- Hot
next move
listen-and-reply on the Reddit thread today; the X reply makes it warm, so a DM is allowed after she answers
identity resolution
How signals about one person become one lead.
Merging is entity resolution. Deterministic rules join records that share a hard key; probabilistic rules join records that only look alike, and carry a confidence. A wrong merge is worse than a missed one: it sends one person's context to another. So soft matches below the bar wait for the founder, and every split the founder makes is kept as a label.
| Rule | Type | Joins when | Confidence | Watch out for |
|---|---|---|---|---|
| Same handle, same channel | deterministic | the platform's user id matches (not just the display name) | certain | renamed handles: key on the platform's id, not the text |
| Same email | deterministic | normalized email matches across waitlist, Stripe, newsletter | certain | plus-addressing and dots in Gmail; normalize first |
| Same domain, founder role | deterministic | a handle's bio links a domain that matches a sign-up's email domain or a launch listing | high | shared domains: gmail.com, agencies, studios |
| Bio cross-link | deterministic | an X or Threads bio links the Reddit, GitHub or site profile, or the reverse | high | links to someone else's product they work on |
| Launch listing maker | deterministic | the maker named on a launch listing links the same handle or domain | high | listings posted by agencies for a client |
| Same product, two handles | probabilistic | two handles both claim the same product URL as their own | medium | cofounders: two people, one product; keep two persons, one company |
| Name plus product words | probabilistic | similar display names and the same distinctive product words, within 30 days | low to medium | common names; never auto-merge, ask the founder |
| Writing match | probabilistic | the same unusual phrases and the same pain, on two platforms, close in time | low | coincidence; use only to suggest, never to merge |
| Founder merge | manual | the founder says two records are one person | certain | keep it as a label for the resolver |
| Founder split | manual | the founder says a merge was wrong | certain | the most valuable label here: it teaches the resolver what not to do |
the lead object
Every lead is one record with the same fields.
This is the shape a lead takes in the store and in every event about it. The person holds identities; the lead holds the reason to talk to them now. One person can have a new lead each time a fresh trigger fires after an old lead closed.
| Field | Type | What it holds |
|---|---|---|
lead_id | uuid | Stable id; every move, touch and outcome carries it. |
person_id | uuid | The merged person. Several leads over time can share one person. |
identities | list | Each handle, domain and email: the channel, how it was linked (rule id), confidence, linked at. |
icp_version | string | The ICP that judged fit. |
fit_lines | map, frozen | Each fit and not-if line: matched, not matched or unknown, with the evidence. |
signals | list | signal_id, trigger, detector_id and version, strength, posted at, captured at. |
score | 0 to 100 | The ordering number for today's list. |
scorer_version | string | Which formula or model produced the score. |
features | JSON, frozen | Every input the scorer used, exactly as it was then. |
tier | hot | warm | watch | Decides who gets a move today. |
tier_history | list | Each change with time and cause: new signal, age, review, rule change. |
enrichment | map | Bio, product, socials, brief of their product; each field with source and date. |
status | enum | new, reviewed, in campaign, replied, joined, paid, closed. |
contact_policy | state | Last touch, touches this week, cooling-off until, channel allowed. |
never_contact | bool + reason | Set by the founder, by a stop request or by a lead rule. Never cleared automatically. |
one lead, as data
{
"lead_id": "ld_7f3c2a",
"person_id": "pn_19b0e4",
"identities": [
{
"channel": "x",
"handle": "@example_founder",
"rule": "bio-cross-link",
"confidence": 0.97
},
{
"channel": "reddit",
"rule": "bio-cross-link",
"confidence": 0.97
},
{
"channel": "domain",
"value": "acmenotes.example",
"rule": "same-domain-founder",
"confidence": 0.9
}
],
"icp_version": "icp.vd.2026-10-09",
"fit_lines": {
"live_saas": "match",
"sells_to_people": "match",
"pain_in_public": "match"
},
"signals": [
{
"signal_id": "sg_a12",
"trigger": "asking-for-a-tool",
"detector_id": "det.asking.reddit",
"detector_v": 3,
"strength": "strong",
"posted_at": "2026-10-16T21:04Z",
"captured_at": "2026-10-16T21:40Z"
},
{
"signal_id": "sg_b07",
"trigger": "engaged-with-you",
"detector_id": "det.engaged.x-replies",
"detector_v": 1,
"strength": "medium",
"posted_at": "2026-10-15T14:10Z",
"captured_at": "2026-10-15T14:12Z"
}
],
"score": 82,
"scorer_version": "v1.0",
"features": {
"strength": 1,
"fresh": 0.76,
"fit": 1,
"stack": 1,
"engaged": 1,
"age_h": 9,
"half_life_h": 24
},
"tier": "hot",
"tier_history": [
{
"at": "2026-10-16T21:41Z",
"to": "hot",
"cause": "new strong signal"
}
],
"enrichment": {
"product": {
"value": "Acme Notes",
"source": "site",
"at": "2026-10-16"
}
},
"status": "new",
"contact_policy": {
"last_touch": null,
"touches_7d": 0,
"cooling_until": null
},
"never_contact": false
}scoring and tiers
A transparent v1 score now, a learned one later.
Start with a formula a founder can read and argue with. Every input it uses is frozen on the lead, so once replies come back the same inputs train v2: a reply-propensity model that predicts who answers within 7 days.
score = 100 × fit_gate × (0.35·strength + 0.25·fresh + 0.20·fit + 0.10·stack + 0.10·engaged)
fit_gate — 0 if any not-if line or lead rule applies, else 1. Fit gates; it doesn't add.
strength — Strongest signal: strong 1.0, medium 0.6, weak 0.3.
fresh — 0.5 ^ (age in hours ÷ the trigger's half-life). With a 24-hour half-life, yesterday's ask counts half.
fit — Share of ICP fit lines that hold, unknowns counted as half.
stack — 1 if two or more signals from different detectors or triggers, else 0.
engaged — 1 if they already replied to, followed or visited you; warm beats cold.
The weights are a starting point to tune, not a finding. Change them only by bumping scorer_version, and judge the change offline on reply labels before it ranks anyone.
Hot — Move today · All three public-pain fit lines hold and the score is high: a strong, fresh signal, or stacked ones.
Warm — Move this week · All three fit lines hold, but the signal is older, weaker or single.
Watch — No move yet · Any fit line fails or is unknown. Keep listening; a second signal can promote them.
A lead is Hot or Warm only when all three fit lines hold; otherwise it is Watch.
the library
Twelve lead types worth knowing by name.
Each type is a pattern of fit and intent that shows up again and again, with the play that fits it. Reply rates are left as "to measure": record them per type and the library fills itself in.
lead_type.stacked — hot
Stacked-signal founder
- who
- Two or more fresh signals from different detectors: asked for a tool on Reddit and posted a quiet launch on X the same week.
- why
- Two independent signals are the strongest evidence you can get without first-party data.
- side
- cold
- score
- 80 to 95 (typical, v1)
- play
listen-and-reply, then founder-led-outbound- reply
- to measure
lead_type.asking — hot
Asking for a tool right now
- who
- "What do you use for {job}?" posted in the last day, by someone who fits.
- why
- The window closes fast: the thread gets answered within a day or two.
- side
- cold
- score
- 70 to 90 (typical, v1)
- play
listen-and-reply- reply
- to measure
lead_type.quiet-launch — hot
Launch-week founder, quiet launch
- who
- Launched in the last 7 days with under 10 upvotes or no comments.
- why
- They know launch didn't work and they're looking for what does.
- side
- cold
- score
- 70 to 85 (typical, v1)
- play
launch-or-launch-again- reply
- to measure
lead_type.unhappy — hot
Paying a competitor, unhappy
- who
- Complains in public, or in a review, about a tool in your category that they pay for.
- why
- Budget already exists; you're replacing a vendor, not creating a need.
- side
- cold
- score
- 65 to 85 (typical, v1)
- play
comparison-interception- reply
- to measure
lead_type.tried-failed — warm
Tried it, it failed
- who
- Posted what they tried (cold DMs, ads, a launch) and that it didn't work.
- why
- High pain, but often venting; answer the post before offering anything.
- side
- cold
- score
- 55 to 75 (typical, v1)
- play
listen-and-reply- reply
- to measure
lead_type.lurker — warm
Engaged twice
- who
- Liked or replied to your posts twice in two weeks, fits the ICP, never signed up.
- why
- They already know you; a direct, low-pressure offer fits.
- side
- warm
- score
- 50 to 70 (typical, v1)
- play
offer-post-reply-loop- reply
- to measure
lead_type.commenter — hot
Commented the keyword
- who
- Replied to a comment-to-get post asking for the thing.
- why
- They raised their hand; the reply is the opt-in. Deliver what was promised first.
- side
- warm
- score
- 75 to 90 (typical, v1)
- play
comment-a-keyword- reply
- to measure
lead_type.waitlist — warm
Waitlist joiner
- who
- Joined the waitlist; may also have a public signal.
- why
- Highest-intent person you have. Learn their pain before you open.
- side
- warm
- score
- 60 to 80 (typical, v1)
- play
waitlist-countdown- reply
- to measure
lead_type.former — warm
Former paying customer
- who
- Paid before, cancelled or lapsed; may be posting about the same pain again.
- why
- They know the product. What changed since they left is the whole message.
- side
- warm
- score
- 55 to 80 (typical, v1)
- play
win-back- reply
- to measure
lead_type.stalled — warm
Signed up, stalled
- who
- Created an account but never did the first action that predicts paying.
- why
- Your problem with them is activation, not reach.
- side
- warm
- score
- 45 to 70 (typical, v1)
- play
stalled-trial-rescue- reply
- to measure
lead_type.building — watch
Building in public, pre-launch
- who
- Shares progress on a product that isn't live yet.
- why
- Fits later. A good beta tester now, a buyer once they've launched.
- side
- cold
- score
- 30 to 50 (typical, v1)
- play
beta-recruit- reply
- to measure
lead_type.adjacent — watch
Right person, no signal
- who
- Fits every line but hasn't shown intent in the window.
- why
- Fit without intent. Messaging them now is cold outreach, not a response.
- side
- cold
- score
- 10 to 30 (typical, v1)
- play
none yet: keep listening- reply
- to measure
how it connects
What comes in, what goes out.
ICP and Trigger Detection — FROM STAGE 3 · the fit lines and not-if lines that gate every lead
Detector catalog — FROM STAGES 5 AND 6 · detectors emit the signals a lead is made of
Buying Moments Map — FROM STAGE 4 · each trigger's strength and how fast it goes stale
Playbook Library — TO STAGE 8 · the tier and type pick the play
GTM tracking plan — TO STAGE 11 · lead_scored, lead_reviewed, identity_linked and identity_unlinked events
data to capture
Record it so the next scorer can be learned.
record
- identity links with method, rule id and confidence, and every split the founder makes
- the score with scorer version and the exact features used, frozen as JSON
- tier changes (Hot, Warm, Watch) with the time and the cause
- enrichment fields with their source and date
- the founder's review of each lead: accepted, skipped, snoozed, never contact, and the reason
- what was on the daily list and in what order, including leads nobody acted on
label — accepted, skipped or never (founder); replied within 7 days (lead); joined the waitlist or paid (outcome); merge right or wrong (split events)
trains — entity resolution (merge or not, from links and splits); reply propensity, lead score v2; calibrated tiers, so "Hot" means a measured reply rate
test — scorer weights and tier cutoffs: offline against reply labels first, then a randomized slice of the daily top 10, with the assignment probability logged