Skip to content

Lead Matching

Match sales leads across Salesforce, HubSpot, and LinkedIn Sales Navigator to eliminate duplicates, consolidate lead intelligence, and route the richest profile to your sales team.

The Problem

Your sales team uses three lead sources: inbound leads captured in HubSpot from marketing campaigns, outbound prospects in Salesforce from the SDR team, and enrichment data from LinkedIn Sales Navigator. The same prospect often enters all three systems:

  • A prospect fills out a form on your website (HubSpot) and an SDR independently reaches out on LinkedIn (Salesforce). Neither team knows the other has made contact.
  • LinkedIn enrichment data uses the prospect's full legal name ("Robert James Smith"), while the CRM has the name they signed up with ("Bob Smith").
  • Company names vary between systems: "Acme Corp" in the CRM, "Acme Corporation" in LinkedIn, "ACME" in the marketing form.

Without deduplication, your SDR sends a cold email to someone who is already an active marketing lead. The prospect sees two outreach emails from the same company and loses trust.

The Data

Salesforce (Outbound Prospects)

lead_idnameemailcompanytitlephonelead_sourcecreated_date
sf_lead_001Bob Smith[email protected]Acme CorpVP Engineering+1-555-0201outbound2026-01-08
sf_lead_002Jenny Lee[email protected]GlobexProduct Managernulloutbound2026-01-10
sf_lead_003Michael Chen[email protected]InitechCTO+1-555-0203referral2026-01-12
sf_lead_004Sarah Connor[email protected]Cyberdyne SystemsDirector of Ops+1-555-0204outbound2026-01-14
sf_lead_005Dave Wilson[email protected]Umbrella CorpHead of Data+1-555-0205inbound2026-01-15
sf_lead_006Amy Zhang[email protected]Soylent CorpData Engineernulloutbound2026-01-16
sf_lead_007Tom Harris[email protected]Wayne EnterprisesVP Sales+1-555-0207referral2026-01-17

HubSpot (Inbound Marketing Leads)

contact_idfull_nameemail_addresscompany_namejob_titlephone_numberlead_scoreform_submitted
hub_44201Robert Smith[email protected]Acme CorporationVP of Engineeringnull85demo_request
hub_44202Jennifer Lee[email protected]Globex CorporationPM+1-555-030272whitepaper
hub_44203Mike Chen[email protected]Initech IncChief Technology Officer+1-555-020391demo_request
hub_44204S. Connor[email protected]CyberdyneDir. Operations+1-555-020445newsletter
hub_44205Lisa Park[email protected]Oscorp IndustriesData Analyst+1-555-030568whitepaper
hub_44206Amy Z.[email protected]Soylentnull+1-555-030634newsletter
hub_44207David Wilson[email protected]Umbrella CorporationHead of Data Engineering+1-555-020577case_study

LinkedIn Sales Navigator (Enrichment)

profile_iddisplay_namework_emailorganizationheadlinelocationconnections
li_prof_901Robert James Smith[email protected]ACMEVP Engineering at ACMESan Francisco, CA1247
li_prof_902Jennifer Lee[email protected]Globex CorporationProduct Manager at GlobexNew York, NY832
li_prof_903Michael J. Chen[email protected]Initech, Inc.CTO at InitechAustin, TX2103
li_prof_904Sarah J. Connor[email protected]Cyberdyne Systems IncDirector of OperationsLos Angeles, CA654
li_prof_905Thomas Harris[email protected]Wayne EnterprisesVP Sales at Wayne EnterprisesChicago, IL1876
li_prof_906Patricia Nguyen[email protected]Stark IndustriesHead of AnalyticsSeattle, WA943

Data Challenges by Prospect

