Ir al contenido

Identidades: PN, LID y peer_ref

En WhatsApp, una misma persona puede aparecer con dos identificadores distintos, y no siempre con su teléfono. Si tu app usa el número de teléfono como clave del contacto, tarde o temprano va a ver un mensaje sin número, o el mismo contacto dos veces. El motor resuelve eso con el peer_ref: una clave estable por instancia que agrupa todos los identificadores comprobados de un chat.

WhatsApp nombra cada chat con un JID: un texto usuario@servidor. El servidor dice qué es:

JID Qué es clase en el motor
CONTACTO@s.whatsapp.net PN (phone number): la cuenta por su número de teléfono. usuario
CONTACTO@lid LID (linked identity): un identificador opaco de la misma cuenta, que no revela el número. usuario
GRUPO@g.us Un grupo. grupo
CANAL@newsletter Un canal. canal
…@broadcast Una lista de difusión o los status. difusion

WhatsApp usa cada vez más el LID: en los grupos donde alguien oculta su número, en las comunidades y, en muchos teléfonos, también en los chats 1:1. Un LID no se puede convertir en teléfono. El único modo de saber que un LID y un PN son la misma persona es que el servidor de WhatsApp los informe juntos.

Para cada instancia, el motor guarda un peer por cada chat o persona que conoce, con un peer_ref (un UUID) y sus alias: los JID que se comprobaron como suyos.

  • Es estable: si el contacto aparece hoy por su PN y mañana por su LID, sigue siendo el mismo peer_ref, y el mismo mensaje que llega por los dos caminos no se duplica.
  • Es por instancia: el mismo contacto tiene un peer_ref distinto en cada instancia, aunque sean de la misma app. No lo uses como clave global de la persona.
  • No es de WhatsApp: es del motor. No lo encontrarás en un teléfono ni en otra herramienta.

Un alias solo se suma a un peer cuando el servidor informa los dos identificadores en el mismo hecho: el remitente de un mensaje con su identificador alternativo, el destinatario de un recibo, el mapa LID↔PN que el SDK guardó para esa instancia, o una consulta al servidor. Nunca por el nombre, ni por parecido, ni por lo que sabe otra instancia.

Si el motor ya tenía dos peers (uno por el PN y otro por el LID, vistos por separado) y después comprueba que son la misma persona, los une en el más antiguo: los mensajes, el estado de los chats, las etiquetas y el contacto pasan a ese peer. El peer_ref del más nuevo deja de existir, y el ledger lo avisa con un peer.merged: el peer que queda, con sus alias, y los peer_ref unidos.

Los chats, los autores, los grupos y los canales viajan en los eventos como un peer:

{
"peer_ref": "4b7d9e01-0000-4000-8000-0000000000b1",
"jid": "CONTACTO@lid",
"clase": "usuario",
"aliases": ["CONTACTO@s.whatsapp.net"]
}
Campo Qué es
peer_ref La clave estable en esta instancia. Úsala como clave del chat en tu sistema.
jid El JID con el que llegó este hecho. Puede ser el LID en un evento y el PN en el siguiente.
clase usuario, grupo, difusion, canal u otro.
aliases Los demás JID comprobados del peer. Vacío si solo se conoce uno.

En un grupo, el mensaje trae el grupo en chat y la persona que escribió en autor, cada uno con su peer. Un autor que oculta su número llega con un jid @lid y aliases: [].

Para contestar un mensaje, usa el peer_ref de su chat:

{ "destino": { "peer_ref": "4b7d9e01-0000-4000-8000-0000000000b1" }, "texto": "¡Gracias por escribirnos!" }
Destino Cuándo Cuidado
peer_ref Responder a alguien que te escribió, o a un chat que tu app ya guardó. Solo vale en su instancia (otra da 422).
jid Un chat que conoces por su JID: un grupo, un LID, un PN. Va tal cual.
telefono Escribirle por primera vez a un número. El motor arma el PN con el número. No te dice si es la misma persona que ya conoces por un LID: eso lo dice el peer_ref del chat.

