Dédoublonnage
Chaque requête POST /leads lance une détection automatique des doublons avant l’insertion. Cela évite de polluer votre base avec des entrées en double venant de sources différentes.
Ordre de détection
Section intitulée « Ordre de détection »Klozeo examine les clés ci-dessous dans l’ordre et s’arrête à la première qui trouve un lead existant. Chaque vérification reste à l’intérieur de votre compte.
| Ordre | Clé | Correspond quand | Résultat |
|---|---|---|---|
| 1 | email |
Même email, sans tenir compte de la casse ni des espaces autour | Fusion dans le lead existant |
| 2 | siren |
Même SIREN. Un SIRET de 14 chiffres est d’abord ramené à son SIREN de 9 chiffres | Fusion dans le lead existant |
| 3 | source_id |
Même source_id (espaces autour ignorés) |
Fusion dans le lead existant |
| 4 | phone + name |
Même téléphone, caractère pour caractère (seuls les espaces autour sont ignorés, donc +33 1 23 45 67 89 et 0123456789 ne correspondent pas), et un nom similaire à plus de 80 % |
Fusion dans le lead existant |
| 5 | name + city |
Même nom et même ville, sans tenir compte de la casse | Crée un nouveau lead et renvoie potential_duplicate_id |
La similarité des noms ignore la casse et les espaces autour, et compte les modifications d’un seul caractère (insertion, suppression, remplacement) nécessaires pour passer d’un nom à l’autre. Les noms correspondent quand ces modifications représentent moins de 20 % de la longueur du nom le plus long. Acme Corp et ACME Corp. correspondent (1 modification sur 10 caractères) ; Acme Corp et ACME Corporation ne correspondent pas (7 modifications sur 16).
Quand plusieurs leads partagent une clé
Section intitulée « Quand plusieurs leads partagent une clé »- Email, SIREN,
source_id: dans un compte, au plus un lead porte un email donné (sans tenir compte de la casse), un SIREN ou unsource_id, donc une correspondance n’est jamais ambiguë. Attribuer à l’un de ces champs une valeur déjà portée par un autre lead est refusé avec409 Conflict. - Téléphone + nom : plusieurs leads peuvent partager un numéro de téléphone. Klozeo examine jusqu’à 5 leads portant ce numéro, sans ordre garanti, et fusionne dans le premier dont le nom correspond.
- Nom + ville : plusieurs leads peuvent partager un nom et une ville.
potential_duplicate_iden désigne un, sans ordre garanti.
Ce qu’une fusion modifie
Section intitulée « Ce qu’une fusion modifie »Quand un doublon est trouvé aux étapes 1 à 4, le lead existant conserve son id et est mis à jour :
- Chaque champ que vous envoyez avec une valeur remplace la valeur stockée. Les champs que vous omettez, ou que vous envoyez vides, restent inchangés.
- Une fusion ne modifie jamais le
name, lestatus, l’assigneeni les coordonnées (latitude,longitude), même si vous les envoyez. Modifiez-les avecPUT /leads/{id}. - Les champs verrouillés ne sont jamais écrits.
- Les
tagssont ajoutés aux tags existants, jamais remplacés : un tag commedo-not-contactsurvit à chaque import. Les doublons sont ignorés ; les tags existants gardent leur ordre. Pour remplacer toute la liste, envoyez unPUT /leads/{id}explicite. - Les
attributespersonnalisés fusionnent sur lenameexact, sensible à la casse : un nouveau nom est ajouté, un nom existant du même type est mis à jour avec la valeur entrante (y comprisfalse,0etnull). Les attributs que vous n’envoyez pas restent inchangés. Un attribut dont le type diffère de celui stocké, ou dont le nom correspond à plusieurs attributs stockés, est ignoré et listé dans la réponse avec sa raison. Les attributs personnalisés ne peuvent pas être verrouillés. - Opposition au contact n’est jamais levée par une fusion.
last_interaction_atest toujours mis à jour, même si aucun champ n’a changé : chaque nouvel envoi compte comme une nouvelle interaction.
Opposition au contact
Section intitulée « Opposition au contact »Une opposition au contact (do_not_contact: true) n’est jamais levée par une fusion : un import qui envoie "do_not_contact": false, ou aucune valeur, la laisse en place, avec sa date et son origine initiales. Un import qui envoie "do_not_contact": true ajoute l’opposition au lead existant. Seul un DELETE /leads/{id}/do-not-contact explicite (tableau de bord ou API REST, jamais MCP) la lève, et la piste d’audit enregistre qui l’a fait.
Verrous de champs
Section intitulée « Verrous de champs »Verrouillez un champ que vous avez vérifié avec POST /leads/{id}/locks/{field}, par exemple POST /leads/{id}/locks/email. À partir de là, aucune fusion, aucun import ni enrichissement d’entreprise n’écrit ce champ ; la réponse de fusion le liste dans skipped_locked_fields. Vous pouvez toujours le modifier vous-même avec PUT /leads/{id}. Déverrouillez-le avec DELETE /leads/{id}/locks/{field}. Chaque verrouillage et déverrouillage est enregistré dans la piste d’audit du lead, avec la personne qui l’a fait. → Référence API
Dans le tableau de bord, ouvrez un lead : le panneau Verrous de champ liste les champs verrouillés, et une icône de verrou les signale dans la vue d’ensemble. Les propriétaires et les éditeurs choisissent les champs sous Choisir les champs verrouillés ; les viewers voient les verrous sans pouvoir les modifier.
Réponse en cas de doublon
Section intitulée « Réponse en cas de doublon »{ "id": "cl_existing...", "message": "Duplicate detected, existing lead updated", "created_at": 1703520000, "duplicate": true, "attribute_changes": [ { "name": "segment", "action": "added" }, { "name": "budget", "action": "updated" }, { "name": "active", "action": "ignored", "reason": "type_mismatch" } ], "skipped_locked_fields": ["email"]}Le statut HTTP est 200 OK (et non 201) lors d’une fusion. id et created_at sont ceux du lead existant.
attribute_changes liste chaque attribut entrant dans l’ordre de la requête : added, updated (même quand la valeur ne change pas), ou ignored avec une reason valant type_mismatch ou ambiguous_name. Le champ est omis quand la requête n’envoie aucun attribut. Un élément fusionné de POST /leads/batch et les outils MCP create_lead et batch_create_leads renvoient la même liste. Si un attribut est invalide, toute la fusion est refusée et rien n’est écrit.
skipped_locked_fields liste les champs entrants qui n’ont pas été écrits parce qu’ils sont verrouillés. Le champ est omis quand aucun champ verrouillé n’a été envoyé. Une fusion peut renvoyer les deux listes.
Doublon potentiel
Section intitulée « Doublon potentiel »Sur une correspondance nom + ville uniquement, un nouveau lead est créé (201 Created) et la réponse contient une indication :
{ "id": "cl_new...", "message": "Lead created successfully", "created_at": 1703520000, "potential_duplicate_id": "cl_similar..."}Les deux leads sont conservés. Quand vous créez un lead depuis le tableau de bord, un avertissement Possible duplicate detected affiche l’ID du lead similaire. Klozeo ne les fusionne pas à votre place : comparez les deux leads, puis mettez à jour ou supprimez celui que vous ne voulez pas.
updated_at et last_interaction_at
Section intitulée « updated_at et last_interaction_at »| Champ | Mis à jour quand |
|---|---|
updated_at |
Une écriture définit au moins un champ du lead, même avec la valeur qu’il avait déjà |
last_interaction_at |
Un envoi arrive, y compris une fusion qui ne change rien |
Utilisez sort_by=last_interaction_at&sort_order=DESC pour trouver les leads avec l’activité la plus récente, indépendamment des changements de données.
Les nouvelles tentatives ne sont pas des doublons
Section intitulée « Les nouvelles tentatives ne sont pas des doublons »Quand une requête dépasse le délai, vous ne savez pas si elle est arrivée. Envoyez l’en-tête Idempotency-Key : si vous renvoyez la même requête avec la même clé, Klozeo renvoie le premier résultat et n’écrit rien une seconde fois. La clé protège les nouvelles tentatives d’une seule requête. Elle ne compare pas les leads : les clés de détection ci-dessus décident, pour chaque nouvelle requête, entre création et fusion. Deux requêtes avec des clés différentes peuvent encore créer deux leads. → Retries and idempotency
Piste d’audit
Section intitulée « Piste d’audit »Chaque création, fusion, mise à jour, suppression et changement de score est enregistré, tout comme chaque changement d’attribut personnalisé et de note, avec le membre qui l’a fait (pour une clé API, le propriétaire du compte, et la clé dans api_key_id). Dans le tableau de bord, l’historique Ne pas contacter d’un lead affiche le nom de la clé API pour les changements enregistrés avec api_key_id, même après sa révocation. Dans le tableau de bord, cet historique affiche l’e-mail du membre qui a agi à tous les membres de l’espace de travail, et un membre retiré apparaît comme un ancien membre. Dans le tableau de bord, l’onglet historique d’un lead montre ses 50 changements les plus récents, sans les mises à jour automatiques du score : voir Auteurs des notes et historique. Récupérez l’historique d’un lead :
GET /leads/{id}/changes?limit=50{ "changes": [ { "id": "uuid-...", "lead_id": "cl_...", "workspace_id": "workspace_id", "user_id": "workspace_id", "actor_id": "actor_user_id", "api_key_id": "api_key_id", "action": "merged", "reason": "dedup:email", "created_at": 1703520000 } ], "count": 1}action |
Quand |
|---|---|
created |
Nouveau lead inséré |
merged |
Fusion par dédoublonnage dans un lead existant (reason vaut dedup:email, dedup:siren, dedup:source_id ou dedup:phone+name) |
updated |
PUT /leads/{id} explicite (ou mise à jour en lot), ou un attribut personnalisé ajouté, modifié ou supprimé (reason vaut attribute:<nom>) |
deleted |
Lead supprimé |
assigned, unassigned |
L’assigné d’un lead a été défini ou retiré ; assignee contient l’email du nouvel assigné sur assigned, before et after sont vides |
note.created, note.updated, note.deleted |
Une note ajoutée, modifiée ou supprimée ; before et after contiennent la note. La suppression d’un lead écrit une entrée note.deleted par note, avec reason à lead:deleted |
scored |
Le score stocké a changé ; before et after ne contiennent que score. reason vaut score:refresh (après une écriture de lead ou d’attribut personnalisé), score:recalculate (POST /leads/{id}/score ou POST /leads/score/batch) ou score:rules (rescore automatique après un changement de règle de scoring, sans actor_id) |
before / after sont des copies JSON du lead (de la note pour les entrées note.*). La limit par défaut est 50 (100 au maximum). L’historique d’un lead supprimé est conservé 30 jours. Un traitement quotidien efface ensuite before, after et assignee : les valeurs ont disparu sous 31 jours après la suppression. Chaque entrée garde l’action, la date, le membre ou la clé API qui a agi, channel, reason et changed_fields, le nom des champs modifiés. L’historique d’un lead non supprimé n’est pas effacé.
api_key_id est l’id que GET /api-keys donne à la clé API à l’origine d’un changement fait avec une clé, par l’API REST ou par le serveur MCP. Pour un tel changement, actor_id est le propriétaire du compte. Le champ api_key_id reste dans l’historique après la révocation de la clé ; les entrées écrites avant son ajout n’en ont pas. Un changement fait depuis le tableau de bord n’a pas d’api_key_id. Un changement fait par un assistant connecté en OAuth n’en a pas non plus, et son actor_id est le membre qui a connecté l’assistant.