---
title: "Webhook : enrichment.completed"
method: POST
path: "enrichment.completed"
tags: ["Person Enrichment"]
---

# Webhook : enrichment.completed

`POST enrichment.completed` (webhook)

Envoyé sur votre `callbackUrl` quand un enrichissement async se termine. En-tête `X-Listar-Signature: t=<ts>,v1=<hmac_sha256>`. Répondez 2xx pour acquitter.

## Payload

- WebhookPayload: Corps POST sur votre callbackUrl quand un enrichissement async se termine.
  - `event` 'enrichment.completed'
  - `id` string: ID de la recherche (= GET /search/results/{id}).
  - `status` 'paid' | 'free' | 'pending_payment': `paid`/`free` → result rempli. `pending_payment` → result null, payez via /search/pay-with-credit.
  - `query` UnifiedSearchQuery: Au moins un critère identifiant est requis : `firstName`, `phone`, `email`, `linkedinSlug`, `facebookSlug`, `facebookId`, `doctolibSlug` ou `maltSlug`. Plus l’identifiant est fort (slug LinkedIn fourni, RPPS, fiche d’annuaire), plus la livraison est ancrée : un simple nom + zone ne suffit pas à livrer un datapoint.
    - `firstName` string
    - `lastName` string
    - `email` string, email
    - `phone` string: Format souple : `+33 6 12 34 56 78`, `0612345678`…
    - `linkedinSlug` string: Slug du profil LinkedIn (la partie après /in/).
    - `facebookSlug` string
    - `facebookId` string
    - `doctolibSlug` string: Chemin de fiche Doctolib (`<specialite>/<ville>/<praticien>`, ex. `medecin-generaliste/paris/jean-dupont`) ou URL complète de la fiche. Son portrait sert de photo de RÉFÉRENCE au face match.
    - `maltSlug` string
    - `company` string
    - `jobTitle` string: Intitulé de poste / spécialité - sert de contexte anti-homonyme.
    - `city` string
    - `postalCode` string: Code postal EXACT du contact - requis pour activer l'ancre `acceptGeoExactUnique` (livraison mobile ou email sur nom + code postal exact).
    - `country` string
    - `birthYear` integer
    - `gender` 'male' | 'female' | 'M' | 'F' | 'm' | 'f'
    - `rppsId` string: Identifiant national RPPS (11 chiffres) du professionnel de santé. ANCRE FORTE : une fiche de l'annuaire officiel partageant ce RPPS désigne la personne sans ambiguïté d'homonyme, indépendamment de la géo ou de la société.
    - `industry` string: Secteur / activité - contexte anti-homonyme quand la raison sociale est opaque.
    - `website` string: Site de l'entreprise : son domaine corrobore les emails professionnels.
    - `enrichmentType` 'both' | 'phone' | 'email': Limiter l’enrichissement au téléphone ou à l’email. Seul le canal demandé est livré et facturé.
    - `searchProEmail` boolean: Livrer les emails PROFESSIONNELS. `enrichmentType` choisit le canal (tél / email) ; celui-ci choisit quel email dans ce canal.
    - `searchPersoEmail` boolean: Livrer les emails PERSONNELS (gmail, orange…). Avec `enrichmentType: email`, `searchProEmail` et `searchPersoEmail` ne peuvent pas être tous deux à `false` (400).
    - `requirePersonalEmailProof` boolean: Ne livrer (et ne facturer) un email PERSONNEL que sur PREUVE d'appartenance : face match entre la photo du compte (Gravatar / GitHub / Google / Teams) et le portrait de référence du contact, ou ancre qui désigne la personne elle-même (RPPS, slug LinkedIn fourni, email ou téléphone d'entrée partagé). Une adresse perso n'est rattachée ni à une société ni à une ville : sans cette option, elle peut être livrée sur une simple corroboration de l'entourage. **Désactivé par défaut** (opt-in) - l'activer réduit le volume d'emails perso livrés.
    - `includeSharedEmails` boolean: Livrer aussi les boîtes PARTAGÉES d'entreprise (`contact@`, `accueil@`, `pharmacieX@…`) en plus des emails nominatifs. Désactivé par défaut (précision d'abord).
    - `acceptGeoExactUnique` boolean: Livrer les mobiles ET les emails corroborés par `nom + CODE POSTAL EXACT` quand un SEUL est trouvé à ce code postal. L’unicité est comptée canal par canal : ≥2 mobiles distincts au même CP = homonymes → mobile non livré, sans effet sur l’email, et réciproquement. Les fixes en restent exclus. Élargit la couverture sur les listes géo-précises (gérants, professions libérales) ; la géo lâche (ville / rayon) reste insuffisante. **Désactivé par défaut** (opt-in) - risque de fuite homonyme si le code postal est PARTAGÉ (siège / arrondissement) et que la cible n’a pas la donnée en base. Passer `true` pour l’activer sur des listes où le CP est le domicile/commune d’exercice réel.
    - `callbackUrl` string, uri: URL HTTPS notifiée (POST `enrichment.completed`, signé HMAC) quand un enrichissement async se termine. Recommandé pour éviter le polling.
    - `respondAsync` boolean: Répond tout de suite `202` avec l’`id`, sans attendre le résultat. Utile pour lancer beaucoup de recherches d’affilée et les récupérer ensuite via `GET /search/results/{id}` (ou par webhook). Facturation identique.
  - `result` UnifiedSearchResponse: Résultat complet d’un enrichissement (après paiement).
    - `query` UnifiedSearchQuery: Au moins un critère identifiant est requis : `firstName`, `phone`, `email`, `linkedinSlug`, `facebookSlug`, `facebookId`, `doctolibSlug` ou `maltSlug`. Plus l’identifiant est fort (slug LinkedIn fourni, RPPS, fiche d’annuaire), plus la livraison est ancrée : un simple nom + zone ne suffit pas à livrer un datapoint.
      - `firstName` string
      - `lastName` string
      - `email` string, email
      - `phone` string: Format souple : `+33 6 12 34 56 78`, `0612345678`…
      - `linkedinSlug` string: Slug du profil LinkedIn (la partie après /in/).
      - `facebookSlug` string
      - `facebookId` string
      - `doctolibSlug` string: Chemin de fiche Doctolib (`<specialite>/<ville>/<praticien>`, ex. `medecin-generaliste/paris/jean-dupont`) ou URL complète de la fiche. Son portrait sert de photo de RÉFÉRENCE au face match.
      - `maltSlug` string
      - `company` string
      - `jobTitle` string: Intitulé de poste / spécialité - sert de contexte anti-homonyme.
      - `city` string
      - `postalCode` string: Code postal EXACT du contact - requis pour activer l'ancre `acceptGeoExactUnique` (livraison mobile ou email sur nom + code postal exact).
      - `country` string
      - `birthYear` integer
      - `gender` 'male' | 'female' | 'M' | 'F' | 'm' | 'f'
      - `rppsId` string: Identifiant national RPPS (11 chiffres) du professionnel de santé. ANCRE FORTE : une fiche de l'annuaire officiel partageant ce RPPS désigne la personne sans ambiguïté d'homonyme, indépendamment de la géo ou de la société.
      - `industry` string: Secteur / activité - contexte anti-homonyme quand la raison sociale est opaque.
      - `website` string: Site de l'entreprise : son domaine corrobore les emails professionnels.
      - `enrichmentType` 'both' | 'phone' | 'email': Limiter l’enrichissement au téléphone ou à l’email. Seul le canal demandé est livré et facturé.
      - `searchProEmail` boolean: Livrer les emails PROFESSIONNELS. `enrichmentType` choisit le canal (tél / email) ; celui-ci choisit quel email dans ce canal.
      - `searchPersoEmail` boolean: Livrer les emails PERSONNELS (gmail, orange…). Avec `enrichmentType: email`, `searchProEmail` et `searchPersoEmail` ne peuvent pas être tous deux à `false` (400).
      - `requirePersonalEmailProof` boolean: Ne livrer (et ne facturer) un email PERSONNEL que sur PREUVE d'appartenance : face match entre la photo du compte (Gravatar / GitHub / Google / Teams) et le portrait de référence du contact, ou ancre qui désigne la personne elle-même (RPPS, slug LinkedIn fourni, email ou téléphone d'entrée partagé). Une adresse perso n'est rattachée ni à une société ni à une ville : sans cette option, elle peut être livrée sur une simple corroboration de l'entourage. **Désactivé par défaut** (opt-in) - l'activer réduit le volume d'emails perso livrés.
      - `includeSharedEmails` boolean: Livrer aussi les boîtes PARTAGÉES d'entreprise (`contact@`, `accueil@`, `pharmacieX@…`) en plus des emails nominatifs. Désactivé par défaut (précision d'abord).
      - `acceptGeoExactUnique` boolean: Livrer les mobiles ET les emails corroborés par `nom + CODE POSTAL EXACT` quand un SEUL est trouvé à ce code postal. L’unicité est comptée canal par canal : ≥2 mobiles distincts au même CP = homonymes → mobile non livré, sans effet sur l’email, et réciproquement. Les fixes en restent exclus. Élargit la couverture sur les listes géo-précises (gérants, professions libérales) ; la géo lâche (ville / rayon) reste insuffisante. **Désactivé par défaut** (opt-in) - risque de fuite homonyme si le code postal est PARTAGÉ (siège / arrondissement) et que la cible n’a pas la donnée en base. Passer `true` pour l’activer sur des listes où le CP est le domicile/commune d’exercice réel.
      - `callbackUrl` string, uri: URL HTTPS notifiée (POST `enrichment.completed`, signé HMAC) quand un enrichissement async se termine. Recommandé pour éviter le polling.
      - `respondAsync` boolean: Répond tout de suite `202` avec l’`id`, sans attendre le résultat. Utile pour lancer beaucoup de recherches d’affilée et les récupérer ensuite via `GET /search/results/{id}` (ou par webhook). Facturation identique.
    - `totalResults` integer
    - `profiles` EnrichedProfile[]: Profil retenu pour la personne recherchée. Les homonymes non vérifiés ne sont jamais renvoyés à une clé API.
      - `firstName` string, nullable
      - `lastName` string, nullable
      - `gender` string, nullable
      - `birthDate` string, nullable
      - `phone` string, nullable: Meilleur téléphone (raccourci de phones[0]).
      - `phones` PhoneInfo[]
        - `number` string
        - `type` 'mobile' | 'fixed' | 'special' | 'unknown'
        - `verified` boolean, nullable
        - `verifiedAt` string, date-time, nullable
        - `carrier` string, nullable: Opérateur auquel l'ARCEP a attribué le bloc du numéro (mobiles français uniquement, `null` ailleurs). Ce n'est pas l'opérateur actuel d'un numéro porté.
        - `isVirtual` boolean, nullable: Numéro virtuel (application de second numéro, téléphonie cloud, tranche VoIP) : `true`, ou `null` quand rien ne l'indique. Jamais `false` : un numéro porté vers un opérateur virtuel garde le préfixe de son opérateur d'origine. Mobiles français : bloc ARCEP attribué à un opérateur de numéros virtuels. Ailleurs : tranche que le plan national réserve à la VoIP.
        - `isWhatsApp` boolean, nullable
        - `isWhatsAppBusiness` boolean, nullable: Compte WhatsApp Business.
        - `pushname` string, nullable: Nom affiché sur WhatsApp.
        - `usage` 'professional' | 'personal' | 'unknown': Usage probable de la ligne : professionnelle ou personnelle. `unknown` quand les indices manquent ou se contredisent ; absent quand l’analyse n’a pas tourné.
        - `usageConfidence` 'high' | 'medium' | 'low', nullable: Confiance dans `usage`, null quand il vaut `unknown`.
        - `faceMatch` FaceMatch, nullable: Résultat de comparaison faciale (validation d’identité).
          - `isMatch` boolean
          - `confidence` 'high' | 'medium' | 'low'
      - `email` string, nullable: Meilleur email (raccourci de emails[0]).
      - `emails` EmailInfo[]
        - `address` string, email
        - `verified` 'verified' | 'smtp_valid' | 'invalid' | 'disposable' | 'unknown', nullable
        - `verifiedAt` string, date-time, nullable
        - `type` 'personal' | 'professional'
        - `stale` boolean: Email pro dont le domaine ne correspond plus à la société courante.
        - `domainUncorroborated` boolean: Domaine de l'email non corroboré par la société demandée ni par son domaine officiel. L'adresse est livrée telle quelle : à vérifier avant prospection.
      - `address` string, nullable
      - `city` string, nullable
      - `postalCode` string, nullable
      - `company` string, nullable
      - `jobTitle` string, nullable
      - `linkedinUrl` string, nullable
      - `facebookUrl` string, nullable
      - `twitterUrl` string, nullable
    - `linkedinParsed` object
      - `jobTitle` string
      - `company` string
    - `bestPhone` PhoneInfo
      - `number` string
      - `type` 'mobile' | 'fixed' | 'special' | 'unknown'
      - `verified` boolean, nullable
      - `verifiedAt` string, date-time, nullable
      - `carrier` string, nullable: Opérateur auquel l'ARCEP a attribué le bloc du numéro (mobiles français uniquement, `null` ailleurs). Ce n'est pas l'opérateur actuel d'un numéro porté.
      - `isVirtual` boolean, nullable: Numéro virtuel (application de second numéro, téléphonie cloud, tranche VoIP) : `true`, ou `null` quand rien ne l'indique. Jamais `false` : un numéro porté vers un opérateur virtuel garde le préfixe de son opérateur d'origine. Mobiles français : bloc ARCEP attribué à un opérateur de numéros virtuels. Ailleurs : tranche que le plan national réserve à la VoIP.
      - `isWhatsApp` boolean, nullable
      - `isWhatsAppBusiness` boolean, nullable: Compte WhatsApp Business.
      - `pushname` string, nullable: Nom affiché sur WhatsApp.
      - `usage` 'professional' | 'personal' | 'unknown': Usage probable de la ligne : professionnelle ou personnelle. `unknown` quand les indices manquent ou se contredisent ; absent quand l’analyse n’a pas tourné.
      - `usageConfidence` 'high' | 'medium' | 'low', nullable: Confiance dans `usage`, null quand il vaut `unknown`.
      - `faceMatch` FaceMatch, nullable: Résultat de comparaison faciale (validation d’identité).
        - `isMatch` boolean
        - `confidence` 'high' | 'medium' | 'low'
    - `bestEmail` EmailInfo
      - `address` string, email
      - `verified` 'verified' | 'smtp_valid' | 'invalid' | 'disposable' | 'unknown', nullable
      - `verifiedAt` string, date-time, nullable
      - `type` 'personal' | 'professional'
      - `stale` boolean: Email pro dont le domaine ne correspond plus à la société courante.
      - `domainUncorroborated` boolean: Domaine de l'email non corroboré par la société demandée ni par son domaine officiel. L'adresse est livrée telle quelle : à vérifier avant prospection.
    - `excluded` boolean: Présent et `true` quand la personne a exercé ses droits RGPD (effacement ou opposition) : `profiles` est vide et rien n’est facturé.
    - `paidWithCredit` boolean
    - `creditDeducted` integer: Montant débité en centimes.
    - `freeResult` boolean
    - `id` string: ID de la recherche, **présent dans toutes les réponses** (sync 200 comme async 202/402). Ré-interrogeable à tout moment via `GET /search/results/{id}` - vous pouvez donc traiter sync et async avec le même code.
    - `searchResultId` string: Alias historique de `id` (même valeur). Conservé pour compatibilité - préférez `id`.
  - `completedAt` string, date-time

## Acknowledgement `200`

Acquittement.

---

[API](https://skmtc.dev/listar/apis/listar-api.md) · [All operations](https://skmtc.dev/listar/apis/listar-api/llms.txt) · [OpenAPI document](https://skmtc.dev/listar/apis/listar-api/revisions/d8b4a5e9acc8?raw)
