curl --request POST \
--url https://api.lemlist.com/api/contacts/merge \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"primaryId": "ctc_xW8Ou6C03Csv8vatp",
"secondaryIds": [
"ctc_bT4Ktq6kbXwWKv2rM",
"john.doe@acme.com"
]
}
'import requests
url = "https://api.lemlist.com/api/contacts/merge"
payload = {
"primaryId": "ctc_xW8Ou6C03Csv8vatp",
"secondaryIds": ["ctc_bT4Ktq6kbXwWKv2rM", "john.doe@acme.com"]
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
primaryId: 'ctc_xW8Ou6C03Csv8vatp',
secondaryIds: ['ctc_bT4Ktq6kbXwWKv2rM', 'john.doe@acme.com']
})
};
fetch('https://api.lemlist.com/api/contacts/merge', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));require 'uri'
require 'net/http'
url = URI("https://api.lemlist.com/api/contacts/merge")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"primaryId\": \"ctc_xW8Ou6C03Csv8vatp\",\n \"secondaryIds\": [\n \"ctc_bT4Ktq6kbXwWKv2rM\",\n \"john.doe@acme.com\"\n ]\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.lemlist.com/api/contacts/merge",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'primaryId' => 'ctc_xW8Ou6C03Csv8vatp',
'secondaryIds' => [
'ctc_bT4Ktq6kbXwWKv2rM',
'john.doe@acme.com'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}Merge Contacts
Merges up to 10 lemlist contacts into one survivor, moving everything attached to the deleted contacts onto it.
curl --request POST \
--url https://api.lemlist.com/api/contacts/merge \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"primaryId": "ctc_xW8Ou6C03Csv8vatp",
"secondaryIds": [
"ctc_bT4Ktq6kbXwWKv2rM",
"john.doe@acme.com"
]
}
'import requests
url = "https://api.lemlist.com/api/contacts/merge"
payload = {
"primaryId": "ctc_xW8Ou6C03Csv8vatp",
"secondaryIds": ["ctc_bT4Ktq6kbXwWKv2rM", "john.doe@acme.com"]
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
primaryId: 'ctc_xW8Ou6C03Csv8vatp',
secondaryIds: ['ctc_bT4Ktq6kbXwWKv2rM', 'john.doe@acme.com']
})
};
fetch('https://api.lemlist.com/api/contacts/merge', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));require 'uri'
require 'net/http'
url = URI("https://api.lemlist.com/api/contacts/merge")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"primaryId\": \"ctc_xW8Ou6C03Csv8vatp\",\n \"secondaryIds\": [\n \"ctc_bT4Ktq6kbXwWKv2rM\",\n \"john.doe@acme.com\"\n ]\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.lemlist.com/api/contacts/merge",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'primaryId' => 'ctc_xW8Ou6C03Csv8vatp',
'secondaryIds' => [
'ctc_bT4Ktq6kbXwWKv2rM',
'john.doe@acme.com'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}primaryId) keeps its id and every value it holds; its empty fields are filled from the other contacts, which are then deleted. A value the survivor already has is never overwritten.
Email addresses follow the same rule: the survivor keeps its own, a loser’s addresses are dropped — unless the survivor has none, in which case it takes the first loser’s.
Each contact is addressed by its lemlist id (ctc_xxx) or by one of its email addresses, like DELETE /contacts/{idOrEmail}. The response always carries the resolved primaryId.
What moves to the survivor
- Leads in campaigns, activities, tasks, list memberships and inbox conversations of every deleted contact.
- When a CRM is connected, the survivor keeps its CRM link, or adopts the first linked contact’s when it has none, and receives the merged values there. lemlist never merges or deletes anything in the CRM itself: the other contacts’ CRM records stay in the CRM, unlinked from lemlist. Merge them CRM-side too, or a later CRM import may recreate the duplicate.
Partial runs — success: true does not mean complete
The contacts are folded into the survivor one at a time and the run stops at the first failure. The response is still 200: mergedIds lists what went through, remainingIds what still exists — fix the cause (typically an enrichment in progress) and call again with the remaining ids. A failure on the very first fold returns an error instead, and nothing was written.
Authorizations
Basic authentication header of the form Basic <encoded-value>, where <encoded-value> is the base64-encoded string username:password.
Body
The surviving contact. A lemlist contact id (ctc_xxx) or one of the contact's email addresses.
"ctc_xW8Ou6C03Csv8vatp"
The contacts folded into the survivor then deleted, 9 at most. A lemlist contact id (ctc_xxx) or one of the contact's email addresses. Must not contain primaryId, and each contact is listed once.
1 - 9 elements[ "ctc_bT4Ktq6kbXwWKv2rM", "john.doe@acme.com" ]
Response
Merge run — success: true does not mean complete. The contacts fold one at a time and the run stops at the first failure, so ALWAYS check remainingIds: the contacts listed there were NOT merged and still exist. Fix the cause (typically an enrichment in progress) and call again with the survivor and the remaining ids.
ID of the surviving lemlist contact — resolved from the email when one was given.
true once at least one contact was folded into the survivor.
IDs of the contacts folded into the survivor and deleted, in fold order.
IDs of the contacts NOT merged because an earlier fold failed. Empty when everything went through.
Fields whose adopted value was refused by a uniqueness rule (kept survivor-side). Only present when it happened.
Was this page helpful?