TL;DR: Build a two-stage lead scoring system in Clay: filter for ICP fit first, then assign points for intent signals. Set a threshold (70+ = SQL-ready) and wire an automation to push qualifying contacts to Instantly, Smartlead, or Outreach. The model runs without manual review once it's configured.
Clay Lead Scoring Models and Thresholds: A Setup Guide
Last updated: June 2026
Lead scoring in Clay turns a flat contact list into a prioritized pipeline. Instead of eyeballing rows and guessing who to contact, you assign points for the attributes and signals that actually predict conversion. But most Clay tutorials stop at 'add a formula column.' They don't explain how to structure the model, calibrate thresholds over time, or wire the scoring output to an outbound tool. This guide covers all three steps.
What Lead Scoring in Clay Actually Does
Lead scoring assigns a numerical value to each contact based on two things: how well they match your ideal customer profile, and how much buying intent they're showing right now. A VP of Sales at a 200-person SaaS company who visited your pricing page twice this week should rank higher than a junior analyst who downloaded an ebook six months ago. Scoring makes that ranking automatic and explicit.
In Clay, lead scoring lives in formula columns. You pull enrichment data from Clay's 100-plus data providers (Apollo, Clearbit, BuiltWith, Crunchbase, and others), assign point values in Clay formulas, sum them into a Total Score column, and filter or trigger actions based on the result.
The mechanics are straightforward. What most guides skip is the model design: which criteria to score, how to weight them, what thresholds to set, and how to clean up the pipeline with negative scoring and decay. That's what this guide walks through, step by step.
Step 1: Build Your ICP Filter Before You Score Anything
Lead scoring is a two-part question: does this contact fit your ideal customer profile, and are they showing buying intent right now? These are different questions, and mixing them produces bad models.
ICP fit is about company attributes: industry, headcount, tech stack, funding stage, geography. These are relatively stable facts. A company is in SaaS or it isn't. Headcount changes slowly. Intent signals are time-sensitive: a pricing page visit this week means something; a whitepaper download six months ago means much less.
If you score both dimensions on the same scale without separating them, a highly active non-ICP contact can outscore a perfect-fit ICP contact who hasn't visited yet. That sends your sequencer to the wrong people and corrupts your threshold calibration over time.
The fix is an ICP filter column that runs before scoring. Build a boolean column called ICP_Pass that evaluates your non-negotiable criteria. If a contact doesn't pass, they never enter the scoring model.
Building the ICP_Pass column in Clay
In your Clay table, add a formula column named ICP_Pass. The formula evaluates two or three firmographic criteria and returns either "ICP_PASS" or "ICP_FAIL".
A typical three-criteria filter looks like this:
const industryOk = ["saas", "software", "b2b tech"].some(i =>
({{Industry}} || "").toLowerCase().includes(i));
const headcountOk = Number({{Employee Count}}) >= 50 &&
Number({{Employee Count}}) <= 1000;
const titleOk = ["vp", "director", "head of", "chief", "founder", "cto", "ceo"]
.some(t => ({{Job Title}} || "").toLowerCase().includes(t));
return (industryOk && headcountOk && titleOk) ? "ICP_PASS" : "ICP_FAIL";
Adjust the industry values, headcount range, and title keywords to match your ICP. If you sell to e-commerce companies with 20-150 employees and need to reach marketing leads, change those values accordingly.
What to put in the ICP filter
Non-negotiable criteria belong here, not nice-to-haves. If you'll reach out to a company regardless of their tech stack, don't put tech stack in the filter. If you'd never reach out to a company with fewer than 20 employees, put headcount in the filter.
Common ICP filter fields:
- Industry / vertical
- Employee count (upper and lower bounds)
- Geography (when relevant)
- Tech stack (when you have a hard integration requirement)
- Funding stage (when you only sell to funded companies)
Job title can go in the filter or in the scoring model depending on how strict you are. If you'll only ever reach out to manager-level and above, it belongs in the filter. If you'd reach out to a senior IC under certain conditions, put title in the scoring model instead.
Only score ICP_PASS contacts
Once the ICP_Pass column is live, add a check to every other scoring column: return 0 when ICP_Pass is not "ICP_PASS". This keeps non-ICP contacts from accumulating points from behavioral columns and keeps your threshold calibration clean. You're calibrating against the population that could convert, not the entire list.
For a deeper look at how ICP criteria map to qualification criteria, see the guide on B2B lead qualification frameworks and ICP definitions.
Step 2: Configure Scoring Formula Columns in Clay
With the ICP filter in place, you're ready to score. Lead scoring models have two layers: explicit (firmographic) signals that reflect how well a contact matches your target profile, and implicit (behavioral) signals that reflect how much interest they're showing right now.
Build each signal as its own formula column. Then sum them into a single Total Score column. Keeping signals separate makes it easy to audit why a contact scored high or low, adjust individual weights without rewriting everything, and use signal columns as personalization variables in your outreach.
Explicit scoring columns
These score based on who the contact is and what their company looks like.
Job Title Score:
const title = ({{Job Title}} || "").toLowerCase();
if (["ceo", "cto", "founder", "chief"].some(t => title.includes(t))) return 30;
if (["vp", "vice president"].some(t => title.includes(t))) return 25;
if (["director", "head of"].some(t => title.includes(t))) return 20;
if (["manager", "lead"].some(t => title.includes(t))) return 10;
return 0;
Headcount Score:
const count = Number({{Employee Count}}) || 0;
if (count >= 200 && count <= 1000) return 20;
if (count >= 50 && count < 200) return 15;
if (count > 1000) return 10;
return 0;
Funding Stage Score:
const stage = ({{Funding Stage}} || "").toLowerCase();
if (["series b", "series c", "series d"].some(s => stage.includes(s))) return 20;
if (["series a"].some(s => stage.includes(s))) return 15;
if (["seed", "pre-seed"].some(s => stage.includes(s))) return 10;
return 0;
Implicit scoring columns
These score based on behaviors the contact has taken: pages visited, forms submitted, content consumed. Pull these signals from your CRM (HubSpot or Salesforce) using Clay's native connectors. If you have a customer data platform, route behavioral events there first, then connect Clay to the CDP.
Behavioral Score:
let score = 0;
if ({{Demo Requested}} === true) score += 30;
if ({{Pricing Page Visit}} === true) score += 25;
if ({{Competitor Comparison Visit}} === true) score += 20;
if ({{Webinar Registered}} === true) score += 10;
return score;
Adjust the event names to match your CRM field names. Demo Requested, Pricing Page Visit, and similar are columns you create in Clay by pulling data from HubSpot via Clay's HubSpot enrichment connector.
The Total Score column
Add a Total Score formula column that sums all your individual scoring columns:
return Number({{Job Title Score}}) + Number({{Headcount Score}}) +
Number({{Funding Score}}) + Number({{Behavioral Score}});
Clay returns 0 for columns with no data, so you don't need defensive null-handling on each term. The Number() wrapper ensures that empty strings coerce to 0 rather than NaN.
With the weights above, a C-level contact at a 200-500 person Series B company who requested a demo would score 30 + 20 + 20 + 30 = 100. A manager at a seed-stage startup with a webinar registration scores 10 + 15 + 10 + 10 = 45. The model separates them clearly before you ever look at the table.
For more on building the enrichment pipeline that feeds these columns, including how to pull company data, contact data, tech stack, and CRM activity into Clay, see the guide on Clay lead enrichment workflows.
Run outbound on autopilot.
Lead lists, enrichment, ICP qualification, personalized openers, sequencer push. Miniloop runs the loop, you take the meetings.
Step 3: Set Your MQL and SQL Thresholds
A threshold converts a raw score into an action. When a contact's Total Score crosses a threshold, they move from "in the pool" to "ready for a nurture sequence" or "ready for direct outreach." Without thresholds, you have a ranking system but no trigger. You still end up eyeballing rows.
Two thresholds matter for most B2B teams:
MQL (Marketing Qualified Lead): The contact is showing enough interest to enter a low-touch nurture sequence. Not ready for a rep's time yet, but worth a personalized email series or retargeting campaign. Starting range: 40-50 points.
SQL (Sales Qualified Lead): The contact has high ICP fit and clear intent signals. Ready for direct outreach, a discovery call request, or a rep follow-up. Starting range: 70-80 points.
These are starting points calibrated to the scoring model in Step 2. A perfect-fit C-level contact with a demo request can score 85+. A manager at an average-fit company with no intent signals might score 30. Adjust the ranges if your model uses different weights.
Building the Status column
Add a formula column called Status that converts Total Score into a label:
const score = Number({{Total Score}}) || 0;
const pass = {{ICP_Pass}} === "ICP_PASS";
if (!pass) return "Non-ICP";
if (score >= 75) return "SQL";
if (score >= 45) return "MQL";
return "Nurture";
The ICP_Pass check is redundant (non-ICP contacts score 0 in your model), but explicit is better than implicit when you're building automations off this column.
Building a filtered table view
In Clay, create a saved table view called "SQLs Today": filter by ICP_Pass = "ICP_PASS" and Status = "SQL". This gives reps a clean queue of hot leads without manual review. Add a Date Added column so the view surfaces the most recent SQLs first.
How to calibrate thresholds
Set thresholds once, then revisit. After 2-4 weeks of outreach, pull two lists: contacts who replied positively or converted, and contacts who bounced or didn't respond. Compare their Total Score values at the time they were sequenced.
If most of your positive replies came from contacts who scored 60-70 and your SQL threshold is 75, lower it. If your sequencer keeps emailing contacts who have no real intent and replies are mostly objections, raise the SQL threshold or add more weight to behavioral signals.
Review thresholds monthly during the first quarter after launch, then quarterly once the model stabilizes. Thresholds that stay fixed for six months without review stop predicting conversions. Your ICP, your messaging, and the market all shift, and the model needs to keep up.
Step 4: Add Negative Scoring and Lead Decay
Positive scoring tells you who's a good fit. Negative scoring tells you who to exclude. Without it, contacts with red-flag attributes can still accumulate enough points from behavioral signals to hit your MQL or SQL threshold. You end up sequencing to interns, competitors, and personal email addresses.
Building the Negative Score column
Add a formula column called Negative Score that deducts points for disqualifying signals:
let deduction = 0;
const email = ({{Email}} || "").toLowerCase();
const title = ({{Job Title}} || "").toLowerCase();
const domain = ({{Company Domain}} || "").toLowerCase();
// Personal email domains
if (["gmail.com", "yahoo.com", "hotmail.com", "outlook.com"]
.some(d => email.endsWith(d))) deduction -= 15;
// Non-buyer titles
if (["intern", "student", "coordinator", "assistant"]
.some(t => title.includes(t))) deduction -= 20;
// Competitor domains. replace with your actual competitors
if (["competitor1.com", "competitor2.com", "competitor3.com"]
.some(d => domain.includes(d))) deduction -= 50;
return deduction;
Update your Total Score column to include Negative Score:
return Number({{Job Title Score}}) + Number({{Headcount Score}}) +
Number({{Funding Score}}) + Number({{Behavioral Score}}) +
Number({{Negative Score}});
A contact with a personal email (-15) who's an intern (-20) gets -35 deducted. If they scored 40 on positive signals, they end at 5. No false positive, no sequencer slot wasted.
Lead decay
Behavioral signals have a shelf life. A pricing page visit from eight months ago says much less about current intent than one from last week. Without decay, old signals stay in the score indefinitely. Contacts who looked interested months ago remain in your "hot leads" view and take up sequencer capacity.
Build a Decay Score column that deducts points based on time since the contact's last engagement:
const lastDate = new Date({{Last Engagement Date}} || "");
const now = new Date();
const daysSince = isNaN(lastDate.getTime()) ? 999 :
Math.floor((now - lastDate) / (1000 * 60 * 60 * 24));
if (daysSince > 90) return -20;
if (daysSince > 60) return -10;
return 0;
Pull Last Engagement Date from HubSpot or Salesforce via Clay's CRM enrichment. Add Decay Score to your Total Score formula.
Set up a weekly Clay automation (or a scheduled Zapier trigger) to refresh the Decay Score column for all contacts. This keeps scores current as time passes. Calibrate the decay thresholds to your average sales cycle: if you typically close within 30 days of first engagement, 60-day inactivity should trigger a meaningful deduction. If your cycles run 90-120 days, push the first decay threshold to 120+ days.
Step 5: Route High-Score Leads to Your Sequencer
The scoring model is only useful if it feeds an action. When a contact hits the SQL threshold, something should happen without a human checking the Clay table. That's what Clay automations handle.
Setting up the Clay automation
In Clay, open the Automations panel and create a new automation with the trigger: Status changes to "SQL". Add a condition: ICP_Pass = "ICP_PASS". This fires once per contact when they first qualify, not on every table refresh.
Clay has native integrations with Instantly, Smartlead, Outreach, and Salesloft. Select your sequencer as the action. Map the payload fields:
- First Name
- Last Name
- Company Name
- Job Title
- Sequence ID (the ID of the specific sequence you want them to enter)
Using scoring signals as personalization tokens
Don't pass only the contact fields. Pass the individual scoring signal columns as custom variables. The fields that made a contact qualify are the most relevant context for the opening line of your email.
For example:
- A column
Top Intent Signalthat returns"requested a demo","visited pricing page", or"compared competitors"based on which behavioral flag is highest {{Funding Stage}}(so you can reference their growth stage){{Job Title Score}}as a proxy for seniority tier
In Instantly or Smartlead, reference these as custom variables in your first-line variable. A contact who requested a demo gets a different opener than one who only visited the pricing page. The scoring model gives you the data; the personalization tokens put it in the email.
For more on sourcing intent signals before they reach the scoring model, see the guide on capturing buying signals for B2B sales.
Syncing scores back to your CRM
Clay's reverse ETL lets you push columns back to HubSpot or Salesforce. Set up a sync that writes Total Score and Status to contact properties in your CRM. This gives reps visibility into why a contact was routed to them, without requiring them to log into Clay.
In HubSpot, build a contact list view showing Clay Score and Clay Status columns. Reps can sort by score, see the signals behind it, and flag contacts that scored high but shouldn't be sequenced. That feedback closes the loop for threshold calibration: when reps mark contacts as "bad fit" or "wrong timing," those flags become the calibration data for your next threshold review.
Where Miniloop Fits in Clay Lead Scoring Workflows
Clay handles the enrichment and the scoring mechanics. But running the full lead scoring loop involves more than those two steps. The busywork: determining which of Clay's 100-plus data providers to call in what order for best coverage at lowest API cost, writing and debugging formula columns that return clean results across messy real-world data, maintaining threshold rules as your ICP shifts, auditing coverage gaps when a field comes back null for 40% of your list, re-scoring stale contacts when you update model weights, and monitoring whether the MQL-to-SQL conversion rate has drifted over time.
Miniloop handles that busywork. We build and run outbound GTM workflows for your team:
- Model setup from your ICP definition. Describe who you're targeting. We configure the ICP filter columns, the scoring columns, and the threshold logic. No formula-writing on your side.
- Data provider waterfall configuration. We set up the right provider call order (Apollo first for basic firmographics, Clearbit for enriched company data, Crunchbase for funding stage) to maximize coverage without redundant API spend.
- Threshold maintenance as your ICP evolves. When you move upmarket, shift your target vertical, or change your product focus, the scoring weights and thresholds need to update. We handle that on an ongoing basis.
- Weekly conversion rate monitoring. We track the ratio of MQL-to-SQL, SQL-to-reply, and reply-to-meeting. When a ratio drops below baseline, we flag it and diagnose whether it's a threshold problem, a data coverage gap, or a sequencer configuration issue.
- Sequencer integration with personalization tokens. We connect the scoring output to your sequencer and wire individual signal columns to email template variables, so every contact gets a relevant opener based on the signal that made them qualify.
Whether you're building the model from scratch, inheriting a Clay table someone else set up, or scaling a model that's already working, Miniloop handles the execution layer.
Try Miniloop or browse templates.
Clay Lead Scoring Mistakes to Avoid
Starting with too many criteria
Building an exhaustive model from day one is tempting: 15 firmographic signals plus 10 behavioral events. In practice, models with many criteria are harder to calibrate and harder to debug when results look wrong. Start with 5-7 criteria: two or three firmographic, two or three behavioral, one or two negative. Add more only after you've seen conversion data from the initial model and know which signals actually predict outcomes.
Skipping the ICP filter
Scoring non-ICP contacts doesn't produce useful rankings. It produces noise. If your list contains 5,000 contacts and 2,000 of them are outside your ICP, scoring all 5,000 dilutes your conversion data and fills your sequencer with the wrong people. Build the ICP_Pass filter first. Only score contacts who pass it.
Treating thresholds as permanent
A threshold set at launch and never reviewed stops predicting conversions within months. Your ICP definition shifts. Your sequencer copy changes. The market changes. Any of these can shift where "good leads" cluster in your score distribution. If a threshold was calibrated when your product cost $500/month and you've since moved to $5,000/month contracts, the signals and weights need to reflect the longer sales motion.
Scoring on columns with poor data coverage
Before adding a field to the scoring model, check how often it's populated across your actual list. If Funding Stage comes back null for 60% of your contacts, a funding-stage score column adds noise rather than signal. It also makes scores incomparable: a contact with a funding stage gets scored on four criteria; one without gets scored on three, and there's no way to tell the difference from the total alone. Audit coverage before building the column. If coverage is poor, either find a better data provider for that field or drop it from the model.
Missing the sales feedback loop
A scoring model without sales feedback is a model that drifts. Reps know which contacts were actually good after the first call or the second email. If reps can't flag contacts in the CRM as "bad fit," "wrong timing," or "already a customer," you have no signal to calibrate the model against. Build that CRM property before you launch the sequencer integration. Review the flags in your monthly threshold calibration.
For teams scoring accounts rather than individual contacts, the same principles apply with adjustments for account-level signals. See the account scoring guide for how to adapt this model.
Related Reading
- ICP Scoring Methodology for B2B Sales: A Step-by-Step Guide
- How to Build a Lead Enrichment Workflow in Clay: Step-by-Step Guide for B2B Teams in 2026
- Clay Platform Use Cases: Lead Enrichment, Scoring, Routing, and List Building
- HubSpot vs Marketo vs Pardot vs ActiveCampaign: Lead Nurturing & Scoring Compared (2026)
Related Resources
- Templates - workflow templates index
- Integrations - integrations index
- AI Automation Tools - Connect your apps and automate with AI
- AI Agent Platform - Build and deploy autonomous AI agents
Frequently Asked Questions
How many scoring criteria should I start with in Clay?
Start with 5-7 criteria: two or three firmographic signals (job title, headcount, funding stage), two or three behavioral signals (demo request, pricing page visit, webinar registration), and one or two negative signals (personal email, competitor domain). A smaller model is easier to calibrate against real conversion data. Add more criteria after you've run the model for a few weeks and can see which signals actually predict replies and meetings.
What is a good MQL threshold to use in Clay lead scoring?
A starting range of 40-50 points works for MQL if you're using the scoring weights in this guide (C-level title = 30, demo request = 30, headcount fit = 20). At those weights, a contact needs at least one strong signal or a combination of moderate signals to hit the MQL floor. Adjust based on your conversion data: if contacts who score 40-50 rarely convert, raise the threshold to 55-60. If your sequencer is under-filled, lower it to 30-35.
Can Clay score leads based on intent data?
Yes, but Clay doesn't generate intent data itself. You pull intent signals from your CRM (HubSpot, Salesforce), a customer data platform, or a third-party intent provider like Bombora or G2, and bring those signals into Clay as columns. Once a signal is a column. `Pricing Page Visit = true`, `Competitor Category Active = true`. you can assign point values to it in a formula column the same way you score firmographic data.
How do I handle lead decay in Clay without native time-decay features?
Build a `Decay Score` formula column that calculates days since last engagement using a date field pulled from your CRM, then returns a negative point value based on how long the contact has been inactive. Run a weekly Clay automation or a Zapier scheduled trigger to refresh that column so the deduction updates over time. Add the `Decay Score` to your `Total Score` formula. Clay doesn't have a built-in time-decay function, but this pattern replicates the same effect using scheduled refreshes.
Do I need a paid Clay plan to build a lead scoring model?
The formula columns, filtering, and table views used in this guide are available on Clay's paid plans. You also need credits to run enrichment (pulling company data from Apollo, Clearbit, or Crunchbase, and activity data from HubSpot). The automation feature that fires when a contact's status changes to SQL requires a plan that includes Clay automations. Check Clay's current pricing page for the latest plan details, as the feature set by tier changes with their product updates.



