HubSpot’s Manage Duplicates tool identifies many possible matches, but sometimes you need to zero-in on specific combinations of fields. With custom code, you can surface additional, legitimate duplicates based on your rules—such as Phone + Full Name + Address—then mark every match for review.
- Detects duplicates at the moment a record enters the workflow.
- Matches on any field mix you choose; the sample below adds
address
to phone and name. - Flags every matching record so teams can merge or clean up.
- Prevents double outreach and keeps reports accurate.
Typical Use Cases
Scenario | Why It Matters |
---|---|
Trade-show lead uploads | Scanners and kiosks often create the same attendee twice; address adds an extra safeguard. |
Phone-centric industries | Phone + name + street address pinpoints the true duplicate among common surnames. |
Data migrations and list imports | Catches duplicates as you import, before they flow into live automation. |
Step-by-Step Implementation
1. Create the Duplicate-Flag Properties
Property | Type | Purpose |
---|---|---|
likely_duplicate_contact |
Checkbox | Master flag; default No. |
likely_duplicated_by |
Single-line text | Stores the rule that fired (for example, “Full Name + Phone + Address”). |
likely_duplicate_of |
Single-line text | Captures the canonical record’s identifier for reference. |
2. Generate a Private App Token
- Go to Settings → Integrations → Private Apps → Create app.
- Name it “Duplicate Checker (Phone-Name-Address)”.
- Add scopes:
crm.objects.contacts.read
andcrm.objects.contacts.write
. - Save and copy the token; store it as a workflow secret named
MyContacts
.
3. Insert the Custom Code Action
- Create or edit a Contact-based workflow (for example, enroll on “Contact is created”).
- Add a Custom Code action.
- Add the secret
MyContacts
. - Paste the script below.
const hubspot = require('@hubspot/api-client');
exports.main = async (event) => {
const hubspotContacts = new hubspot.Client({
accessToken: process.env.MyContacts
});
// 1. Get phone, name, and address (extend as needed)
let phone, firstName, lastName, street;
try {
const res = await hubspotContacts.crm.contacts.basicApi.getById(
event.object.objectId,
['phone', 'firstname', 'lastname', 'address']
);
phone = res.properties.phone;
firstName = res.properties.firstname;
lastName = res.properties.lastname;
street = res.properties.address;
} catch (err) {
console.error(err);
throw err;
}
// 2. Abort if phone is blank
if (!phone) {
console.log('No phone number — skipping duplicate check.');
return;
}
// 3. Find contacts with the same phone
let ids = [], fNames = [], lNames = [], streets = [];
try {
const body = {
filterGroups: [{
filters: [{ value: phone, propertyName: 'phone', operator: 'EQ' }]
}],
properties: ['hs_object_id', 'firstname', 'lastname', 'phone', 'address'],
limit: 100
};
const res = await hubspotContacts.crm.contacts.searchApi.doSearch(body);
ids = res.results.map(r => r.properties.hs_object_id);
fNames = res.results.map(r => r.properties.firstname);
lNames = res.results.map(r => r.properties.lastname);
streets = res.results.map(r => r.properties.address);
} catch (err) {
console.error(err);
throw err;
}
// 4. Skip if only one record has that phone
if (ids.length <= 1) return;
// 5. Keep those that match name (and address if present)
const dupIds = [];
ids.forEach((id, i) => {
const sameName = fNames[i] === firstName && lNames[i] === lastName;
const sameStreet = !street || streets[i] === street;
if (sameName && sameStreet) dupIds.push(id);
});
// 6. Skip if only itself
if (dupIds.length <= 1) return;
// 7. Flag duplicates
for (const id of dupIds) {
await hubspotContacts.crm.contacts.basicApi.update(id, {
properties: {
likely_duplicate_contact: 'Yes',
likely_duplicated_by: street ? 'Full Name and Phone and Address' : 'Full Name and Phone',
likely_duplicate_of: `${firstName} ${lastName} ${phone}`
}
});
}
};
4. Branch or Notify
- Add an If/then branch: “likely_duplicate_contact is Yes”.
- Duplicate path → create a task, send a Slack alert, or trigger an Ops Hub merge routine.
- Clean path → continue normal workflow logic.
5. Test Thoroughly
- Create two contacts with identical phone, name, and address.
- Enroll both; confirm both flip to Yes and populate reference fields.
- Change the address on one record and re-test to verify the custom rule behaves as expected.
Results and Benefits
- Higher precision than default duplicate scanning when you rely on specific fields.
- Immediate detection as records arrive.
- Cleaner data for sequences, dashboards, and attribution.
Wrapping Up
HubSpot’s deduplication tool casts a broad net, which is useful for quick reviews. When you need to apply unique criteria—like matching on phone, full name, and address—Operations Hub Pro plus custom code lets you surface the exact duplicates you care about and keep your CRM pristine.
Have questions or ideas for extending this recipe to other properties? Share your thoughts below.