Lead Merge
When the same person is tracked under two or more separate lead records -- for example because they engaged from a work email and a personal email -- you can consolidate them into a single lead. Merging moves all events, aliases, sessions, and watch-list entries onto the target lead and soft-deletes the source leads.
POST /leads/merge/preview
Returns a read-only summary of what a merge would do without modifying any data. Use this to review the impact before committing to a merge.
When to Use This
- Confirm the correct lead is being kept as the target before merging
- See how many events and aliases would be moved
- Identify whether the leads belong to different accounts, so you can decide which account to keep
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
targetLeadId | string | Yes | The lead that will survive the merge and receive all data |
sourceLeadIds | array of strings | Yes | One or more lead IDs that will be absorbed into the target |
{
"targetLeadId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"sourceLeadIds": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
}
Response
| Field | Type | Description |
|---|---|---|
target_lead_id | string | The target lead ID |
source_lead_ids | array | The source lead IDs to be absorbed |
target_events | integer | Events currently on the target lead |
total_events | integer | Events that would be moved from source leads |
total_aliases | integer | Aliases that would be moved from source leads |
total_sessions | integer | Sessions that would be moved from source leads |
duplicate_aliases | integer | Aliases on source leads that already exist on the target (will be de-duplicated; target's aliases win) |
target_score | number | Current total score on the target lead |
source_scores | object | Current score for each source lead, keyed by lead ID |
account_conflict | boolean | true if sources and target belong to different accounts |
accounts | array | All unique accounts linked to the leads being merged |
{
"target_lead_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"source_lead_ids": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
"target_events": 14,
"total_events": 9,
"total_aliases": 2,
"total_sessions": 3,
"duplicate_aliases": 0,
"target_score": 85.0,
"source_scores": {
"b2c3d4e5-f6a7-8901-bcde-f12345678901": 42.0
},
"account_conflict": false,
"accounts": [
{
"account_id": "acc-123",
"account_name": "Acme Corp",
"lead_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
]
}
Example
curl -X POST "https://api.kenbun.io/leads/merge/preview" \
-H "Authorization: Basic <credentials>" \
-H "Content-Type: application/json" \
-d '{
"targetLeadId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"sourceLeadIds": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
}'
Common Errors
| Status | Meaning | Solution |
|---|---|---|
| 400 | Bad Request | targetLeadId or sourceLeadIds is missing or malformed |
| 401 | Unauthorized | Check your API credentials |
POST /leads/merge
Merges one or more source leads into a target lead. This action is permanent and cannot be undone. Use the preview endpoint first to confirm what will be affected.
What happens during a merge:
- All engagement events from source leads are moved to the target lead. Each event retains its
original_lead_idso you can always trace which lead it came from. - Aliases (email addresses, phone numbers, etc.) are moved to the target. If a source lead has an alias of the same type as one already on the target, the source's version is discarded and the target's alias is kept.
- Sessions are reassigned to the target lead.
- Watch-list entries are transferred to the target. If a team member already watches the target, the duplicate watch is removed.
- Scores are recalculated on the target lead from all moved events.
- Source leads are soft-deleted.
- A
lead_mergedevent is recorded on the target's activity timeline. - The merge is captured in the audit log with the user who performed it.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
targetLeadId | string | Yes | The lead that will survive and receive all data |
sourceLeadIds | array of strings | Yes | Lead IDs to absorb into the target |
accountId | string | No | Override the account linked to the merged lead |
metadataSourceLeadId | string | No | Copy profile metadata from this lead (must be one of the leads being merged) |
{
"targetLeadId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"sourceLeadIds": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
}
Response
| Field | Type | Description |
|---|---|---|
target_lead_id | string | The surviving lead's ID |
merged_sources | array | Lead IDs that were absorbed |
moved_events | integer | Number of events transferred to the target |
moved_aliases | integer | Number of aliases transferred to the target |
{
"target_lead_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"merged_sources": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
"moved_events": 9,
"moved_aliases": 2
}
Example
curl -X POST "https://api.kenbun.io/leads/merge" \
-H "Authorization: Basic <credentials>" \
-H "Content-Type: application/json" \
-d '{
"targetLeadId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"sourceLeadIds": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
}'
Common Errors
| Status | Meaning | Solution |
|---|---|---|
| 400 | Bad Request | Missing or invalid lead IDs, or no valid sources to merge |
| 401 | Unauthorized | Check your API credentials |
| 404 | Not Found | One or more lead IDs do not exist in the active OU |
GET /leads/{leadId}/merge-history
Returns the merge history for a specific lead — both merges where this lead absorbed other leads, and merges where this lead was itself absorbed into another.
Request
| Parameter | Type | Required | Description |
|---|---|---|---|
leadId | string | Yes | Lead identifier (path parameter) |
Response
| Field | Type | Description |
|---|---|---|
mergeHistory | array | List of merge records for this lead |
Each merge record:
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this merge record |
target_lead_id | string | The lead that survived the merge |
source_lead_ids | array | Lead IDs that were absorbed |
moved_events | integer | Number of events moved during the merge |
user_email | string or null | Email of the user who performed the merge, or null for automated merges |
created_at | timestamp | When the merge occurred |
merged_into_this | boolean | true if this lead was the target (absorbed others); false if this lead was a source (merged out into another lead) |
{
"mergeHistory": [
{
"id": "merge-abc123",
"target_lead_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"source_lead_ids": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
"moved_events": 9,
"user_email": "jane@example.com",
"created_at": "2025-03-10T14:22:00Z",
"merged_into_this": true
}
]
}
Example
curl -X GET "https://api.kenbun.io/leads/a1b2c3d4-e5f6-7890-abcd-ef1234567890/merge-history" \
-H "Authorization: Basic <credentials>"
Common Errors
| Status | Meaning | Solution |
|---|---|---|
| 401 | Unauthorized | Check your API credentials |
| 404 | Not Found | Lead does not exist in the active OU |
Notes
- All three endpoints are OU-scoped to the active Organizational Unit. Leads from different OUs cannot be merged.
- Merges are recorded in the Audit Log with the email of the user who initiated the action.
- After a merge, events on the target lead may include an
original_lead_idfield indicating which source lead originally owned that event. This field isnullfor events that were never moved. - The merge operation is atomic -- if any step fails, no changes are saved.
Related
- Lead Detail - View lead profiles
- Events for a Lead - See all events on a lead, including moved events
- Lead Aliases - Manage lead identifiers