• facebook
  • twitter
  • linkedin

Automatically Flag Likely Duplicate Contacts in HubSpot by Phone, Name & Address — A Custom Code Action Guide

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.

01. Find Duplicate Contacts

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

  1. Go to Settings → Integrations → Private Apps → Create app.
  2. Name it “Duplicate Checker (Phone-Name-Address)”.
  3. Add scopes: crm.objects.contacts.read and crm.objects.contacts.write.
  4. Save and copy the token; store it as a workflow secret named MyContacts.

3. Insert the Custom Code Action

  1. Create or edit a Contact-based workflow (for example, enroll on “Contact is created”).
  2. Add a Custom Code action.
  3. Add the secret MyContacts.
  4. 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

  1. Create two contacts with identical phone, name, and address.
  2. Enroll both; confirm both flip to Yes and populate reference fields.
  3. 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.

Comments (0)
Other

Insights You Might Like

Automatically Tag...

HubSpot stores heaps of data about your contacts, but it doesn’t natively track
Read More

Rank the Top Candidates...

Matching applicants to open roles is only half the battle; you still need to...
Read More

Auto-Populate Deals from...

When a subscription is created in HubSpot—often via Stripe, Chargebee or a...
Read More