Configuring Duplicate Merging in Bitrix24 CRM
Finding duplicates is half the work. The other half is merging them without losing call history, deals, tasks, and custom fields. This is where most companies run into real problems: after a merge, activities disappear, the wrong "master" card is selected, and related deals get reassigned to the wrong record.
The merging mechanism in Bitrix24 operates through the CCrmEntityMerger class — it sequentially transfers related objects, then deletes the absorbed records. Understanding the order of operations is essential to avoid broken references.
Manual Merging via the Interface
Basic workflow: CRM → Contacts → Find Duplicates → select a group → Merge. The system displays cards side by side and prompts you to choose which value to keep for each field in the resulting record.
Pitfalls of manual mode:
- When merging companies, related contacts are automatically moved to the "master" — verify that you selected the correct one.
- Files from absorbed cards are transferred, but their original path in
b_disk_objectchanges — any external links to those files will break. - Custom fields of type "list" are merged only if the "multiple selection" option is enabled; single values are taken from the master record and the rest are lost.
Configuring Merge Rules
In CRM → Settings → Duplicates → Merge Rules, you define the logic for resolving field conflicts:
| Rule | Behavior |
|---|---|
| Master record | Value taken from the selected "master" card |
| More complete | Non-empty value is used; on conflict — from master |
| Merge | Applicable for multi-value fields (phones, email) |
| Most recent by date | Value taken from the record with the later modification date |
For most use cases the optimal setup is: key identifiers (tax ID, phone, email) — "merge"; status fields (responsible person, stage) — "master record"; text notes — "more complete".
Programmatic Merging via API
Manual mode is unsuitable for bulk database cleanup. REST method for merging contacts:
// Merge: record 1001 absorbs 1002 and 1003
$response = CRest::call('crm.contact.merge', [
'id' => 1001,
'victims' => [1002, 1003],
'fields' => [
'PHONE' => 'merge', // merge phone numbers
'EMAIL' => 'merge', // merge email addresses
'ASSIGNED_BY_ID' => 'primary', // responsible from master
],
]);
For companies, use crm.company.merge in the same way. The method is synchronous; merging more than 5 absorbed records at once may cause a timeout — process in pairs.
Within Bitrix24 at the PHP level (On-Premise):
$merger = new CCrmEntityMerger(CCrmOwnerType::Contact);
$merger->Merge(
1001, // master record ID
[1002, 1003], // victims
$fields, // field rules
$errorMessage
);
Transferring Related Objects
This is a critical part that is easy to overlook. The following are transferred automatically during a merge:
- Deals (
CONTACT_IDfield is updated) - Activities: calls, emails, meetings from the
b_crm_activitytable - Tasks via the
b_tasks_memberrelation - Timeline (
b_crm_timeline)
Not transferred automatically:
- Relations via custom Smart Process (SPA) blocks — requires a separate update script
- External relations from third-party integrations (data in custom tables)
- Open Line chats — history is tied to the line, not to the contact
For Smart Processes, relation updates must be done manually via crm.item.update with the new contactId.
Rolling Back a Merge
There is no built-in rollback. After a merge, absorbed records are marked as deleted (the DELETED=Y flag in b_crm_contact), but remain physically in the database for several days before the recycle bin is cleared.
Recovery window: CRM → Settings → Recycle Bin — absorbed cards land there and are available for restoration for up to 30 days (configurable). After restoration, relations to the master record are not restored automatically — they must be reassigned manually.
Before running any bulk merge, always back up the b_crm_contact, b_crm_company, b_crm_deal tables and the activity tables.







