First, find the pattern
Before changing anything, export a sample of duplicates and look at what they have in common. Typical patterns:
- Same email, created seconds apart. Almost always a retry or a double submission.
- Same person, different email formats. For example
Jane@Example.comandjane@example.com, or a work and a personal address. - Same phone, no email. Phone-only leads from calls or texts with no shared identifier.
- Created by different sources. One from the website form, one from a booking tool, one typed in by staff.
Each pattern has a different fix, so work out which one you have most of.
Cause 1: retries and double submissions
Integrations retry when they do not get a quick success response. Webhook providers say this openly — Stripe's webhook documentation, for example, notes that endpoints may receive the same event more than once and should handle duplicates. If your connector "creates a contact" every time it receives an event, each retry becomes a new record.
Fix: change the step from "create" to "find or create" (sometimes called upsert), keyed on a stable identifier. Where the source provides an event or submission ID, store it and ignore any event you have already processed. Stripe's guide to idempotent requests explains the same idea from the API side.
On the website, disable the submit button after the first click and show a clear confirmation so customers are not tempted to press it again.
Cause 2: identifiers that don't match
CRMs match on whatever field they are told to. If one integration sends email in a field the CRM does not use for matching, or sends it with spaces or capitals, the CRM sees a stranger. HubSpot documents how it deduplicates records, including which properties it uses; other CRMs have equivalent pages.
Fix: normalise before you send — trim spaces, lower-case email, store phone numbers in one consistent international format. Make sure every source maps email to the same CRM property.
Cause 3: too many doors into the CRM
A website form, a booking tool, a chat widget, a phone system and manual entry can each create contacts. Each door needs the same matching rule, and ideally the same integration path.
Fix: list every source that can create a contact. For each one, note the identifier it sends and whether it uses find-or-create. Close doors you do not need.
Cleaning up what's already there
- Back up or export the affected records first.
- Fix the cause before merging, or you will be merging again next week.
- Merge in small batches, starting with exact email matches, which are the safest.
- Review fuzzy matches (similar names, same phone) by hand — households and businesses legitimately share details.
- Record what was merged and when, so reporting changes can be explained.
Prevent it coming back
- Every source uses find-or-create on the same normalised identifier.
- Event or submission IDs are stored and checked before processing.
- Submit buttons can't be pressed twice.
- A weekly duplicate report goes to a named person for the first month.
- New integrations are tested by sending the same test event twice.
If you are about to import an old spreadsheet, the same rules apply — see moving from a spreadsheet to a CRM.
Questions
Should I just delete duplicates?
Merge rather than delete wherever your CRM supports it, so notes, emails and history are kept. Deleting can lose the one record that had the important conversation.
Can phone number be the unique identifier?
It can help, but phone numbers are often shared within households or businesses and typed in different formats. Email is usually a more reliable primary match, with phone as a secondary check.
Sources and further reading
- Stripe's webhook documentation docs.stripe.com
- idempotent requests docs.stripe.com
- deduplicates records knowledge.hubspot.com
Official documentation changes. Check the current version before you rely on a rule, limit or setting.
Next step
Start with one workflow worth fixing.
Bring the real steps, the tools you already use and the place the handoff gets stuck. We review feasibility, access and approval points before quoting — no instant integration promise.
See Business Automations for what we scope, or prepare a custom quote brief.