Thomas Lopez — RevOps
← All writing

A five-tier account fit score that routes each lead to a different motion

Before this existed, "good fit account" meant whatever the rep looking at it thought it meant. Two reps would rank the same list differently, and neither could tell you why.

The short version

An account fit score is only useful if a different thing happens at each score. I built a five-tier model in Clay from three signals available on every account — mission category, revenue band, and current platform — using fixed additive weights plus hard disqualifiers. Each tier then routes to a different motion: the top two go to AEs through a sequencer, the middle runs a high-volume play with human follow-up, and the bottom two are suppressed entirely.

The scoring was the easy half. The half that made it work was the enrichment waterfall — cheapest signals first, expensive lookups gated behind a score threshold — because that's what kept per-account cost low enough to run on the whole database.

The actual problem wasn't scoring

Reps were working whatever landed in their queue. Not lazily — there was simply no shared definition of a good account, so "who should I call today" was answered by recency and gut feel.

The cost shows up in two places at once. Expensive AE hours go into accounts that were never going to close, and accounts that would have closed sit untouched because nothing surfaced them. Both halves are invisible in a pipeline report. You only see the pipeline you built, never the pipeline you walked past.

So the goal wasn't a score. It was a routing decision: for any given account, which of our motions should touch it, if any. The score is just the mechanism.

The three signals that actually predicted fit

I started with a longer list and cut it down to what was both predictive and available on essentially every account. Availability matters more than people expect — a brilliant signal present on 20% of your database can't drive routing, because you'd be routing 80% of accounts on a null.

1. Mission category

Nonprofits are classified in public data by standardized activity codes. Our product landed very differently depending on category — some categories have donor bases that behave the way our product assumes, and some don't. This was the single strongest signal and it's free.

The practical trick: don't score at the individual-code level. There are hundreds of codes, most with too few historical deals to say anything. I collapsed them into a handful of buckets based on how our existing customers actually clustered, then scored the bucket.

2. Revenue band

Also public, from annual filings. This is a proxy for two things simultaneously — ability to pay, and organizational complexity. There's a floor below which the product is too much machinery for the organization, and a ceiling above which they've usually built something in-house or bought enterprise.

Band it, don't use the raw number. A continuous variable invites false precision — nobody can defend why $2.4M scores differently from $2.6M, and bands force you to be honest that you're making a coarse judgment.

3. Current platform

What are they using today? Detectable from their own public donation flow. Three states matter: on a platform we consistently displace, on a platform we consistently lose to, or on nothing identifiable at all.

That third state is more interesting than it sounds. "Nothing detectable" splits into an organization too small to have tooling and an organization sophisticated enough to have built their own. Those are opposite ends of the fit spectrum sharing one data value, which is exactly the kind of ambiguity worth spending a research agent on.

What I deliberately left out Headcount, technology stack breadth, and social following all correlated with fit and all got cut. They were either sparsely available or so correlated with revenue band that they added weight without adding information. A scoring model with four inputs that reps understand beats one with twelve that nobody trusts.

How the scoring works

Fixed additive weights with hard disqualifiers. No machine learning.

That's a deliberate choice, not a limitation. A learned model needs hundreds of won and lost outcomes to find real structure, and below that threshold it confidently overfits to noise. More importantly, a hand-weighted model is legible — a rep can look at a tier-two score, disagree, and tell me which weight is wrong. That argument is the feedback loop. You don't get it from a model nobody can read.

SignalConditionPoints
Mission categoryCore bucket40
Adjacent bucket20
Outside0
Revenue bandTarget band30
One band adjacent15
Outside range0
Current platformKnown displacement target30
Nothing detectable15
Known hard competitor0

Then the disqualifiers, which override the score entirely rather than subtracting from it:

  • Below the revenue floor — the product is more machinery than the organization can absorb
  • Already a customer, or an open opportunity
  • Closed lost inside the re-engagement window
  • No working public web presence

Disqualifiers have to be absolute. My first version made them heavy negative weights, and accounts with strong scores on everything else clawed back into tier two while being fundamentally unsellable. A disqualifier is a different kind of statement from a low score. Model it differently.

Tiers only mean something because motions differ

Five tiers, five decisions:

TierScoreWhat happens
185–100Named to an AE, personalized sequence, manual research before first touch
270–84AE sequence, template-driven with generated account-level personalization
350–69High-volume sequencer, no AE time until someone replies, then human follow-up
430–49Newsletter and ad audiences only, no outbound
5<30 or disqualifiedSuppressed

The important line is between two and three, because that's where a human's time stops being spent. Everything above it costs AE hours. Everything below it costs software.

That single boundary is what the model exists to draw. If your tiers all feed the same sequence, you haven't built scoring — you've built a sort order.

Where AI research agents help, and where they lie

The gap in the model was that third platform state. "Nothing detectable" covered both the smallest organizations and the most sophisticated ones, and I couldn't route on it.

Research agents resolve that well — with two constraints that took me longer to learn than they should have.