ProspectSalesforceHubSpotLinkedInChallenge
Bob/Robert Smith[email protected][email protected][email protected]3 different emails, name as "Bob" / "Robert" / "Robert James"
Jenny/Jennifer Lee[email protected][email protected][email protected]3 different emails, "Jenny" / "Jennifer" / "Jennifer"
Michael Chen[email protected][email protected][email protected]3 different emails, "Michael" / "Mike" / "Michael J."
Sarah Connor[email protected][email protected][email protected]3 different emails, "Sarah" / "S." / "Sarah J."
Dave/David Wilson[email protected][email protected]not in LinkedIn2 different emails, "Dave" / "David"
Amy Zhang[email protected][email protected]not in LinkedIn2 different emails, name truncated in HubSpot
Tom Harris[email protected]not in HubSpot[email protected]2 different emails, "Tom" / "Thomas"
Lisa Parknot in Salesforce[email protected]not in LinkedInOnly in HubSpot (singleton)
Patricia Nguyennot in Salesforcenot in HubSpot[email protected]Only in LinkedIn (singleton)

Not a single prospect has the same email address across all their sources. Exact email matching alone would fail to deduplicate any of these leads.

The Spec

yaml
api_version: kanoniv/v1
identity_version: "1.0"

entity:
  name: lead
  description: "Sales lead deduplication across CRM, marketing, and enrichment platforms"

sources:
  - name: salesforce
    adapter: csv
    location: data/salesforce_leads.csv
    primary_key: lead_id
    attributes:
      name: name
      email: email
      company: company
      title: title
      phone: phone
      lead_source: lead_source
      created_date: created_date

  - name: hubspot
    adapter: csv
    location: data/hubspot_contacts.csv
    primary_key: contact_id
    attributes:
      name: full_name
      email: email_address
      company: company_name
      title: job_title
      phone: phone_number
      lead_score: lead_score
      form_submitted: form_submitted

  - name: linkedin
    adapter: csv
    location: data/linkedin_navigator.csv
    primary_key: profile_id
    attributes:
      name: display_name
      email: work_email
      company: organization
      title: headline
      location: location
      connections: connections

rules:
  # Email exact match. Despite different email addresses per person,
  # some pairs may share an email (e.g., future data loads).
  # Weight is highest because email is the most reliable identifier.
  - name: email_exact
    type: exact
    field: email
    weight: 1.0

  # Fuzzy name matching is critical here because every prospect
  # has name variations. Jaro-Winkler at 0.88 catches:
  #   "Bob Smith" vs "Robert Smith" -> 0.78 (MISS -- below threshold)
  #   "Jennifer Lee" vs "Jenny Lee" -> 0.89 (HIT)
  #   "Michael Chen" vs "Mike Chen" -> 0.86 (MISS -- just below)
  #   "Michael Chen" vs "Michael J. Chen" -> 0.93 (HIT)
  # Some pairs rely on corroborating rules to bridge the gap.
  - name: name_fuzzy
    type: similarity
    field: name
    algorithm: jaro_winkler
    threshold: 0.88
    weight: 0.6

  # Company name fuzzy match corroborates name matches.
  # "Acme Corp" vs "Acme Corporation" -> 0.92 JW (HIT)
  # "Acme Corp" vs "ACME" -> 0.76 JW (MISS)
  # "Globex" vs "Globex Corporation" -> 0.85 JW (MISS)
  # "Initech" vs "Initech Inc" -> 0.91 JW (HIT)
  - name: company_fuzzy
    type: similarity
    field: company
    algorithm: jaro_winkler
    threshold: 0.90
    weight: 0.3

  # Phone exact match as a strong corroborating signal.
  # Bridges records when email and name both differ.
  - name: phone_exact
    type: exact
    field: phone
    weight: 0.8

blocking:
  strategy: phonetic
  keys: [name]

decision:
  scoring: weighted_sum
  thresholds:
    match: 0.85
    review: 0.55
  review_queue:
    enabled: true
    sla_hours: 336    # 14 days

