Duplicates and merge
These routes find contacts that share a phone, email or name, and merge two of them into one.
#Find duplicate contacts
Groups of contacts that share a phone, email or name, largest groups first. Returns up to 50 groups of up to 200 contacts each. Needs the view_contacts permission.
Parameters
bystring · queryDefaultphonephone,email,name, or several separated by commas: then contacts must match on all of them. Phone compares the last 10 digits.bulk_job_idinteger · query- Only contacts from this import.
dialer_list_idinteger · query- Only contacts in this list or smart list.
search, tag, status, stage, ...query- The filters of List contacts narrow the scan the same way.
Returns
groupsobject[]- Each has
key(the shared value),by,countandcontacts: each contact’sid,first_name,last_name,email,phone,pipeline_stageandscore, lowest id first. total_duplicatesinteger- Contacts beyond the first in each returned group.
too_largebooleantruewhen your account has over 1,500,000 contacts and you sent no filter: no groups are returned. Use Start a duplicate scan.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_SCAN_SCOPE | bulk_job_id or dialer_list_id isn’t a valid id. |
| 400 | A filter value is invalid. | |
| 413 | DUPLICATE_FILTER_TOO_LARGE | Your filters still match over 1,500,000 contacts. Narrow them. |
| 500 | DUPLICATE_SCAN_FAILED | The scan didn’t finish. Try again in a minute. |
#Start a duplicate scan
Queues a background scan of your whole account for contacts that share a phone number, for accounts too large for Find duplicate contacts. Starting again replaces the previous scan with a new one. Takes no parameters. Needs the edit_contacts permission.
Returns { "status": "queued" } in data.
Errors
| HTTP | Code | When |
|---|---|---|
| 500 | DEDUPE_SCAN_QUEUE_FAILED | The scan wasn’t queued. Try again in a minute. |
#Get the duplicate scan
The background scan’s status and, once it’s done, its groups in the same shape as Find duplicate contacts. Contacts deleted or merged since the scan are left out, and so are groups left with one contact. Needs the view_contacts permission.
Returns
statusstringnone(never run),queued,running,doneorerror. Anoneanswer has no other fields.groups, group_count, total_duplicatesobject[], integer, integer- The groups, and counts of what was returned. Empty until
done. errorstring- A sentence when the last scan didn’t finish, otherwise
null. error_codestringtoo_large_for_scanwhen the background scan cannot finish for an account this size (scan one import or Smart List instead), otherwisenull.requested_at, completed_atstring- ISO 8601, or
null.
Errors
| HTTP | Code | When |
|---|---|---|
| 500 | DEDUPE_SCAN_STATUS_FAILED | We couldn’t read the scan. Try again in a minute. |
#Merge contacts
Permanently deletes the duplicates after folding them into the contact you keep. This can’t be undone. Needs the edit_contacts permission.
For each duplicate, in order, in one step that saves all of it or none of it:
- Kept contact’s fields: its phone number, status and opt-out stay as they are. A blank name, email, company, website, address, date of birth, gender, time zone, inbox category or assignee is filled from the duplicate, and so are blank property and sale fields. Tags are combined. The higher score is kept. The duplicate’s notes field is added after the kept one’s. Custom fields the kept contact doesn’t have are added; its own values win.
- Moved to the kept contact: the conversation and its messages (folded into one thread), scheduled texts, activity, notes, tasks, calls and callbacks, AI agent pauses, appointments, link clicks and form submissions. Drip enrollments, waiting automation steps, list memberships, group chats and custom field values move too, except where the kept contact already has one for the same drip, automation, list, group or field: then the duplicate’s is deleted.
- Deleted: the duplicate contact. If its number had opted out, the opt-out is kept on that number, as with Delete a contact. The kept contact’s activity gets a line naming the merged contact.
Parameters
primary_idintegerRequired- The contact to keep.
duplicate_idsinteger[]Required- The contacts to fold in and delete, up to 200.
primary_idand repeats are ignored. An id that isn’t on your account, or that your seat can’t see, is skipped.
Returns
merged_countinteger- Duplicates merged and deleted.
primaryobject- The kept contact after the merge, as its stored row: every column, with
tagsandcustom_fieldsas stored.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | merge_selection_invalid | primary_id isn’t a contact id, duplicate_ids is empty, or it has more than 200 ids. |
| 400 | invalid_field | primary_id is a list or object. |
| 404 | CONTACT_NOT_FOUND | The contact to keep isn’t on your account, or your seat can’t see it. |
| 409 | CONTACT_MERGE_NOTHING_MERGED | None of the duplicates could be merged: they’re gone, on another account or not visible to you. Nothing changed. |
| 423 | LITIGATION_HOLD | Under a legal hold. Nothing changed. |
| 500 | CONTACT_MERGE_INCOMPLETE | Some duplicates didn’t merge. They’re still separate, with nothing of theirs deleted. merged_count and not_merged_count say how many. |
| 500 | CONTACT_MERGE_PRIMARY_GONE | The contact to keep was deleted while the merge ran. |
| 500 | CONTACT_MERGE_FAILED | The merge stopped. Check which duplicates still exist, then try again. |