Scoring & routing

Score fit and intent on separate axes, match the lead to its account, then route on a clock. The model is not the point. The clock is.

GTM Engineering guide

The first scoring model I inherited summed one number out of firmographics and clicks. It lied in a specific way: a prospect who binged five blog posts outscored a VP at a perfect-fit account who had not lifted a finger. Sales stopped trusting it inside a month, and once a rep stops trusting a score, the score is dead. Nobody works a queue they think is random. The fix was not a better weighting. It was splitting the number in two, and then admitting the model was never the bottleneck. The clock was.

RevenueHero mystery-shopped 1,000 B2B websites in 2025. 63.5% never responded to a qualified inbound at all, and the ones that did answered in an average of 29 hours. Read that as the opening it is. Buyers now expect a reply inside the hour, and most vendors take more than a day or ghost entirely. That gap is the moat your routing stack is built to hold, not a benchmark to match.

63.5%
Of 1,000 B2B sites never responded to a qualified inbound (RevenueHero 2025)
29 hrs
Average first response time among sites that did reply (RevenueHero 2025)
fit × intent
Two axes, never one blended sum (HubSpot 2025 rebuild)

Keep fit and intent on separate axes

Fit is whether the account should ever buy from you: industry, size, tech stack, region. It moves slowly and comes from firmographics. Intent is whether they are moving right now: a demo request, three pricing-page visits in a week, a repeat session in the same day. It spikes, it decays, and it comes from behavior. When you add the two into one total, a flood of low-value clicks masks a bad-fit account, and a great-fit account with no recent activity sinks to the bottom of the queue. A fit score that drops because someone stopped opening emails is a confused composite reporting two unrelated facts as one.

HubSpot rebuilt its scoring in 2025 around exactly this split: a fit letter grade crossed with an engagement number, so a lead reads as A1 or C3, never a single blended total. You act on the top-left corner, high fit and high intent, and you route the rest to the play each quadrant deserves. Intent prioritizes inside the high-fit pool. It never rescues a low-fit one. A C-grade account requesting five demos is a tire-kicker with a fast trigger finger, not a lead.

Here is the grid I hand every team that asks how to read the two axes at once. The quadrant a lead lands in is the routing decision, before any rep sees it.

The grid Fit on one axis, intent on the other, four routes out
Intent (behavior, spikes and decays)Fit (firmographic, slow)Route to a rep nowA1 / A2 / B1: SLA clock startshigh fit, high intentNurture, no cold callA3: great fit, quietwarm when a signal firesTire-kickerC1: poor fit, high intentself-serve or holdSuppressC3: poor fit, no intent
High fit and high intent go to a rep on a clock. High intent with low fit is a tire-kicker. High fit with low intent is nurture, not a cold call. Low-low is suppression.

The scoring rubric that produces those grades is a points table you can write down and a rep can read, not a mystery model. Two rule sets, two outputs, no blending.

SignalAxisPointsWhy
Enterprise employee countFit+30Target segment, slow-moving firmographic
Target industryFit+20ICP core (HubSpot 2025 fit-grade inputs)
Target tech in stackFit+15Technographic fit, from enrichment
Personal email domainFit-20gmail/outlook signals non-buyer or bad match
Demo requestIntent+40Strongest first-party intent
Pricing-page visitIntent+15High-commercial-intent page
Three-plus sessions in seven daysIntent+15Repeat behavior, last-7-days weighs ~3× older (Woodpecker signal-decay data)
UnsubscribedIntent-100Hard suppression

Notice the two axes never touch. Fit sums to a letter band, intent sums to a number band, and the pair is the grade. A -20 on the fit axis for a personal email domain cannot be bought back by clicking the pricing page nine times, because those points live on a different axis and never meet in a sum.

Speed-to-lead is the reason the stack exists

The old five-minute rule gets quoted as gospel, and the 21x and 100x multipliers behind it come from studies old enough to vote (2007 to 2012). Treat the exact number as a heuristic, not a law. The shape holds anyway: contact odds fall off a cliff in the first hour and keep bleeding for a week. A qualified A1 that sits unrouted overnight is worth a fraction of the same lead answered in ten minutes, and no scoring refinement recovers that decay.

What waiting costs a qualified inbound
Relative odds against a 5-minute baseline of 100. Directional heuristic drawn from the 2007-2012 speed-to-lead studies, not a fresh benchmark. Toggle contact odds against qualification odds; both decay, qualification faster.
View as table
PointValue
5 min100
30 min62
1 hr45
4 hr28
24 hr12
48 hr7

That is what your scoring-and-routing stack buys: a shorter clock, not a cleverer model. I wrote the longer argument for treating routing as a latency problem in routing is latency. The one-line version: if a hot A1 waits overnight because the round-robin was down or the L2A match failed, the model quality is irrelevant. You lost the deal in the queue, not in the score.

Lead-to-account matching happens before you route

Routing before you know the account is how two reps end up dialing one company on the same afternoon. Lead-to-account matching (L2A) resolves an inbound lead to the account it belongs to, and it is harder than it looks. Matching on email domain alone gets you roughly 70% coverage. Layering fuzzy company-name matching on top adds another 15 to 20 points, because half your inbounds arrive from a gmail address or type “Acme Corp.” against an account stored as “Acme, Inc.” Native CRM dedup does exact matches. It does not do fuzzy. That is the whole reason L2A is a tooling category and not a checkbox.

Match methodCoverageNotes
Exact email domain~70%Native CRM can do this; misses free-mail and typos
+ Fuzzy company name+15 to 20 ptsHandles “Acme, Inc.” vs “Acme Corp”; needs a real matching engine
+ LinkedIn company URL+5 to 10 ptsBest disambiguation for common names (matches the enrichment-side lift)
Native CRM dedup aloneExact onlyNo fuzzy; “Block” mode can silently reject API inserts and lose the lead

