Sign inGet started

Recevoir des fiches contact WhatsApp

contact_cards est le seul bras qui porte le même champ dans les deux sens. Un contact peut partager une fiche depuis son carnet d'adresses, et un appui sur une demande d'informations de contact que vous avez envoyée arrive ici aussi, avec le numéro que le contact a choisi de communiquer.

Ce que contient une fiche contact entrante

contact_cards est toujours un tableau, et origin indique comment la fiche est arrivée :
Exemple de code
{
  "id": "wam_01kyg9v3timy1w7n0r4cbh9ukf",
  "direction": "inbound",
  "from": { "phone_number": "+14155550100" },
  "to": { "phone_number": "+13124495569" },
  "status": "received",
  "contact_cards": [
    {
      "origin": "contact_request",
      "phone_numbers": [{ "phone_number": "+14155550100", "type": "cell" }]
    }
  ],
  "created_at": "2026-08-25T09:27:45Z"
}
originComment la fiche est arrivée
contact_requestLe contact a appuyé sur un bouton que vous avez envoyé pour demander son numéro
otherLe contact a partagé une fiche dans la conversation, spontanément
Vérifiez origin avant de traiter une fiche comme une réponse à votre demande. C'est le seul signal qui distingue les deux, et une fiche partagée spontanément peut désigner un tiers plutôt que le contact lui-même. La liste de valeurs est ouverte : traitez toute valeur que vous ne reconnaissez pas comme un autre mode de partage ajouté depuis.
Une fiche envoyée par cet espace de travail est relue sans aucun origin, ce qui permet de distinguer une fiche sortante d'une fiche entrante sur le même champ.

Ce que contient un appui, et ce que contient une fiche partagée

Les deux arrivent avec des niveaux de détail différents, et rien n'est obligatoire sur une fiche : WhatsApp envoie les parties que la fiche contient et omet le reste, donc une fiche ne portant qu'un origin arrive quand même au lieu d'être ignorée.
ChampSur un appui de boutonSur une fiche partagée dans la conversation
phone_numbers[].phone_number, typeLe numéro que le contact a choisi de communiquerLes numéros que la fiche contient
vcardOmis ; un appui ne porte que le numéroLa fiche au format vCard
name, org, birthday, emails, urls, addressesCe que WhatsApp envoie, c'est-à-dire généralement rienPrésents quand la fiche les contient
Exemple de code
{
  "contact_cards": [
    {
      "origin": "other",
      "vcard": "BEGIN:VCARD\nVERSION:3.0\nN:Johnson;Barbara;;;\nTEL;type=CELL:+16505551234\nEND:VCARD\n",
      "name": {
        "formatted_name": "Barbara J. Johnson",
        "first_name": "Barbara",
        "last_name": "Johnson"
      },
      "org": { "company": "Northside Plumbing" },
      "phone_numbers": [{ "phone_number": "+16505551234", "type": "cell" }]
    }
  ]
}
Deux champs demandent de la prudence à l'analyse. phone_number est normalisé en E.164 lorsqu'il peut être interprété, et transmis tel quel, tel que l'appareil du contact l'a stocké, dans le cas contraire (une extension en fait partie) : analysez-le de manière défensive plutôt que de supposer le format E.164. birthday provient de l'appareil sans validation et est transmis sous forme de texte au format YYYY-MM-DD plutôt que typé comme une date : ne supposez pas qu'il est analysable. Un libellé type sur une fiche reçue est en minuscules, et WhatsApp ne définit aucun vocabulaire pour ce champ : comparez-le sans tenir compte de la casse plutôt que de faire un switch sur CELL.

Le numéro de téléphone communiqué par un contact

Un contact qui a adopté un nom d'utilisateur WhatsApp vous contacte par identifiant utilisateur à portée business sans numéro de téléphone sur from. Une demande d'informations de contact est le moyen de demander le numéro, et ce bras est l'endroit où la réponse arrive, avec origin: "contact_request" et le numéro dans phone_numbers.
Le numéro communiqué n'est pas forcément le numéro depuis lequel le contact discute : Meta prévient que l'identifiant d'un utilisateur et son numéro de téléphone ne correspondent pas toujours. Stockez donc le numéro communiqué comme une donnée à part plutôt que d'écraser l'identité sur from.

Le payload du webhook

whatsapp.received porte le tableau contact_cards sur l'enveloppe de l'événement :
Exemple de code
{
  "type": "whatsapp.received",
  "timestamp": "2026-08-25T09:27:45.019Z",
  "data": {
    "whatsapp_id": "wam_01kyg9v3timy1w7n0r4cbh9ukf",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "direction": "inbound",
    "from": { "phone_number": "+14155550100", "bsuid": "US.13491208655302741918" },
    "to": { "phone_number": "+13124495569" },
    "contact_cards": [
      {
        "origin": "contact_request",
        "phone_numbers": [{ "phone_number": "+14155550100", "type": "cell" }]
      }
    ],
    "tags": null,
    "metadata": null
  }
}

Points d'attention

  • Une demande refusée ne produit rien. WhatsApp affiche au contact une feuille de partage, et la fermer n'envoie aucun message et ne déclenche aucun webhook. Un flux en attente d'un numéro a donc besoin de son propre délai d'expiration plutôt que d'un événement de refus à surveiller.
  • Deux demandes en attente sont indiscernables. Une fiche répondant à une demande d'informations de contact ne porte aucun in_reply_to_message_id : une seconde demande envoyée avant que la première ne reçoive de réponse ne peut pas être associée à sa propre réponse.
  • Le tableau peut contenir plusieurs fiches. Un contact qui partage plusieurs fiches dans un même message remplit plusieurs entrées, chacune avec son propre origin.
  • Une fiche est une donnée de contact que vous n'avez pas collectée. Elle peut contenir le nom, les numéros et la date de naissance d'un tiers : appliquez les mêmes règles de conservation et de consentement que pour toute autre donnée personnelle avant de la stocker.

Étapes suivantes