survivorship:
  default: most_complete
  overrides:
    - field: email
      strategy: source_priority
      priority: [salesforce, hubspot, linkedin]
    - field: company
      strategy: source_priority
      priority: [linkedin, salesforce, hubspot]
    - field: name
      strategy: source_priority
      priority: [linkedin, salesforce, hubspot]

metadata:
  owner: [email protected]
  tags: [lead-matching, sales, dedup]
  description: "Cross-platform lead deduplication with review queue"

How the Rules Score Each Prospect

Bob/Robert Smith (Acme)

Pairemail_exactname_fuzzycompany_fuzzyphone_exactTotalDecision
SF / HubSpot0.00.0 (0.78 JW)0.3 (0.92 JW)0.0 (null)0.3Reject
SF / LinkedIn0.00.0 (0.72 JW)0.0 (0.76 JW)0.0 (no field)0.0Reject
HubSpot / LinkedIn0.00.6 (0.91 JW)0.0 (0.76 JW)0.0 (no field)0.6Review

"Bob Smith" vs "Robert Smith" fails the name threshold. But "Robert Smith" vs "Robert James Smith" scores 0.91. The HubSpot-LinkedIn pair enters the review queue at 0.6, and a human reviewer can confirm the match and pull in the Salesforce record transitively.

Jenny/Jennifer Lee (Globex)

Pairemail_exactname_fuzzycompany_fuzzyphone_exactTotalDecision
SF / HubSpot0.00.6 (0.89 JW)0.0 (0.85 JW)0.0 (null)0.6Review
SF / LinkedIn0.00.6 (0.89 JW)0.3 (0.92 JW)0.0 (no field)0.9Match
HubSpot / LinkedIn0.00.6 (0.95 JW)0.3 (0.92 JW)0.0 (no field)0.9Match

"Jenny Lee" vs "Jennifer Lee" scores 0.89 in Jaro-Winkler. LinkedIn and both CRM sources match. All three records cluster together.

Michael Chen (Initech)

Pairemail_exactname_fuzzycompany_fuzzyphone_exactTotalDecision
SF / HubSpot0.00.0 (0.86 JW)0.3 (0.91 JW)0.81.1Match
SF / LinkedIn0.00.6 (0.93 JW)0.3 (0.91 JW)0.0 (no field)0.9Match
HubSpot / LinkedIn0.00.0 (0.82 JW)0.3 (0.91 JW)0.0 (no field)0.3Reject

Phone match bridges SF-HubSpot despite "Michael Chen" vs "Mike Chen" being below the name threshold. SF-LinkedIn matches via "Michael Chen" vs "Michael J. Chen". HubSpot-LinkedIn ("Mike Chen" vs "Michael J. Chen") rejects, but transitive closure through Salesforce clusters all three.

Sarah Connor (Cyberdyne)

Pairemail_exactname_fuzzycompany_fuzzyphone_exactTotalDecision
SF / HubSpot0.00.0 (0.71 JW)0.0 (0.87 JW)0.80.8Review
SF / LinkedIn0.00.6 (0.92 JW)0.3 (0.95 JW)0.0 (no field)0.9Match
HubSpot / LinkedIn0.00.0 (0.68 JW)0.0 (0.87 JW)0.0 (no field)0.0Reject

"Sarah Connor" vs "S. Connor" scores only 0.71. But phone bridges SF-HubSpot into review, and SF-LinkedIn matches cleanly. The review queue catches the HubSpot record.

The Result

Match Summary

Sources:              3
Total records:        20
Clusters:             9
  - 3-record clusters: 3  (Jennifer Lee, Michael Chen, Sarah Connor)
  - 2-record clusters: 3  (Dave Wilson, Amy Zhang, Tom Harris)
  - Singletons:        3  (Lisa Park, Patricia Nguyen, Bob Smith partially)
Match pairs:          7
Review queue:         4
Rejected pairs:       9

Matched Leads (Automatic)

