# RevOps · Post 1 — Story

**Where to post:** r/revops, r/sales, RevGenius Slack, Modern Sales Pros,
Pavilion communities, LinkedIn (your own feed).

**Format:** ~400 words. Tactical war-story style. Don't pitch in the body.

---

## Title

We were paying HubSpot for 4,200 duplicate contacts. Here's the dedupe pipeline that caught them.

## Body

Last quarter I ran a count on our HubSpot instance: ~4,200 contacts that were almost-certainly the same person as another contact already in the system. Our HubSpot bill is per-marketing-contact, so this was a real number. ($X/month — pick your tier.)

The problem is that HubSpot's native "find duplicates" tool is exact-match-only on a small set of fields. It misses:

- "Sarah O'Brien" vs "Sarah Obrien" (apostrophe / no-apostrophe)
- "+1 (415) 555-0143" vs "415-555-0143" vs "4155550143" (phone formats)
- "sarah@acme.com" vs "Sarah@acme.com" (case)
- Same person from a LinkedIn scrape (no phone) + a webform fill (no LinkedIn URL) + a trade-show import (only email + company)

Here's the 4-step pipeline I run before *every* HubSpot import now. You can build the first 3 with Python + pandas + rapidfuzz; the 4th is the one that matters and is the easiest to skip:

**Step 1 — Normalize before comparing.** Lowercase emails, strip phone formatting to E.164, trim whitespace, normalize unicode (NFKC). This alone catches ~40% of dupes.

**Step 2 — Fuzzy-match on name + company, blocked by email domain.** Don't fuzzy-match across the whole list (O(n²) and full of false positives). Block by email domain first — only compare contacts within the same company. Use rapidfuzz token-set ratio at threshold 85.

**Step 3 — Cross-source merge logic.** When LinkedIn-source and webform-source records match, *the LinkedIn one wins on title/company* (more recent), *the webform one wins on phone/email* (verified). Document this rule somewhere your team can read it.

**Step 4 — Confidence tiers, not yes/no.** Don't auto-merge anything below 95% confidence. Auto-merge 95-100. Queue 85-95 for manual review. Drop everything below 85. The manual queue is the magic — it catches the cases the algorithm doesn't dare touch and trains you on what your data actually looks like.

I eventually wrapped all this into a desktop tool I called DataTools because I got tired of re-running the script every campaign. Local-only, $49 if anyone wants it: datatools.app/revops. But the 4-step framework above is the real takeaway — works regardless of what tool you use.

What's your dedupe pipeline look like?

— {{your-name}}
