How to Handle Duplicate Leads in Salesforce

Match email before creating a lead and log a repeat form fill as a Task, so first touch survives. Then duplicate rules, phone matching and merging up to 3 leads.

By Published

Duplicate leads are an intake problem that shows up as a data problem. Two reps call the same person, a report counts one buyer twice, and the second record carries a later campaign that takes credit for the first. Salesforce's duplicate rules help, but they were designed around people typing records in, and most leads today arrive from forms, booking tools and integrations. This guide starts with the check that runs before a lead exists, using our own org as the worked example, then covers the rules, phone matching and merging.

Where do duplicate leads come from?

Sources of duplicate leads and the guard for each
SourceHow the duplicate appearsGuard
Repeat form fillsThe same person comes back weeks later and becomes a second lead with a new first touchMatch email at intake; log the repeat as a Task
Retries from forms, webhooks and booking toolsOne submission delivered twice within secondsAn idempotency key: a request ID, a booking ID, or an External ID upsert
Web-to-LeadEvery submit creates a lead, with no check in frontSend forms through an intake step that checks first
Product signups synced from an appA new lead per sync run when the record is not keyedUpsert on an External ID such as the app's user ID
Phone and voice intakeThe same caller with the number formatted differentlyNormalize to digits and match on every phone field
Imports and event listsPeople who already exist, loaded againMatch before the load; expect rules to block, not warn
Two brands in one orgThe same email under each brand, matched to the wrong oneScope the match by record type or brand

Should you block, alert, or match and log?

There are four ways to respond to a likely duplicate, and they suit different entry points. Salesforce's duplicate rules offer the first three; the fourth is logic you put in front of the insert.

Four responses to a duplicate lead
ResponseWhat happensUse it forThe cost
Block (duplicate rule)The save fails with a duplicate errorManual entry where a duplicate is always a mistakeAn integration that does not catch the error loses the lead
Alert (duplicate rule)The user sees a warning and can save anywayManual entry where a person should decideThrough the API and import tools it acts as a block
Report (duplicate rule)The record saves and joins a duplicate record setMeasuring the problem before you enforce anythingSomebody has to work the sets, or nothing changes
Match and log (intake logic)No new lead; the submission is logged on the existing recordForms, chat, bookings, product signupsYou build and own it, and the repeat touch is free text unless you design for it

How does our intake handle a repeat form fill?

Every website inquiry to our own Salesforce org goes through one n8n workflow, and its first Salesforce step is a lookup, not an insert. We chose that over Web-to-Lead because Web-to-Lead is form-only, creates a lead on every submission and gives the site no reliable success signal. The workflow:

  1. Looks for the newest unconverted Lead with the exact email (query below), with the email escaped before it goes into SOQL.
  2. When it finds one, it creates a completed Task on that Lead with the subject “Website form:” plus the topic, puts the new message and the latest click detail in the description, and sends a repeat-submission notice. It updates no field on the Lead and adds no Campaign member.
  3. When it finds none, it creates the Lead with the full attribution block, and a flow adds the Campaign member from the campaign key.

Lead activity timeline

  1. Day 1

    Lead created from the website form

    Lead Source and the UTM fields are written once, from the first visit

  2. Day 1

    Campaign member added

    Status Form Submitted, matched on the campaign key

  3. Day 23

    Task, completed: Website form: pricing question

    Holds the new message and the latest click detail. A repeat-submission notice goes to the owner

  4. Day 23

    First-touch fields unchanged

    No second lead, no overwrite, no second Campaign member

Illustration: a lead's timeline after the same person fills in the form a second time. Days, topic and values are generic.
-- Our intake today: newest unconverted Lead with this email
SELECT Id, Email, Name FROM Lead
WHERE Email = :email AND IsConverted = false
ORDER BY CreatedDate DESC LIMIT 1

-- What we add on client builds: scope to the brand, then check Contacts
SELECT Id, OwnerId FROM Lead
WHERE Email = :email AND IsConverted = false
  AND RecordType.DeveloperName = :brandRecordType
ORDER BY CreatedDate DESC LIMIT 1

SELECT Id, AccountId, OwnerId FROM Contact
WHERE Email = :email
ORDER BY LastModifiedDate DESC LIMIT 1

Two other entry points follow the same idea with a different key. Bookings come from Cal.com's own Salesforce app, which writes the booking UID on the Event, and a check on that UID stops Cal.com's retries from creating a second Event. Product signups from our ApexGenius app are upserted on an External ID holding the app's user ID; a signup whose email matches an unconverted lead with no user ID adopts that lead instead of creating a new one. The cutover check for that sync is the aggregate query further down, scoped to the product's record type and expected to return zero rows.

