Data Migration from a Third-Party CRM to Bitrix24
Switching from Salesforce, HubSpot, Pipedrive, or a custom-built CRM to Bitrix24 is not simply a matter of exporting a CSV and importing it. Every CRM has its own data model, relationship logic, and field semantics. Bitrix24 does too. The primary goal of migration is to preserve entity relationships and retain the historical value of the data.
Bitrix24 Data Structure
Before migrating, you need to understand where data lands. Key entities in Bitrix24:
| Entity | DB Table | REST API |
|---|---|---|
| Contacts | b_crm_contact |
crm.contact.* |
| Companies | b_crm_company |
crm.company.* |
| Leads | b_crm_lead |
crm.lead.* |
| Deals | b_crm_deal |
crm.deal.* |
| Activities (calls, emails) | b_crm_activity |
crm.activity.* |
| Custom fields | b_uts_crm_* |
crm.*.userfield.* |
Relationships: a contact is linked to a company via b_crm_company_contact; a deal is linked to a contact via b_crm_deal_contact.
Migration Tools
Bitrix24 REST API — the official and most reliable method. Supports batch requests (batch), which is critical when transferring thousands of records:
$batchCalls = [];
foreach ($contacts as $contact) {
$batchCalls['create_contact_' . $contact['id']] = [
'method' => 'crm.contact.add',
'params' => [
'fields' => [
'NAME' => $contact['first_name'],
'LAST_NAME' => $contact['last_name'],
'EMAIL' => [['VALUE' => $contact['email'], 'VALUE_TYPE' => 'WORK']],
'PHONE' => [['VALUE' => $contact['phone'], 'VALUE_TYPE' => 'WORK']],
'COMPANY_ID' => $companyMapping[$contact['company_id']] ?? null,
'UF_CRM_SOURCE_ID' => $contact['id'], // Preserve source ID
],
],
];
}
// Send batch (up to 50 requests at a time)
$result = $b24->callBatch(array_slice($batchCalls, 0, 50));
Direct DB write — unavailable for Bitrix24 Cloud. For the on-premise edition it speeds up bulk imports, but requires manual index rebuilding and careful handling of triggers.
Field Mapping: The Most Labor-Intensive Step
Every source field must be mapped to a Bitrix24 field. Example mapping from HubSpot:
| HubSpot | Bitrix24 | Notes |
|---|---|---|
firstname + lastname |
NAME + LAST_NAME |
Split |
email |
EMAIL[0].VALUE |
Type: WORK |
phone |
PHONE[0].VALUE |
Normalize |
company |
COMPANY_ID |
Create company separately |
lifecyclestage |
STATUS_ID |
Map stages |
hs_lead_status |
Custom field | UF_CRM_HS_STATUS |
createdate |
DATE_CREATE |
Only via direct SQL (on-premise) |
Non-standard HubSpot fields are moved to Bitrix24 custom fields (UF fields). These must be created in advance via crm.contact.userfield.add.
Entity Creation Order
Order matters because of referential integrity:
- Companies (no upstream dependencies for contacts and deals)
- Contacts (link to companies)
- Deals (link to contacts and companies)
- Activities — calls, emails, tasks (link to deals/contacts)
-
Comments and history — via
crm.timeline.comment.add
After creating each entity, save the mapping: source_id → b24_id. This is needed to attach dependent objects.
$mappingFile = 'migration_map.json';
$mapping = json_decode(file_get_contents($mappingFile), true) ?: [];
$mapping['contacts'][$sourceContact['id']] = $b24ContactId;
file_put_contents($mappingFile, json_encode($mapping));
Handling Duplicates
Third-party CRMs frequently contain duplicate contacts — the same person registered under different email addresses. Deduplicate in the source before migrating. Strategies:
- Hard: one unique email = one contact. Duplicates are merged.
-
Soft: transfer all records, then use the built-in Bitrix24 deduplication tool (
Contacts → Duplicates).
The soft strategy is recommended — it preserves all data and lets managers handle deduplication during normal work.
Activity History: Calls and Emails
Migrating communication history is optional but valuable. Calls from the source → crm.activity.add with type CALL:
$b24->call('crm.activity.add', [
'fields' => [
'OWNER_TYPE_ID' => 3, // 3 = contact, 2 = deal
'OWNER_ID' => $mapping['contacts'][$call['contact_id']],
'TYPE_ID' => 2, // Call
'SUBJECT' => 'Call on ' . date('d.m.Y', strtotime($call['created_at'])),
'DESCRIPTION' => $call['notes'],
'START_TIME' => $call['created_at'],
'END_TIME' => $call['ended_at'],
'DIRECTION' => $call['direction'] === 'inbound' ? 1 : 2,
'COMPLETED' => 'Y',
],
]);
Migration Quality Control
After the transfer, a mandatory reconciliation:
# Source
SELECT COUNT(*) FROM hubspot_contacts WHERE is_deleted = 0;
# → 12 847
# Bitrix24
SELECT COUNT(*) FROM b_crm_contact WHERE DELETED = 'N';
# → 12 839 ← 8 records missing — investigate
Discrepancies are logged and analyzed: they are typically duplicates or records with invalid data (empty email, malformed phone number).
Timelines
| Data Volume | Timeline |
|---|---|
| Up to 5,000 contacts + deals without history | 1–2 weeks |
| 5,000–50,000 records + basic activities | 3–6 weeks |
| 50,000+ records + full communication history | 2–4 months |
A successful migration means managers open Bitrix24 the next day and see the complete history of client relationships — as if they had always worked there.