Con peer_ref, el motor envía al JID comprobado más antiguo del peer. Las menciones aceptan las mismas tres formas.

Hay dos caminos, y no son equivalentes:

GET …/identities POST …/contacts/check
Scope contacts:read contacts:lookup
Pregunta a Lo que esta instancia ya comprobó El servidor de WhatsApp
Cuota No consume Cada número cuenta en la cuota diaria de la app (500 de fábrica)
Si no sabe 404 identidad_desconocida Responde registrado: false
Crea contactos o peers No No

GET /v1/instances/{id}/identities recibe uno de telefono, jid o peer_ref y devuelve el peer con su PN, su LID y su teléfono solo si esta instancia los comprobó:

Ventana de terminal
curl "https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/identities?jid=CONTACTO@lid" \
-H "Authorization: Bearer TU_API_KEY"
200 OK
{
"peer_ref": "4b7d9e01-0000-4000-8000-0000000000b1",
"jid": "CONTACTO@lid",
"clase": "usuario",
"aliases": ["CONTACTO@s.whatsapp.net"],
"pn": "CONTACTO@s.whatsapp.net",
"lid": "CONTACTO@lid",
"telefono": "+NUMERO_DEL_CONTACTO",
"contacto": true
}

telefono solo aparece si hay un PN comprobado: un LID solo nunca da un teléfono. contacto dice si la persona está entre los contactos del número (los de GET …/contacts).

POST /v1/instances/{id}/contacts/check pregunta al servidor si unos números (hasta 20 por consulta) tienen WhatsApp, y trae el JID canónico (que puede ser un LID), el PN y el LID. Si la instancia ya conocía al contacto, el par que dio el servidor queda comprobado en su peer y la respuesta trae su peer_ref. No es un oráculo público: consume cuota, y un número inválido o la cuota agotada rechazan la consulta entera antes de llegar al servidor. Los detalles están en Contactos.

  • Las menciones que llegan van por LID. En un grupo, el message.received de una mención al número de la instancia trajo en contenido.menciones un JID @lid (prueba del 21/10/2026). No hay un campo «me mencionaron»: compara la lista con los JID propios de la instancia, su PN y su LID. El motor guarda el LID propio como alias del propio número al conectar, así que identities?telefono= con el numero de la instancia devuelve los dos.
  • El teléfono guarda los chats 1:1 por LID. De los cambios de chats que llegaron del teléfono en una instancia, 60 chats 1:1 vinieron por @lid y uno solo por @s.whatsapp.net (falla F12, 16/10/2026). El motor ahora manda los cambios de estado de un chat (fijar, archivar, silenciar, destacar) por el LID cuando lo conoce.

Un teléfono es un dato personal, y un LID existe justamente para no revelarlo. El motor los trata así:

  • Nunca en claro en la base: cada JID se guarda como un HMAC (para buscarlo) y sellado (para leerlo). Tampoco en los logs ni en la auditoría: una consulta de números registra cuántos, nunca cuáles.
  • Nada se comparte entre instancias: el mapa LID↔PN del SDK es de todo el proceso, pero el motor solo expone a cada instancia los pares que aprendió ella. La instancia de otra app no recibe el teléfono detrás de un LID que solo vio la tuya.
  • Nada se infiere: ni un teléfono a partir de un LID, ni una persona por su nombre.
  • Las consultas al servidor tienen cuota por app, para que el motor no sirva para barrer números.

Lo que sí sale en claro son tus eventos y respuestas: jid, aliases, telefono. Desde ese momento, protegerlos es tarea de tu app.

  1. Usa el peer_ref (junto con el instance_id) como clave de cada chat en tu sistema.
  2. Guarda también el jid y los aliases del último evento, para volver a resolverlo si hace falta.
  3. Responde con destino.peer_ref; usa telefono solo para escribir por primera vez.
  4. Trata a un contacto sin telefono como normal: muchos solo tendrán su LID.
  5. Nunca uses el teléfono como identidad global, ni intentes deducirlo de un LID.