Traiter les doublons de contacts
Lister les paires de doublons, fusionner deux contacts, défaire une fusion, écarter une fausse paire et suivre un contact absorbé.
Les règles de dédoublonnage de l'organisation comparent les contacts entre eux. Une paire qu'une règle signale attend d'être
examinée ; une fusion réunit deux fiches en une, et se défait pendant 30 jours. Le dédoublonnage ne concerne que les contacts : le
type se nomme Contact dans toutes les routes qui suivent. Pour être averti d'un doublon avant de créer un contact, voir
Créer un contact, une société et une carte.
1Les règles de l'organisation
GET https://staging-api.docage.com/DuplicateRules/Usable?type=Contact
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/DuplicateRules/Usable?type=Contact" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/DuplicateRules/Usable?type=Contact");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/DuplicateRules/Usable?type=Contact", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;Chaque règle dit si l'organisation l'applique (Applied), ce qu'elle fait à la saisie (InteractiveAction : 0 rien, 1 avertir,
2 bloquer), ce qu'elle fait sans saisie (AutomaticAction : 0 rien, 1 signaler, 2 fusionner), et si elle est certaine
(IsCertain : tous ses critères sont des égalités).
Attention
Une règle certaine dont AutomaticAction vaut 2 fusionne sans intervention humaine : un contact que votre outil connaît peut
être absorbé par un autre. Voyez le § 6 pour le reconnaître.
2Les paires à examiner
GET https://staging-api.docage.com/Duplicates/Pairs?type=Contact&skip=0&take=50
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/Duplicates/Pairs?type=Contact&skip=0&take=50" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/Duplicates/Pairs?type=Contact&skip=0&take=50");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Duplicates/Pairs?type=Contact&skip=0&take=50", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La réponse porte le nombre de paires en tout (TotalCount) et une page de paires (Items) — les certaines d'abord, puis les plus
ressemblantes, puis les plus récentes ; take vaut 50 d'office, 200 au plus. Chaque paire a son Id, sa ressemblance (Score, de 0
à 100), sa règle (RuleName) et ses deux fiches (First, Second) : Id, Name, Email, Mobile, Company, City,
CreationDate. Seules sont rendues les paires dont vous voyez les deux fiches.
Pour un contact précis : GET https://staging-api.docage.com/Duplicates/Of/Contact/{id} rend ses doublons (Candidates), selon les règles appliquées, et le
nombre de ceux que vous ne voyez pas (HiddenCount), jamais nommés.
3Préparer la fusion
POST https://staging-api.docage.com/Duplicates/Preview/Contact
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
["00000000-0000-4000-8000-000000000201", "00000000-0000-4000-8000-000000000202"]curl -X POST "https://staging-api.docage.com/Duplicates/Preview/Contact" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '["00000000-0000-4000-8000-000000000201", "00000000-0000-4000-8000-000000000202"]'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Duplicates/Preview/Contact");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
[
"00000000-0000-4000-8000-000000000201",
"00000000-0000-4000-8000-000000000202"
]
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Duplicates/Preview/Contact", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(["00000000-0000-4000-8000-000000000201", "00000000-0000-4000-8000-000000000202"])
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;Rien n'est modifié. La réponse propose la fiche à garder (SuggestedKeptRecordId), dit pour chaque fiche si vous pouvez la garder
(CanKeep : la modifier) ou l'absorber (CanAbsorb : la supprimer), donne pour chaque propriété la valeur de chaque fiche (Fields :
Name, Values, SuggestedRecordId, Differs), et ce qui suivra la fiche gardée (References : « 3 emails, 1 parcours »).
4Fusionner
POST https://staging-api.docage.com/Duplicates/Merge
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"EntityTypeName": "Contact",
"KeptRecordId": "00000000-0000-4000-8000-000000000201",
"AbsorbedRecordIds": ["00000000-0000-4000-8000-000000000202"],
"ValueChoices": {
"Mobile": "00000000-0000-4000-8000-000000000202",
"CustomField:NumeroAdherent": "00000000-0000-4000-8000-000000000202"
},
"PairId": "00000000-0000-4000-8000-000000000901"
}curl -X POST "https://staging-api.docage.com/Duplicates/Merge" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"EntityTypeName": "Contact",
"KeptRecordId": "00000000-0000-4000-8000-000000000201",
"AbsorbedRecordIds": ["00000000-0000-4000-8000-000000000202"],
"ValueChoices": {"Mobile": "00000000-0000-4000-8000-000000000202", "CustomField:NumeroAdherent": "00000000-0000-4000-8000-000000000202"},
"PairId": "00000000-0000-4000-8000-000000000901"
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Duplicates/Merge");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"EntityTypeName": "Contact",
"KeptRecordId": "00000000-0000-4000-8000-000000000201",
"AbsorbedRecordIds": [
"00000000-0000-4000-8000-000000000202"
],
"ValueChoices": {
"Mobile": "00000000-0000-4000-8000-000000000202",
"CustomField:NumeroAdherent": "00000000-0000-4000-8000-000000000202"
},
"PairId": "00000000-0000-4000-8000-000000000901"
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Duplicates/Merge", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"EntityTypeName": "Contact",
"KeptRecordId": "00000000-0000-4000-8000-000000000201",
"AbsorbedRecordIds": ["00000000-0000-4000-8000-000000000202"],
"ValueChoices": {"Mobile": "00000000-0000-4000-8000-000000000202", "CustomField:NumeroAdherent": "00000000-0000-4000-8000-000000000202"},
"PairId": "00000000-0000-4000-8000-000000000901"
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;ValueChoicesdésigne, pour une propriété — ouCustomField:suivi du nom technique d'un champ personnalisé —, la fiche dont la valeur est retenue. Une propriété absente garde la valeur de la fiche gardée, ou, si elle n'en a pas, celle de la première fiche absorbée qui en a une.PairId, facultatif, désigne la paire d'où vient la fusion : elle passe « Fusionnée ».- Ce qui désignait une fiche absorbée — ses parcours, ses emails, ses cartes… — passe à la fiche gardée. Son email et son mobile deviennent des adresses rattachées à la fiche gardée, par lesquelles la messagerie reconnaît encore la personne.
La réponse dit ce qui a été fait (ValuesWritten, ReferencesMoved, AliasesCreated) et porte l'identifiant de chaque fusion
(MergeIds, une par fiche absorbée) : gardez-le pour la défaire. Il faut pouvoir modifier la fiche gardée et supprimer chaque fiche
absorbée ; un refus répond 422, avec son motif, et rien n'est changé.
Les paires certaines se fusionnent aussi en lot : POST https://staging-api.docage.com/Duplicates/Pairs/MergeCertain, avec la liste des paires (200 au plus),
rend le nombre de paires fusionnées (Merged), écartées (Skipped) et les motifs des refus (Errors).
5Défaire une fusion
POST https://staging-api.docage.com/Duplicates/Undo/00000000-0000-4000-8000-000000000951
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl -X POST "https://staging-api.docage.com/Duplicates/Undo/00000000-0000-4000-8000-000000000951" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Duplicates/Undo/00000000-0000-4000-8000-000000000951");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Duplicates/Undo/00000000-0000-4000-8000-000000000951", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;Possible pendant 30 jours : la fiche absorbée revient, et ce qui avait suivi la fiche gardée lui est rendu. Une valeur modifiée
depuis la fusion garde sa nouvelle valeur. Les fusions d'une fiche se relisent par GET https://staging-api.docage.com/Duplicates/Merges/Contact/{id} : leur
Id, CanUndo et l'échéance (UndoDeadlineUtc). Un délai passé, une fusion déjà défaite ou un droit manquant répondent 422. À
l'échéance, les fiches absorbées sont supprimées définitivement.
6Reconnaître un contact absorbé
Une fiche absorbée n'est ni dans la liste ni dans la corbeille. Pendant les 30 jours, elle se lit encore par son identifiant :
GET https://staging-api.docage.com/Contacts/{id} la rend avec EntityState à -4 (fusionnée). Pour savoir quelle fiche la remplace :
GET https://staging-api.docage.com/Duplicates/Merges/Contact/00000000-0000-4000-8000-000000000202
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/Duplicates/Merges/Contact/00000000-0000-4000-8000-000000000202" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/Duplicates/Merges/Contact/00000000-0000-4000-8000-000000000202");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Duplicates/Merges/Contact/00000000-0000-4000-8000-000000000202", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La fusion où elle est absorbée (AbsorbedRecordId) donne la fiche gardée (KeptRecordId) : reportez cet identifiant dans votre
outil. Après l'échéance, la lecture par identifiant rend null.
7Écarter une fausse paire
POST https://staging-api.docage.com/Duplicates/Pairs/Ignore, avec la liste des paires : elles sont marquées « Pas un doublon », et aucune règle ne les signalera plus. La réponse est le nombre de paires écartées.POST https://staging-api.docage.com/Duplicates/IgnoreRecords/Contact, avec{ "RecordId": "…", "OtherIds": ["…"] }: la même chose entre une fiche et d'autres, qu'une règle les ait déjà rapprochées ou non.
Seules comptent les paires dont vous voyez les deux fiches.
Les pièges
ExternalIdreste celui de la fiche gardée : la référence de votre outil posée sur une fiche absorbée ne se retrouve plus dans la liste. Suivez le § 6.- Une adresse absorbée devient une adresse rattachée : un filtre de liste sur
Emailne la retrouve plus. MergeIdsest le seul identifiant qui défait une fusion ; sans lui, relisez les fusions de la fiche (§ 5).