Match first, then route. If an AE already owns the resolved account, the inbound goes to that AE, not into the round-robin. Here is the domain-match cut I run first in Salesforce before any fuzzy layer, to see how many inbounds resolve cleanly and how many fall through to the fuzzy engine:

SELECT l.Id, l.Email, l.Company, a.Id accountId, a.Name
FROM Lead l
LEFT JOIN Account a
  ON a.Website LIKE '%' + SUBSTRING(l.Email, INSTR(l.Email,'@')+1) + '%'
WHERE l.IsConverted = false
  AND l.Email != null

Salesforce SOQL will not do that join or SUBSTRING natively, so in practice you extract the domain into an Email_Domain__c field on the Lead with a formula, then match it against a normalized Account_Domain__c. The lesson from the field is the same one that governs enrichment: standardize the domain into its own column before you match anything, or the fuzzy layer inherits your dirty data. I go deeper on that in enrichment waterfalls.

The build order, in the order I build it

This is the sequence I stand up on every routing engagement. Each step is a precondition for the next, so the order is not a suggestion. Skip enrichment and there is no fit grade to route on. Skip L2A and you route duplicates. Skip the SLA clock and the whole stack is decoration.

The routing build order
  1. 1

    1. Enrich before you score

    Fit attributes (size, industry, tech, domain) must exist before a fit grade can be computed. No enrichment, no fit axis. Run the company waterfall first so 40 to 60% of junk drops before you spend a credit on people.

  2. 2

    2. Score fit and intent on separate axes

    Two rule sets, two outputs: a letter for fit, a number for intent. Never a single sum. Write the points table down so a rep can read why a lead graded A1.

  3. 3

    3. Match to the account (L2A)

    Resolve to the existing account by domain, then fuzzy name, then LinkedIn URL. If an AE owns it, the inbound is theirs. This runs before any owner assignment.

  4. 4

    4. Apply the routing gate

    Only leads clearing both the fit floor and the intent floor enter sales routing. Everything else goes to nurture, self-serve, or suppression per its quadrant.

  5. 5

    5. Assign by territory and capacity

    Round-robin inside the right segment with a per-rep daily cap, so nobody drowns while another rep idles. Territory rules win over round-robin.

  6. 6

    6. Notify and start the SLA clock

    A Slack ping plus a task the second it lands, with a timestamp. The clock is the product, not the assignment.

  7. 7

    7. Escalate on breach

    A lead untouched inside the window reassigns or alerts a manager and logs the miss, so the P90 is auditable and the breach has an owner.

The capacity cap in step 5 is where round-robins quietly fail. Naive round-robin ignores who is buried, so leads-per-rep can spread 20 to 40% across a team while the assignment log looks perfectly even. Cap it. Here is the shape of the guard in Apex, checking today’s assigned count before the rep is eligible:

// Skip a rep who has already hit the daily cap; fall through to the next in ring
Integer todayCount = [
  SELECT COUNT() FROM Lead
  WHERE OwnerId = :candidateRepId
    AND Routed_At__c = TODAY
];
if (todayCount >= repDailyCap) {
  continue; // move to next rep in the round-robin ring
}
lead.OwnerId = candidateRepId;
lead.Routed_At__c = System.now();

Rules-based versus predictive: start with rules

Every team eventually asks whether to replace the points table with a machine-learned model. The honest answer is: not yet, and probably not for a while. Under about 50 conversions, a model has nothing to learn from, and it will confidently mislabel everything for a quarter while reps lose faith in the whole system.

Rules-based Predictive (ML)
Cold-start Works on day one Needs history; under ~50 conversions it guesses
Transparency Reps see the points and trust them Black box; reps reject scores they cannot explain
Data floor None to speak of HubSpot wants 25 won + 25 lost; Einstein ~1,000 leads / 120 conversions
Upkeep You tune it on purpose Drifts silently as the market shifts
Verdict Start here, always Add only once rules provably miss buyers, keep a rules floor under it
Rules win until you can prove they miss real buyers. Then you add ML with a rules floor still under it.

When you do earn the right to add a model, do not swap it in live. Run it in shadow mode: score every lead both ways, route on the rules, and log where the model disagreed and who converted. That is the only way to know the model beats the table before you bet the queue on it. I laid out that pattern in shadow-mode scoring.

Report the P90, not the mean

The mean response time is the number that makes a broken SLA look fine. Ten leads answered in two minutes and one answered at 3am average out to a respectable figure while that one overnight lead was the deal you lost. Watch the P90. That is where deals die, and it is the number a manager can act on. Pull it straight from the routing timestamps:

SELECT Owner.Name,
       AVG(Minutes_To_First_Touch__c) meanMins,
       MAX(Minutes_To_First_Touch__c) worstMins,
       COUNT(Id) leads
FROM Lead
WHERE Routed_At__c = LAST_N_DAYS:30
  AND First_Touch_At__c != null
GROUP BY Owner.Name
ORDER BY MAX(Minutes_To_First_Touch__c) DESC

SOQL has no percentile function, so pull the raw Minutes_To_First_Touch__c per lead and compute the P90 downstream, or approximate with MAX per rep to surface the worst offenders first. The reps at the top of that worstMins list are where your speed-to-lead advantage leaks out.

Before you touch a single weight, pull your own speed-to-lead distribution and find the P90. If a qualified inbound waits hours, no scoring change moves the number, because the score was never the constraint. The clock was. Fix the clock first, then earn the right to argue about weights. For the deeper systems view of routing as a distributed-latency problem, read lead routing as a distributed system.