ClusterLeadSourcesConfidenceMatch Path
1Jennifer LeeSF + HubSpot + LinkedIn0.90name_fuzzy + company_fuzzy
2Michael ChenSF + HubSpot + LinkedIn0.93phone_exact (SF-HB) + name_fuzzy (SF-LI)
3Sarah ConnorSF + LinkedIn0.90name_fuzzy + company_fuzzy
4Dave WilsonSF + HubSpot0.86phone_exact + company_fuzzy
5Tom HarrisSF + LinkedIn0.88name_fuzzy + company_fuzzy

Review Queue

Candidate PairScoreReason for ReviewSignal
Bob Smith (SF) / Robert Smith (HubSpot)0.60company_fuzzy onlySame company, name below threshold
Robert Smith (HubSpot) / Robert James Smith (LinkedIn)0.60name_fuzzy onlyName match, company below threshold
Sarah Connor (SF) / S. Connor (HubSpot)0.80phone_exact onlyPhone match, name abbreviated
Amy Zhang (SF) / Amy Z. (HubSpot)0.56phone_exact onlyPhone match, name truncated

Consolidated Lead View (Jennifer Lee Example)

After survivorship with most_complete strategy and field overrides:

json
{
  "canonical_id": "canon_lead_002",
  "entity_type": "lead",
  "canonical_data": {
    "name": "Jennifer Lee",
    "email": "[email protected]",
    "company": "Globex Corporation",
    "title": "Product Manager",
    "phone": "+1-555-0302",
    "location": "New York, NY",
    "lead_score": 72,
    "connections": 832,
    "lead_source": "outbound",
    "form_submitted": "whitepaper"
  },
  "field_provenance": {
    "name": { "source": "linkedin", "strategy": "source_priority" },
    "email": { "source": "salesforce", "strategy": "source_priority" },
    "company": { "source": "linkedin", "strategy": "source_priority" },
    "title": { "source": "hubspot", "strategy": "most_complete" },
    "phone": { "source": "hubspot", "strategy": "most_complete" },
    "location": { "source": "linkedin", "strategy": "most_complete" },
    "lead_score": { "source": "hubspot", "strategy": "most_complete" },
    "connections": { "source": "linkedin", "strategy": "most_complete" },
    "lead_source": { "source": "salesforce", "strategy": "most_complete" },
    "form_submitted": { "source": "hubspot", "strategy": "most_complete" }
  },
  "cluster": {
    "size": 3,
    "sources": ["salesforce", "hubspot", "linkedin"],
    "confidence": 0.90
  }
}

The golden record combines the best data from each source: LinkedIn's authoritative name and company, Salesforce's email for outreach, HubSpot's engagement data, and LinkedIn's enrichment metadata.

Key Takeaway

Fuzzy matching plus a review queue catches matches that exact matching misses, while keeping humans in the loop for uncertain cases. In this dataset, zero email addresses match exactly across sources. Without fuzzy name and company matching, every single lead would remain a duplicate. The review queue with a low threshold (0.55) ensures borderline cases like "Bob" vs "Robert" are flagged for human decision rather than silently missed or incorrectly auto-merged.

The most_complete survivorship strategy is ideal for lead matching because you want the richest possible profile, with every field filled in from whichever source has it.

Variations to Consider

If you want to catch "Bob" vs "Robert" automatically, use Soundex or Metaphone:

yaml
- name: name_phonetic
  type: similarity
  field: name
  algorithm: soundex
  threshold: 1.0
  weight: 0.4

Soundex maps "Robert" and "Bob" differently (R163 vs B100), so this would not help for that specific case. For nickname resolution, consider a preprocessing step that normalizes common nicknames before matching.

If you need stricter matching (e.g., B2B where false positives are costly):

yaml
decision:
  thresholds:
    match: 0.95    # very high bar for auto-match
    review: 0.70   # send more to review

If you want to score by lead engagement, use an aggregate survivorship override:

yaml
survivorship:
  default: most_complete
  overrides:
    - field: lead_score
      strategy: aggregate
      function: max

The identity and delegation layer for AI agents.