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

Tell Claude Code
/niche-signal-discovery closed-won.csv closed-lost.csv

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

Tell Claude Code

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)

SignalLiftWhy it matters
Account creation flow on website11.1xCompany has consumer onboarding - needs identity verification
Adyen in tech stack5.1xEnterprise payment infrastructure - high-volume, sophisticated
Braze in tech stack3.7xLifecycle messaging - large user base to verify
CIP language on website3.0xBSA-mandated identity verification at onboarding
Developer sandbox in docs2.5xAPI-first buyer - faster integration
Fraud leadership hiring1.8xActive fraud budget - decision maker exists

Anti-fit signals (predict closed-lost)

SignalLiftWhy it predicts a loss
Kubernetes + Terraform + Docker (all 3)0.06xHeavy DevOps - internal build culture, won't buy
Adverse media screening0.08xAdvanced AML stack - existing IDV solution
SOC 2 Type II (prominently marketed)0.14xStrict vendor certification - slow procurement
Snowflake (prominent)0.20xData warehouse focus - internal ML/analytics culture
Azure as only cloud0.22xMicrosoft ecosystem lock-in - prefer Microsoft tools
IPO in progress0.40xProcurement frozen during lock-up period

The scoring model

Signals get weighted into a lead score:

ScoreTierAction
60-100Tier 1Immediate outreach - personalized sequence referencing their fraud/compliance signals
35-59Tier 2Compliance-led messaging - sequence on triggering event
<35Tier 3Nurture 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 typeSources
FirmographicsCrustdata, People Data Labs
TechnographicsBuiltWith, TheirStack
Hiring signalsCrustdata job postings
FundingCrunchbase via Crustdata
Web researchExa semantic search
EmailsBetterContact, Fullenrich waterfalls
OutreachLemlist, Smartlead, Instantly

Cost breakdown

StageCreditsCost
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 pipeline235-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:

Tell Claude Code

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.