Why generic ICP filters fail
Your ICP doc says "Series B SaaS, 100-500 employees." So does everyone else's. There is no differentiation in the signals you score on.
The question that matters: what is actually different between your closed-won accounts and your closed-lost accounts? Not what you think. What the data shows.
How to discover your niche signals
Step 1: Export two lists
Pull from Salesforce or HubSpot:
closed-won.csv(50 accounts that closed)closed-lost.csv(50 accounts that did not)
Step 2: Run the niche signal discovery skill
The skill authors one durable Play that enriches both cohorts, preserves evidence, and compares the same frozen contract across won and lost accounts:
deepline plays search "won lost signal discovery firmographics technographics hiring" --json
# Claude Code authors signal-discovery.play.ts with one frozen contract for both cohorts.
deepline plays check signal-discovery.play.ts
deepline plays run --file signal-discovery.play.ts --input @pilot-cohorts.json --watch
Step 3: Review the signals
Claude analyzes both cohorts and surfaces the 3-5 attributes that statistically separate winners from losers.
Step 4: Deploy as a scoring model
Take the discovered signals and apply them as a Deepline workflow that scores incoming HubSpot contacts.
The discovered signals become a webhook-bound scoring Play. Its definePlay
options declare and authenticate the trigger:
{
webhook: {
auth: {
type: 'standard-webhooks',
headerFamily: 'standard',
signingSecrets: ['INBOUND_RELAY_WEBHOOK_SECRET'],
toleranceSeconds: 300,
},
},
}
Use an authenticated relay that verifies HubSpot's v3 signature, then signs the
forwarded body with Standard Webhooks v1 headers. Reuse the same webhook-id
when retrying the same event. Then store the Play-scoped signing secret, publish,
and verify the Play:
deepline secrets set INBOUND_RELAY_WEBHOOK_SECRET --scope play --play icp-scoring
deepline plays check icp-scoring.play.ts
deepline plays publish icp-scoring.play.ts
deepline plays describe icp-scoring --json
deepline plays versions --name icp-scoring --json
Sample analysis: Cybersecurity ICP
Sample analysis. Numbers are illustrative; your results will vary.
A sample / illustrative fraud prevention company ran niche signal discovery on 84 closed-won and 85 closed-lost accounts. Here's what the data showed.
Positive-fit signals (predict closed-won)
| Signal | Lift | Why it matters |
|---|---|---|
| Account creation flow on website | 11.1x | Company has consumer onboarding - needs identity verification |
| Adyen in tech stack | 5.1x | Enterprise payment infrastructure - high-volume, sophisticated |
| Braze in tech stack | 3.7x | Lifecycle messaging - large user base to verify |
| CIP language on website | 3.0x | BSA-mandated identity verification at onboarding |
| Developer sandbox in docs | 2.5x | API-first buyer - faster integration |
| Fraud leadership hiring | 1.8x | Active fraud budget - decision maker exists |
Anti-fit signals (predict closed-lost)
| Signal | Lift | Why it predicts a loss |
|---|---|---|
| Kubernetes + Terraform + Docker (all 3) | 0.06x | Heavy DevOps - internal build culture, won't buy |
| Adverse media screening | 0.08x | Advanced AML stack - existing IDV solution |
| SOC 2 Type II (prominently marketed) | 0.14x | Strict vendor certification - slow procurement |
| Snowflake (prominent) | 0.20x | Data warehouse focus - internal ML/analytics culture |
| Azure as only cloud | 0.22x | Microsoft ecosystem lock-in - prefer Microsoft tools |
| IPO in progress | 0.40x | Procurement frozen during lock-up period |
The scoring model
Signals get weighted into a lead score:
| Score | Tier | Action |
|---|---|---|
| 60-100 | Tier 1 | Immediate outreach - personalized sequence referencing their fraud/compliance signals |
| 35-59 | Tier 2 | Compliance-led messaging - sequence on triggering event |
| <35 | Tier 3 | Nurture or skip - likely not a fit today |
From signals to outreach
Once you have the signals, Deepline finds the companies and contacts that match.
Step 1: Resolve the stage graph
deepline plays search "company to contacts" --json
deepline plays describe prebuilt/company-to-contact --json
deepline plays describe prebuilt/name-and-domain-to-email-waterfall --json
deepline plays bootstrap company-people-email --from csv:tam-companies.csv \
--people play:prebuilt/company-to-contact \
--email play:prebuilt/name-and-domain-to-email-waterfall \
--limit 500 --out signal-outreach.play.ts
# Complete every generated TODO mapping, select the people output rows field,
# and remove the scaffold's guard throws before checking or running it.
Step 2: Check and pilot the owned Play
deepline plays check signal-outreach.play.ts
deepline plays run --file signal-outreach.play.ts --limit 3 --watch
Step 3: Run the accepted route
deepline plays run --file signal-outreach.play.ts --limit 500 --watch
What gets enriched
| Data type | Sources |
|---|---|
| Firmographics | Crustdata, People Data Labs |
| Technographics | BuiltWith, TheirStack |
| Hiring signals | Crustdata job postings |
| Funding | Crunchbase via Crustdata |
| Web research | Exa semantic search |
| Emails | BetterContact, Fullenrich waterfalls |
| Outreach | Lemlist, Smartlead, Instantly |
Cost breakdown
| Stage | Credits | Cost |
|---|---|---|
| Signal discovery (100 won + 100 lost) | 80-120 | ~$0.80-1.20 |
| Score TAM list (500 companies) | 100-150 | ~$1-1.50 |
| Find contacts at Tier 1 (50 companies) | 25-40 | ~$0.25-0.40 |
| Email enrichment (150 contacts) | 30-60 | ~$0.30-0.60 |
| Full pipeline | 235-370 | ~$2.35-3.70 |
From raw CRM export to scored outreach list for under $40.
Who uses this
- Founders who want to stop guessing which accounts to prioritize
- RevOps leads building scoring models that predict revenue
- AEs who want to know which accounts are worth their time
Deploy ongoing scoring
Once you have the signals, deploy them as an always-on workflow:
Schedule the niche-signal-discovery scoring to run daily on new HubSpot contacts. For Tier 1 hits, enrich emails with BetterContact and push to my Lemlist campaign 'tier1-fraud-signals'.
Claude Code adds the schedule to the same definePlay options before
republishing:
{
cron: { schedule: '0 8 * * *', timezone: 'America/New_York' },
}
deepline plays check icp-scoring.play.ts
deepline plays publish icp-scoring.play.ts
deepline plays describe icp-scoring --json
deepline plays versions --name icp-scoring --json
New leads get scored, enriched, and sequenced automatically. Signals stay tied to real outcomes, not assumptions.
Common questions
1How many accounts do I need?+
50 of each is enough to find patterns. 100+ gives stronger signal.
2What if my won/lost sets are imbalanced?+
Deepline normalizes for sample size. 30 won and 70 lost still works.
3Can I re-run this quarterly?+
Yes. Your ICP evolves as you close more deals. Re-running keeps signals fresh.