A merge is only as good as the rule that picked each surviving value. Set those rules first, and stop the leaks before you touch the backlog.
Dana Whitfield
Northwind Logistics · post-merger org
Toggle to see the same person before and after a rules-based merge.
Duplicates aren't a cleanup problem.
They're a creation-path problem you clean up after.
Key: contact email
An existing email updates that contact. Personal emails and typos still create new people.
Key: email, domain or Record ID
Rows with no matching key create new records. Re-running a file without Record IDs doubles them.
Key: none for companies
Per HubSpot, companies created through the API, including third-party apps, aren't deduplicated by domain.
Key: contact email domain
One company per unique domain. Subdomains such as info.acme.com get their own company.
Key: your matching rules
Integrations can save through alerts with allowSave, and exempted integration users skip rules entirely.
Key: your matching rules
Duplicate rules alert or block. Quick Create and lead conversion skip them unless configured.
"I turned off company creation in the sequencing tool and it's still happening."
HubSpot's own "Create and associate companies with contacts" setting, imports and other apps keep creating companies. HubSpot advises against that setting when the Salesforce integration is on.
"A few hundred thousand records across Leads, Contacts and Accounts."
Two orgs, two owners, two spellings. Native Salesforce merge handles three records at a time, with no bulk field rules.
"In Salesforce they all collapse into one account."
Independent agents and franchisees share a brand and email domain. Domain-only matching merges customers that should be linked in a hierarchy.
"How do Pardot opt-outs and prospect records behave when we merge?"
HubSpot can't unmerge and drops the losing record's conflicting values. In Salesforce, consent flags and finance-linked accounts break if the master's values win by default.
"Budget is a bit challenging right now, so I'm gathering stories for a business case."
Duplicates cost contact-tier fees, enrichment credits, rep hours and misrouted leads every month. Without those numbers in a model, dedupe stays a someday project. The calculator in section 7 runs them.
Benchmarks: weak, acceptable, strong
| Metric | Weak | Acceptable | Strong |
|---|---|---|---|
| Duplicate rate per object (records in an open duplicate set ÷ total) | >10% | 3–10% | <3% |
| New duplicates per 1,000 records created per month | >50 | 10–50 | <10, each traced to a source |
| False-positive rate in the merge-review sample | >5% | 1–5% | <1% before any auto-merge |
| CRM data accurate and complete | <50% (76% of orgs) | 50–80% | >80% |
| Team time spent fixing and reconciling data | 6+ hrs/week (nearly a third of teams) | 2–5 hrs/week | <2 hrs/week, mostly queue review |
| Lead-to-account match accuracy (business email leads) | <70% | 70–90% | >90% (vendor-reported ceiling: 95%) |
| Opt-outs preserved through merges | Any loss | Spot-checked | 100%, reconciled against a pre-merge export |
Sources: Validity, The State of CRM Data Management in 2025 (n=602) and 2026 (n=500, Aug 2026); LeanData, Lead-to-Account Matching reference (Jun 2026, vendor-reported 95% match accuracy); HubSpot Knowledge Base, Merge records (2026); Salesforce merge limits via RizeX Labs and Brian Hong (2026). Duplicate-rate, new-duplicate and false-positive bands are JayOh practitioner targets, not published studies.
List every system and setting that creates leads, contacts, accounts or companies.
Merging while leaks run resets the clock. You also can't tell new duplicates from old ones.
Record source and Record source detail 1 to see which app, import or form created each one.CreatedById and LeadSource for 90 days, and list integration users that send allowSave or skip duplicate rules. Connector settings: our HubSpot-Salesforce integration guide.| Symptom in HubSpot | Likely cause | Fix |
|---|---|---|
| New companies with only a domain and no owner | Auto-create from contact email domain | Turn off the setting, or exclude the domain |
acme.com and mail.acme.com as two companies | Subdomains count as separate domains | Normalize to the root domain; merge; add secondary domains |
| Same domain, two companies, record source "Integration" | API-created companies skip domain dedupe | Make the app search by domain before create, or stop it creating companies |
| Duplicates appear after every list upload | Imports without Record ID or domain | Require a Record ID column for updates |
A full export, plus stored, normalized copies of the fields you'll match on.
The export is your only undo. Raw values like "www.Acme.com/" never match "acme.com."
HasOptedOutOfEmail and Account Engagement fields; in HubSpot, include Record IDs.Email_Normalized__c, Website_Domain__c (no protocol, www. or path), Phone_E164__c, Name_Normalized__c (no Inc, LLC, Ltd).Company domain name; alternates go to secondary domains.Rules per object that separate certain matches from probable and possible ones.
One broad fuzzy rule either misses duplicates or flags thousands of false positives you then have to review.
info@-style emails.A written survivorship spec: which record is master and which value wins for each field.
Default merges keep the master's value and discard the other. In HubSpot that discard is permanent.
Native tools for prevention, plus a tool or Apex batch for the backlog.
At three records per merge, a few hundred thousand records is not a click-through job.
| Tool | Where it runs | Matching | Best at | Watch for |
|---|---|---|---|---|
| Plauti Deduplicate (formerly Duplicate Check) | Native managed package; optional cloud processing for big jobs | Rules: weighted fields, thresholds | Real-time prevention, scheduled batch jobs, Flow actions, cross-object | Rules need tuning; no merge undo; quote-based, unlimited seats |
| DemandTools (Validity) | Web app (Elements) plus full edition | Rules and scenarios, exact to fuzzy | Mass merge with field-level survivorship; power users | Learning curve; seat-based pricing |
| DataGroomr | Web app outside the org | Pre-trained machine learning | Messy data with no clean key; 14-day undo | Review ML suggestions; from $1,195/yr, record-based |
| Cloudingo | Connected app with indexing | Rules, fuzzy and exact | Scheduled scans, import dedupe, undo | Standard $2,500/yr, Professional $6,000/yr; unlimited users |
| LeanData | Native managed package | Lead-to-account match, filters and tiebreakers | Matching and routing new leads to the right account | A router, not a bulk merge tool |
Database.merge over your Duplicate Record Sets.Pricing from vendor and comparison pages (DataGroomr, Apr 2026; Plauti, Jan 2026). Confirm quotes directly.
Run the rules and survivorship spec on a labeled sample in a full sandbox.
False positives are permanent once merged. The pilot sets your auto-merge threshold.
Separate merge paths for finance-linked accounts and for any cluster with an opt-out.
These are the merges that break the books or re-subscribe someone who unsubscribed.
Gotcha: merging in Account Engagement does not merge or delete the Salesforce records. Always merge on the Salesforce side and let the connector follow.
Accounts first, then contacts, then leads, one tier at a time.
Contacts, opportunities and cases re-parent to the master account. Merging contacts first can leave them on the losing account.
MasterRecordId set; export them daily as your audit trail.A parent account for the brand and a child account per independently operated location.
When agents share a brand domain, the domain identifies the brand, not the customer. Matching needs a location key.
Location_Key__c AG-20417Owner: SMB territory EastLocation_Key__c AG-31108Owner: SMB territory CentralLocation_Key__c (agent code, store ID, or normalized address plus phone) as an external ID on child accounts, and capture it at signup.Location_Key__c to the lead's, use LOGIC or SOQL tiebreakers instead of fuzzy name, and the Match to Parent edge for leftovers.Prevention rules, scheduled scans and a named owner for the queue.
Without an owner and a metric, duplicate rates drift back within a quarter.
Fix the source before the backlog. Close each creation path, confirm it's closed, then merge.
Consent is most-restrictive-wins. Opt-outs carry to the master no matter which record wins otherwise.
Finance owns billing-linked accounts. Certinia accounts with transactions follow the accounting merge path, never the bulk tool.
Normalize into stored fields. Formula fields can't drive every matching rule; Flow-maintained fields can.
Auto-merge only Tier A. Exact keys merge in bulk; fuzzy matches get a sample review first.
Merge accounts, then contacts, then leads. Related records follow the master account.
Pilot on 200 labeled pairs. Set thresholds from measured false positives, not vendor defaults.
Export losers every day. MasterRecordId plus a pre-merge export is your only real undo.
One creator per object per system. If two tools can create companies, you'll get two companies.
Roll out Report, then Alert, then Block. Block only manual entry; never block an integration user.
Exclude shared and personal domains. gmail.com and a franchise brand domain both identify nobody.
Report new duplicates by source monthly. A rising source is a broken integration, not a cleanup task.
The default merge keeps the master's Email Opt Out, even if a losing record had opted out.
CostEmails to people who unsubscribed: CAN-SPAM and GDPR exposure and spam complaints.
The backlog clears, the integration keeps creating, and the count climbs back.
CostPaying for the cleanup twice, and a team that stops trusting the next one.
Every location under one brand domain collapses into one account.
CostWrong owner, wrong territory, and customers' usage and billing mixed together.
Skipping validation and the balance update after the merge.
CostLive balances stay wrong until updated, and a currency mismatch undermines accounting data.
A Block rule returns DUPLICATES_DETECTED to the sync.
CostFailed syncs and leads that never arrive, which is worse than a duplicate.
"J. Smith" at two companies becomes one person.
CostA permanent false merge: wrong history, wrong owner, and no unmerge in HubSpot.
Your database
Run a Tier A report or HubSpot's duplicate tool to get a floor.
Contact tier and enrichment
Marketing contact price preset
Pro: 5,000 more contacts from $250/mo. Enterprise: 10,000 from $100/mo.
People and routing
Investment
36,000 duplicate records across four cost lines.
Where the money goes, per year
Calculating…
Formulas: duplicates = records × rate. Contact tier = duplicates × billed share × price × 12. Enrichment = duplicates × cost per record. Rep time = reps × hours × 46 working weeks × hourly cost. Routing = leads × rate × minutes ÷ 60 × hourly cost × 12. Savings assume cleanup removes 85% of these costs, minus the yearly tool cost. Contact prices are HubSpot list add-on rates (Encharge, Oct 2026). Rep hours and fix minutes are your estimates; use your own time study.
Copies all three tables as tab-separated text.
AMatching rule spec
| Object | Tier | Criteria | Method | Action |
|---|---|---|---|---|
| Lead / Contact | A | Email_Normalized__c | Exact; role emails excluded | Bulk merge (lead into contact: convert) |
| Lead / Contact | B | First Name + Last Name + (Phone_E164__c OR Account/Company) | Fuzzy name, exact phone | Merge after sample review |
| Account | A | Website_Domain__c + Billing Country + Name_Normalized__c | Exact; shared/franchise domains excluded | Bulk merge unless finance-linked |
| Account | B | Account Name + Billing Postal Code + Phone | Fuzzy name, exact postal and phone | Merge after sample review |
| Account | C | Account Name + Billing State | Fuzzy | Steward queue only |
| Account (multi-location) | A | Location_Key__c | Exact external ID | Never merge across keys; link to parent |
| HubSpot company | A | Company domain name (root, primary) | Exact | Bulk merge; add secondary domains |
BSurvivorship spec: field-by-field winner rules
| Field or decision | Winner rule | Tiebreaker | Approver |
|---|---|---|---|
| Master record | Has finance transactions > open opportunity > most related records | Oldest created date | RevOps + Finance |
| Email Opt Out / Opted Out | True if any record is true | None: most restrictive always | Marketing + Legal |
| Do Not Email, Do Not Call | True if any record is true | None | Marketing + Legal |
| Most recent engagement | Business domain over personal | Marketing | |
| Owner | Owner of the master account | Owner with open opportunity | Sales leadership |
| Billing address, payment terms, tax | Billing system of record | Finance decides manually | Finance |
| Phone, title, mailing address | Most recently updated non-blank | Most complete record | RevOps |
| Industry, employees, revenue | Enrichment source of record | Most complete record | RevOps |
| Lead Source / Original Source | Oldest value | Keep both in a notes field | Marketing |
| Lifecycle / Lead Status | Furthest stage | Most recent change | Marketing + Sales |
CDedupe runbook checklist
| Phase | Check | Owner | Done when |
|---|---|---|---|
| Before | Every creation path listed; one creator per object | RevOps | New duplicates by source trending to zero |
| Before | Full export of all objects, opt-out fields and IDs stored | RevOps | Files archived with date |
| Before | Survivorship spec signed by sales, finance and marketing | RevOps lead | Written approval on file |
| Before | Certinia-linked accounts and opted-out clusters flagged and excluded from bulk runs | Finance + Marketing ops | Exclusion field populated |
| Before | Pilot on ~200 labeled pairs in a full sandbox | Data steward | False positives <1% for auto-merge tier |
| During | Accounts, then contacts, then leads; Tier A before Tier B | RevOps | Each batch log reviewed |
| During | Losers exported daily with MasterRecordId | RevOps | Audit file per batch |
| After | Opted-out emails reconciled against the pre-merge export | Marketing ops | Zero opt-outs lost |
| After | Certinia balances updated for every merged master | Finance | Balance update emails confirm success |
| After | Duplicate rules moved Report > Alert > Block (UI only) | Salesforce admin | Weekly steward queue live |
Start from constraints. Data can't leave the org: Plauti, which runs natively. Power users who want field-level survivorship: DemandTools. No clean match key: DataGroomr's machine learning. Tight budget with scheduled scans: Cloudingo. Whichever you pick, run the 200-pair pilot from step 6 on your own data before you sign.
With "automatically delete or merge prospects" on (the AMPSEA default), a Salesforce merge deletes the losing prospect and updates the survivor. The survivor's opt-out is whatever the merged Salesforce record holds, so set Email Opt Out to true first if anyone in the cluster opted out, then reconcile against a pre-merge export.
Merge them, but only through Certinia's three-step process: validate in the Merge Accounts tab, merge in Salesforce, then run Update Balances. Accounts in different trading currencies, with incomplete documents or payment proposals, or tied to a company (intercompany) fail validation. Leave those for finance to resolve by hand.
Something else is creating them. Usually it's HubSpot's "Create and associate companies with contacts" setting, which makes a company per email domain and treats subdomains as separate. Next most likely: another integration (API-created companies skip domain dedupe) or imports without a domain. Record source detail 1 on the duplicates tells you which.
Make the brand a parent account and each location a child account with a location key, such as an agent code, store ID or normalized address plus phone. Capture that key at signup and match on it first. Domain-only matches go to the parent and a steward queue, so the manual part shrinks to the leftovers.
Not in HubSpot: merges are permanent, and conflicting values from the losing record aren't stored. In Salesforce, merged records sit in the Recycle Bin for 15 days, but the merge itself can't be reversed cleanly. Your real undo is a pre-merge export plus a daily export of losers with their MasterRecordId.
Price what duplicates already cost (contact-tier fees, enrichment, rep hours, misrouted leads), compare it to cleanup and tool cost, and show payback in months. The section 7 calculator writes the summary for you. Post-merger, we run dedupe inside a roughly 12-week add-on integration rather than as a separate project.
We trace every duplicate pair to the integration, import, form or setting that made it, and close those paths first.
Sales, finance and marketing approve the master and field rules in writing before a single record merges.
Billing-linked accounts follow the accounting merge path. Opt-outs are carried over and reconciled after every batch.
Locations, brands and business units are linked rather than merged; real duplicates still merge.
When CRM, billing and ERP all hold customers, we map duplicate lineage in a warehouse such as Snowflake before deciding what merges.
For buy-and-build companies, dedupe becomes a standing capability that runs on every add-on, not a one-time project.
We'll review your creation paths, matching rules and survivorship spec, and show you which leak to close first.
We engineer growth systems. JayOh builds the RevOps architecture, GTM execution and data infrastructure that turn messy revenue operations into predictable pipeline for B2B SaaS and PE-backed teams.