Here is what it does not do yet. It matches Leads only, so a person who already converted to a Contact arrives as a new Lead. It does not filter by record type, so a lead for our consulting brand and one for our marketing brand with the same email can collide. And the repeat touch lives in Task text, which a person can read but a report cannot group by campaign. The second and third queries above are what we add on client builds. The reporting trade-off is covered in how we tie every ad lead to a Salesforce Campaign.

How do Salesforce duplicate and matching rules treat leads?

A duplicate rule decides what happens (block, alert, report), and a matching rule decides what counts as a match. The standard lead matching rule is active by default, and its equation is the first thing to read, because every branch of it needs both First Name and Last Name:

  • First Name AND Last Name AND Email
  • First Name AND Last Name AND Phone (alone, or with Company)
  • First Name AND Last Name AND Title AND Company (or Email)
  • First Name AND Last Name AND Mailing Street AND (City, ZIP, Phone or Title)

Email is compared exactly, first names fuzzily (exact, initials, Jaro-Winkler, Metaphone 3 and name variants, threshold 85), and phone by section, which Salesforce says works best with North American data (Standard Contact Matching Rule and Standard Lead Matching Rule, Salesforce Help, read 16 October 2026). A blank name field is ignored when Email is part of the comparison; a different one is not. The practical consequence: an intake that fills a missing last name with a placeholder, or splits a single name field differently from the way a rep types it, produces leads the standard rule will not match even when the email is identical. If email is your identity key, add a custom lead matching rule with Email on an exact match. Salesforce documents the same fix for accounts that share only a name (Knowledge Article 000386375).

When duplicate rules do not behave the way people expect
SituationWhat Salesforce does
Lead created through an API or the data import toolsNo alert is shown and the record cannot be saved, so an Alert rule acts as a Block
Several records saved at once (a batch or an import)Block and Alert compare them with existing records only, not with each other; Report does include them
A record is editedThe rule runs only when an edited field is in the matching rule
The user cannot see a field the matching rule usesThe rule does not find duplicates for that user
Quick Create, undelete, manual merge, or lead convert without Apex Lead ConvertDuplicate rules do not run
A Workflow Rule or Process Builder field updateDuplicate rules do not run on that update
Rule limitsUp to five active duplicate rules per object and up to three matching rules in each

Sources, read 16 October 2026: Things to Know About Duplicate Rules and Knowledge Article 000386375. For an integration that should save past an Alert rule, REST API accepts Sforce-Duplicate-Rule-Header: allowSave=true from API version 52.0 (Duplicate Rule header); Apex has the equivalent in Database.DMLOptions.DuplicateRuleHeader.

Free consultation

Stop duplicate leads at the door, not in a cleanup project.

How do you catch a duplicate error in a flow?

A mass-tort intake firm whose org we worked in creates a person record from a completed intake questionnaire in a flow. When the duplicate rule stops the insert, the flow's fault path does not end the run:

  1. A decision checks whether the fault message contains DUPLICATES_DETECTED or “Duplicate Alert”.
  2. If it does, the flow gets the most recent existing record with the same first name, last name and phone.
  3. It links the intake to that record and continues, so the case is never left without a person attached.
  4. Any other fault ends the run on the default outcome. That is the branch we would add an error log and an alert to, because only the duplicate case has a safe automatic answer and the rest need a person.

The same org also had a scheduled job meant to flag likely duplicate intakes from duplicate record sets. In the snapshot we reviewed, the duplicate rule feeding those sets was inactive, so the job ran and had nothing to flag. When any automation depends on a duplicate rule, add a check that the rule is still active to whatever you already monitor.

How do you match leads by phone number?

Phone numbers arrive as (954) 555-0100, +1 954-555-0100 and 9545550100, and an exact match treats those as three people. Normalize first, store the normalized value in a text field beside each phone field, and match on those fields.

// Normalize before matching. Store the result in a text field
// next to each phone field and match on those, not on raw input.
public static String phoneKey(String raw) {
    if (String.isBlank(raw)) return null;
    String digits = raw.replaceAll('[^0-9]', '');
    // North American numbers: drop a leading country code 1.
    if (digits.length() == 11 && digits.startsWith('1')) {
        digits = digits.substring(1);
    }
    return digits.length() >= 10 ? digits : null;
}