Ask for a fact, not a judgment. "Is this a good fit for us?" produces confident, useless answers. "Does this organization's donation page use a third-party payment processor, and if so which one?" produces a checkable answer. Every question should have a right answer that exists on a page somewhere.

Require a source URL and treat its absence as a null. This is the whole trick. If the agent must return the URL where it found the answer, and you discard any answer without one, fabrication largely stops being your problem. It becomes a coverage problem instead — some percentage comes back empty — and a coverage problem is one you can see and manage. A fabrication problem is invisible until a rep gets embarrassed on a call.

research prompt — structure, not wording
You are researching one organization. Return JSON only.

Organization: {{name}}
Website: {{domain}}

Answer only from pages on this organization's own website.
Do not infer. Do not use general knowledge about similar
organizations.

Return exactly this shape:

{
  "processor": "<name of third-party payment or donation
                 processor visible on their donation flow,
                 or null>",
  "source_url": "<the exact URL where you saw it, or null>",
  "self_hosted": "<true if the donation form is served from
                   their own domain with no third-party
                   processor visible, else false, else null>"
}

If you cannot find the answer on their site, return null for
every field. An empty result is correct and useful. A guess
is not.

That last instruction earns its place. Models default to being helpful, and being helpful reads as "produce something." You have to explicitly make returning nothing a success state.

The enrichment waterfall is what made it affordable

Running every enrichment on every account would have cost more than the pipeline it produced. So the order is cheapest-first, with each step gated on the score so far:

  1. Free public data — classification code and revenue band. Runs on every account. Most of the scoring signal comes from here, at zero marginal cost.
  2. Platform detection — cheap, automated. Runs on everything that cleared the revenue floor.
  3. Paid contact lookups — runs only on accounts already scoring into tier three or above. No reason to buy contact data for an account you won't touch.
  4. Research agent — runs last, on the small ambiguous set where the answer would actually change the tier.

Written out it's obvious. It was not obvious while building, because the natural instinct is to enrich everything fully and then score. That instinct is what turns a $200 monthly enrichment bill into a $2,000 one for the same routing decision.

What broke

I treated fit as intent. The first version implicitly assumed a tier-one account was ready to talk. It isn't. Fit says an account should buy eventually; intent says they're looking now. Fit is stable and worth batching monthly. Intent decays in days. Conflating them means your best-fit accounts get worked hardest at moments when nothing is happening, which burns the accounts you can least afford to burn.

Scores went stale silently. An account scored once and then sat there. Organizations change platforms, cross revenue bands, get new leadership. Without a re-scoring cadence the model slowly becomes a snapshot of whenever you happened to run it.

Reps didn't trust it until they could see inside it. A tier with no explanation is an opinion. Once the score reason was visible on the record — which signals fired, which didn't — reps started challenging specific weights instead of dismissing the whole model. Several of those challenges were right.

The name-matching problem bit me here too. Deduplicating enriched accounts against existing records needs the same fuzzy handling as any other join on company names. I wrote that up separately in the piece on extracting Call Report data.

What I'd do differently

Write the disqualifiers before the scoring. Deciding who you will never sell to is faster, less contentious, and removes a large share of the database before you spend a cent enriching it. I built it in the other order and paid for enrichment on accounts that a two-line rule would have excluded.

Instrument tier outcomes from run one. The obvious question — do tier-one accounts actually convert better than tier three — needs a tier stamped on the record at the moment of routing, not recalculated later. Without that stamp you can't tell whether the model works, and you're back to arguing from gut feel with extra steps.

Ship three tiers, not five. Tiers four and five did the same thing in practice for months: nothing. Two tiers of "nothing" is a distinction that exists in the spreadsheet and nowhere in reality. Start with the smallest number of tiers that map to genuinely different actions and split later when a real fourth action appears.

Questions I get about this

Machine learning or fixed weights?

Fixed weights, until you have hundreds of won and lost outcomes. Below that a learned model overfits to noise, and it costs you the thing that actually improves the model — reps being able to read it and tell you which weight is wrong.

What's the difference between fit and intent scoring?

Fit is whether they should ever buy, from stable attributes. Intent is whether they're looking now, from behavior. Fit is worth batching monthly; intent decays in days. Treating a high fit score as a buying signal is the most common way tiering fails.

How many signals should a fit model use?

Three or four that are present on nearly every account. A signal available on 20% of your database can't drive routing, because you'd be routing the other 80% on a null.

Can you trust AI research agents for enrichment?

For retrieving a stated fact from a public page, yes. For judgment calls, no. Require a source URL with every answer and discard answers without one — that converts an invisible fabrication problem into a visible coverage problem.

How often should accounts be re-scored?

Monthly worked for us, because the inputs are slow-moving. The failure mode isn't scoring too rarely, it's never scheduling a re-score at all and letting the model become a snapshot of one day.

I'm Thomas Lopez. I run revenue operations solo — Salesforce architecture, pipeline analytics, and the data plumbing underneath outbound — for a nonprofit fundraising SaaS platform.