Sign inGet started

Recibir tarjetas de contacto de WhatsApp

contact_cards es el único campo que funciona igual en ambas direcciones. Un contacto puede compartir una tarjeta de su agenda, y un toque en una solicitud de información de contacto que enviaste también llega aquí, con el número que eligió revelar.

Qué contiene una tarjeta de contacto entrante

contact_cards siempre es un array, y origin indica cómo llegó la tarjeta:
Ejemplo de código
{
  "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"
}
originCómo llegó la tarjeta
contact_requestEl contacto pulsó un botón que enviaste para pedir su número
otherEl contacto compartió una tarjeta en el chat sin que se la pidieras
Comprueba origin antes de tratar una tarjeta como respuesta a tu solicitud. Es la única señal que distingue ambos casos, y una tarjeta compartida sin pedirla puede contener los datos de un tercero en lugar de los del contacto. La lista de valores es abierta, así que trata cualquier valor que no reconozcas como otra forma de compartir añadida después.
Una tarjeta enviada por este espacio de trabajo se lee de vuelta sin origin, y así se distingue una tarjeta saliente de una entrante en el mismo campo.

Qué contiene un toque y qué contiene una tarjeta compartida

Ambos llegan con distinta cantidad de detalle, y ningún campo de la tarjeta es obligatorio: WhatsApp envía las partes que la tarjeta contiene y omite el resto, así que una tarjeta con solo un origin llega igualmente en lugar de descartarse.
CampoAl pulsar un botónEn una tarjeta compartida en el chat
phone_numbers[].phone_number, typeEl número que el contacto eligió revelarLos números que contenga la tarjeta
vcardOmitido; un toque solo lleva el númeroLa tarjeta en formato vCard
name, org, birthday, emails, urls, addressesLo que WhatsApp envíe, que normalmente es nadaPresentes cuando la tarjeta los contiene
Ejemplo de código
{
  "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" }]
    }
  ]
}
Dos campos requieren cuidado al procesarlos. phone_number se normaliza a E.164 cuando se puede analizar y se pasa tal cual lo almacenó el dispositivo del contacto cuando no, incluidas las extensiones, así que analízalo de forma defensiva en lugar de asumir E.164. birthday sale del dispositivo sin validar y se pasa como texto con forma YYYY-MM-DD en lugar de tiparlo como fecha, así que no asumas que se puede analizar. Una etiqueta type en una tarjeta recibida llega en minúsculas, y WhatsApp no define un vocabulario para ella, así que compárala sin distinguir mayúsculas en lugar de hacer un switch sobre CELL.

El número de teléfono que un contacto revela

Un contacto que ha adoptado un nombre de usuario de WhatsApp te contacta mediante un identificador de usuario con alcance de negocio sin número de teléfono en from. Una solicitud de información de contacto es la forma de pedir el número, y este campo es donde llega la respuesta, con origin: "contact_request" y el número en phone_numbers.
No se garantiza que el número revelado sea el número desde el que chatean: Meta advierte que el identificador de un usuario y su número de teléfono pueden no coincidir siempre, así que almacena el número revelado como un dato independiente en lugar de sobrescribir la identidad en from.

El payload del webhook

whatsapp.received contiene el array contact_cards en el envelope del evento:
Ejemplo de código
{
  "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
  }
}

Aspectos a tener en cuenta

  • Una solicitud rechazada no produce nada. WhatsApp muestra al contacto una hoja de compartir, y cerrarla no envía ningún mensaje ni dispara ningún webhook, así que un flujo que espera un número necesita su propio tiempo de espera en lugar de un evento de rechazo.
  • Dos solicitudes pendientes son indistinguibles. Una tarjeta que responde a una solicitud de información de contacto no lleva in_reply_to_message_id, así que una segunda solicitud enviada antes de que se responda la primera no se puede asociar con su propia respuesta.
  • El array puede contener varias tarjetas. Un contacto que comparte varias tarjetas en un mismo mensaje llena varias entradas, cada una con su propio origin.
  • Una tarjeta son datos de contacto que tú no recopilaste. Puede contener el nombre, los números y la fecha de nacimiento de un tercero, así que aplica las mismas reglas de retención y consentimiento que aplicarías a cualquier otro dato personal antes de almacenarla.

Próximos pasos