---
name: "lead_scoring"
description: "Score and qualify sales leads with a small model people can trust: separate fit and intent scores, hard gates for true disqualifiers, decay so stale activity stops routing, thresholds set from outcomes, and a monthly review that compares bands against what actually closed. Use when someone asks to build, fix, audit, or explain a lead scoring model, set MQL thresholds, prioritize a lead list, or stop reps complaining about lead quality. For researching one named account use account_planning; for diagnosing where a pipeline leaks use sales_pipeline_leak_diagnosis."
---

# Lead Scoring

## Purpose

Lead scoring ranks leads so reps work the ones most likely to buy first, and
so marketing can prove which of its leads were worth sales time. A model does
that job only if it stays small, separates two different questions, and gets
checked against real outcomes on a schedule.

The two questions, kept separate in every serious implementation: fit (who
is this lead, how closely do they match the customers you win) and intent,
also called engagement or behavior (what have they done recently that shows
interest). Salesforce Account Engagement makes the same split, a score from
implicit buying signals and a grade from explicit profile factors. HubSpot
supports separate fit, engagement, and combined scores for the same reason.
One blended number hides the difference between a perfect-fit lead who has
gone quiet and a poor-fit lead clicking everything, and those two leads need
opposite handling.

This skill ships three things: the method below, a working scorer at
`bin/score.py`, and an outcome-review loop in `references/outcome-review.md`.
It does not ship universal point values. No honest set exists. Point values
are a local decision that your outcome data revises.

## Workflow

### 1. Define fit from wins and losses, not from the pitch deck
List the traits your closed-won customers share and your disqualified leads
share: industry, company size, region, role and seniority of the contact,
segment, required technology or budget signals where you actually know them.
Use won and lost records, not the ideal customer slide. Pick four to six fit
factors. More than that and you are scoring data you do not reliably have.
Mark the facts that end the conversation outright (competitor, region you
do not serve, segment you do not sell to) as gates, not as big negative
numbers a busy lead can out-click.

### 2. Define intent from effort, with a clock on it
List the actions a lead can take, ordered by what each one costs the lead:
a demo or contact request at the top, then pricing page visits, trial or
product use, event attendance, content downloads, email clicks at the
bottom. Email opens sit below clicks or out of the model entirely, since
privacy features in mail clients make opens unreliable. Give the top action
roughly double the next tier, the next tier roughly double the one below.
Every intent factor carries a time window (in the last 30 days, say) and
the score decays when activity stops: after a grace period, intent loses
points for each quiet month, down to zero. Fit does not decay. A company
does not stop matching your profile because it went quiet; its interest
level is what went stale.

### 3. Write the model in one file and score a real list
Put gates, factors, thresholds, and decay in one JSON model file following
`references/model-file.md`, then run the scorer on a real export:

```bash
python3 ~/workspace/skills/lead-scoring/bin/score.py score \
  --model references/model-example.json \
  --leads references/leads-example.csv --out scored.csv
```

Read the output by hand before you trust it. Check the top ten ROUTE leads:
would a rep thank you for each one? Check the DISQUALIFIED list for anyone
a gate caught wrongly. Check a good-fit lead with old activity: decay should
have moved it to NURTURE. The worked example in `references/model-file.md`
walks through eight leads covering all four bands.

### 4. Set thresholds as two bars, and name the action per band
A lead routes to sales when fit AND intent both clear their bars. Set the
bars from step 5's backtest when you have history; without history, start
where only a lead with real fit plus one strong intent action (or several
weak ones) can route, and expect to move the bars at the first review.
Write down the action and the response time for each band before launch:
ROUTE gets a rep and a same-day first touch, NURTURE gets marketing nurture
and re-scoring on new activity, HOLD gets enrichment or nothing, and
DISQUALIFIED gets a recorded reason and no outreach. A band with no action
attached is decoration. Sales confirms qualification facts on the call
(budget, authority, need, timing); the score earns the call, it does not
replace it.

### 5. Backtest before launch if you have any history at all
Score last quarter's leads using the data as it stood when they arrived,
then compare bands against what actually happened: meetings, opportunities,
wins. Conversion should fall band by band. If a lower band converts like the
band above it, the model is not separating anything and launching it will
teach reps to ignore scores within a month. Fix the weights or the bars and
backtest again. Follow `references/outcome-review.md`.

### 6. Review against outcomes on a cadence, one change at a time
Monthly for the first quarter, then quarterly, and right after any change
to the offer, pricing, segment, or sales motion. Each review: export band
at handoff plus final outcome, run the review mode, check per-signal lift,
make at most one change, and write down what changed and why.

```bash
python3 ~/workspace/skills/lead-scoring/bin/score.py review --scored export.csv
```

The full procedure, including the feedback-loop trap (reps work high scores
first, so high scores convert partly because of the attention), is in
`references/outcome-review.md`.

### 7. Keep the model owned and readable
One named owner for the model file, changes in version control, and the
whole model printable on one page. When a rep asks why a lead scored what
it scored, the scorer's reason column must answer it factor by factor. A
model no one can explain gets worked around, and a worked-around model is
worse than none because its reports still look authoritative.

## Output Contract

One scoring package, containing:
1. The model file: gates with reasons, fit and intent factors with point
   values and time windows, thresholds for both bars, decay settings.
2. A scored lead list with fit, intent, total, band, and a factor-by-factor
   reason per lead, produced by `bin/score.py`, spot-checked by hand.
3. The band action sheet: owner, action, and response time per band,
   agreed with sales before launch.
4. The backtest or first-review result: conversion by band with lead
   counts, any inversion found, and the one change it produced.
5. The review log: date and justification for every model change so far.

## Operating Rules

- Accuracy first: every claim about what predicts conversion comes from the
  team's own outcome data or gets labelled as a starting guess. Never import
  point values, thresholds, or conversion statistics from a vendor guide as
  if they were evidence. Sources disagree on all of them because the right
  numbers depend on the offer and the market.
- Fit and intent stay separate end to end: separate subtotals, separate
  thresholds, separate decay treatment. A combined display number is fine
  for sorting a list; it is never the routing rule on its own.
- Hard facts are gates, soft signals are points. Never let accumulated
  clicks override a gate, and never use a huge negative number to do a
  gate's job.
- Cap repeated low-effort events. Roll repeats up before scoring so ten
  visits cannot outscore one demo request.
- Score only on data that existed at the time. Activity that happens after
  sales engages (meetings booked, proposals sent) stays out of the model,
  or every backtest will flatter it.
- Treat protected or sensitive traits with care: score on company and role
  facts and on behavior, and check local privacy law before scoring on any
  personal attribute. When in doubt, leave the factor out; a small model
  loses little from one fewer factor.
- One change per review period. Changing three weights at once makes the
  next review unreadable.
- If the data is too thin to support a model (few leads, no outcome
  history), say so and ship the smallest honest version: gates plus a
  hand-ranked intent list, reviewed by a human, until the volume earns
  arithmetic.