A behavioral health provider whose org we worked in takes calls through an AI voice agent that posts each call to an Apex REST endpoint. Numbers are reduced to digits, and a flow looks for a lead whose normalized phone, mobile or contact phone matches, then updates that lead or creates one. That keeps one lead per caller. The update path also writes the latest call's click ID and campaign over the existing values, so the lead ends up credited to the most recent call instead of the first one. If first touch matters to you, write attribution fields only when they are blank. The voice intake itself is in a voice AI answers the phone, Salesforce gets a perfect lead.

How do you merge duplicate leads without losing data?

In Lightning Experience you open a lead, click View Duplicates, choose up to three leads, pick one as the principal and choose the field values to keep. You need Delete on leads to merge. After the merge (Merge Duplicate Leads in Lightning Experience and Things to Know About Merging Duplicate Leads, read 16 October 2026):

  • The non-principal leads move to the Recycle Bin.
  • Related items such as campaign memberships and activities move to the merged lead, with some exceptions, and each campaign's Member Status Updated date is kept.
  • Chatter feeds come from the principal record only; files are kept.
  • The merged lead keeps the Created By and Created Date of the oldest lead, whichever one you chose as principal.
  • Hidden and read-only fields keep the principal record's values.

Apex merge follows the same limit of three records of one object, deletes the duplicates and reparents related records (Merging Records, Apex Developer Guide). The risk in code is the field copy you write before the merge. On a 2023 project, Apex we reviewed found duplicate contacts with Datacloud.FindDuplicates, copied every field from the duplicate onto the survivor, blanks included, and queried inside a loop. The blanks erased good values on the survivor. Decide the survivor rules before anyone merges anything:

Survivor rules for a lead merge
Field groupKeep
First-touch attribution (UTM values, click IDs, landing page, Lead Source)The oldest lead's values
Email and phoneThe most recently confirmed value that is not blank
OwnerThe rep with the open activity or the most recent contact
StatusThe most advanced status
Consent and opt-outsThe most restrictive choice on any of the records
Any fieldA value over a blank, never a blank over a value

How do you find the duplicate leads you already have?

An aggregate query lists every email held by more than one open lead. A version of it scoped to one record type is the check we run after a sync cutover, where the expected result is zero rows.

SELECT Email, COUNT(Id) n
FROM Lead
WHERE IsConverted = false AND Email != null
GROUP BY Email
HAVING COUNT(Id) > 1

Add a record type filter if the org holds more than one brand, and run the same query on Contact. For an ongoing view, set the duplicate rule to Report for a few weeks before switching it to Block or Alert: the duplicate record sets show how many duplicates each entry point creates, which tells you where the intake check belongs. If a clinical record is involved, the same principle (check before you create, on a key you trust) is what stops duplicate charts in our AdvancedMD and Salesforce integration. Who owns the lead once it is single is the subject of Salesforce lead routing architecture.

Frequently asked questions

How do I prevent duplicate leads in Salesforce?

Check before you create. Route every form, chat, booking and integration through one intake step that looks for an existing unconverted lead or contact with the same email (and phone, for call intake) and logs the new submission on that record when it finds one. Then add duplicate rules as a second net for manual entry and imports.

Why doesn't my Salesforce duplicate rule catch leads with the same email?

The standard lead matching rule never matches on email alone. Every branch of its equation also compares First Name and Last Name, so the same email under a different last name, such as a placeholder an intake filled in, is not a duplicate to it. Create a custom lead matching rule with Email on an exact match and add it to your duplicate rule.

Do duplicate rules block leads created through the API?

Yes. When records are added through Salesforce APIs or the data import tools, no alert is shown and the record cannot be saved, so an Alert rule behaves like a Block and the API returns DUPLICATES_DETECTED. An integration can send the Sforce-Duplicate-Rule-Header with allowSave=true (REST, API 52.0 and later) to save anyway.

How many leads can you merge at once in Salesforce?

Three. In Lightning Experience you choose up to three leads, pick one as the principal record and choose which field values to keep; the other leads go to the Recycle Bin. Apex merge has the same limit of three records of one object per merge.

Should a repeat form fill update the existing lead?

Not the attribution fields. Overwriting UTM values, click IDs or landing page on a repeat visit erases the first touch that brought the lead in. Log the new submission as an activity on the existing lead, and if repeat touches by campaign must be reportable, add a second Campaign member on purpose.

Continue the decision

View all resources →

Free consultation

Stop duplicate leads at the door, not in a cleanup project.

Free, 30 minutes, no obligation.

  • You leave with
  • Our first read on where your operation leaks time, live on the call
  • A straight answer on whether a full systems audit is worth it for you
  • If it is: the scope and the fixed price. If not: what to do instead