Ir al contenido

Contactos

Estas rutas dicen lo que el número vinculado sabe de las personas con las que habla:

  • los contactos de transporte: cómo los guardó el teléfono, el nombre que mandan (push name), su nombre de negocio, su foto y su Info;
  • si un número está en WhatsApp, y el perfil público y la foto de otros usuarios;
  • la resolución de identidades: el peer_ref estable de un contacto y sus alias comprobados (su JID de teléfono, PN, y su LID).
  • Una API key con estos scopes:
Operación Ruta Scope
Listar los contactos GET /v1/instances/{id}/contacts contacts:read
Ver un contacto GET /v1/instances/{id}/contacts/{peer_ref} contacts:read
Traer la libreta del store POST /v1/instances/{id}/contacts/sync contacts:read
Resolver una identidad GET /v1/instances/{id}/identities contacts:read
¿Está en WhatsApp? POST /v1/instances/{id}/contacts/check contacts:lookup
Perfil público de otros POST /v1/instances/{id}/contacts/info contacts:lookup
La foto de otro POST /v1/instances/{id}/contacts/picture contacts:lookup
La cuota de consultas de hoy GET /v1/instances/{id}/contacts/quota contacts:lookup
  • Para las consultas con contacts:lookup, una instancia conectado: se hacen en el momento y sin conexión responden 409 no_conectada (no quedan en cola).

Los ejemplos de Go y JavaScript usan una función auxiliar, llamar, que se define en el primer paso: el SDK de Go no trae métodos de contactos, así que es HTTP directo con el token del cliente.

GET /v1/instances/{id}/contacts devuelve los contactos en el orden en que el motor los conoció, de a limit (1 a 200; por omisión, 50), con un next_cursor estable. Filtros: libreta=true (solo los guardados en el teléfono) y buscar (un teléfono internacional o un JID, exacto).

Cada contacto trae su contacto (peer), nombre y primer_nombre (cómo lo guardó el teléfono), en_libreta, push_name (el que manda el contacto), nombre_negocio (el verificado de una cuenta Business), foto_id, recado (su Info, si la comparte) y telefono (solo si el motor comprobó su PN).

POST …/contacts/sync pasa a la lista lo que el SDK guardó de la libreta y los nombres que aprendió (también los del historial). Ocurre solo al conectar; no consulta al servidor, no gasta cuota y no deja eventos. Responde con cambiados.

Ventana de terminal
curl "https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/contacts?libreta=true&limit=50" \
-H "Authorization: Bearer TU_API_KEY"
curl "https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/contacts?buscar=%2BNUMERO_DEL_CONTACTO" \
-H "Authorization: Bearer TU_API_KEY"
GET …/contacts/{peer_ref} · 200 OK
{
"contacto": { "peer_ref": "4b7d9e01-0000-4000-8000-0000000000b1", "jid": "CONTACTO@s.whatsapp.net", "clase": "usuario", "aliases": ["CONTACTO@lid"] },
"telefono": "+NUMERO_DEL_CONTACTO",
"nombre": "Nombre en la libreta",
"primer_nombre": "Nombre",
"en_libreta": true,
"push_name": "Nombre del contacto",
"foto_id": "FOTO_ID",
"recado": "Disponible",
"creado_en": "2026-10-20T12:00:00Z",
"actualizado_en": "2026-10-24T15:08:13Z"
}

GET /v1/instances/{id}/identities con uno de telefono, jid o peer_ref devuelve el peer estable y sus alias comprobados por esta instancia: pn y lid cuando el servidor los informó juntos, y telefono solo si hay un PN comprobado. contacto dice si es un contacto de transporte.

No consulta al servidor ni a otras instancias: lo que esta instancia no comprobó no aparece y la respuesta es 404 identidad_desconocida. Un LID solo nunca da un teléfono.

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"],
"lid": "CONTACTO@lid",
"pn": "CONTACTO@s.whatsapp.net",
"telefono": "+NUMERO_DEL_CONTACTO",
"contacto": true
}

POST …/contacts/check con telefonos (números internacionales, con o sin +; los repetidos cuentan una vez) responde, por cada uno, si está registrado y, si lo está, su jid canónico (puede ser un LID), su pn, su lid, si es una cuenta Business verificada (negocio, nombre_verificado) y su peer_ref si la instancia ya lo conocía. En ese caso, el par PN↔LID que dio el servidor queda comprobado en su peer. Una consulta no crea contactos.

Ventana de terminal
curl -X POST https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/contacts/check \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"telefonos": ["+NUMERO_DEL_CONTACTO", "+OTRO_NUMERO"]}'
200 OK
{
"resultados": [
{
"telefono": "+NUMERO_DEL_CONTACTO",
"registrado": true,
"jid": "CONTACTO@lid",
"pn": "CONTACTO@s.whatsapp.net",
"lid": "CONTACTO@lid",
"negocio": false,
"peer_ref": "4b7d9e01-0000-4000-8000-0000000000b1"
},
{ "telefono": "+OTRO_NUMERO", "registrado": false }
],
"cuota": { "cuota": 500, "usadas": 27, "maximo_por_consulta": 20, "renueva": "2026-10-25T00:00:00Z" }
}
  • POST …/contacts/info con destinos (usuarios: telefono, jid o peer_ref; no grupos) devuelve, de los que tienen cuenta, su recado (si lo comparte con este número), foto_id, nombre_verificado, negocio, lid y cuántos dispositivos tiene. Un usuario sin cuenta no aparece. Si la instancia ya lo conocía, su Info y su foto quedan en el contacto y, si cambiaron, llegan contact.about y contact.picture con origen: consulta. Gasta una consulta por destino.
  • POST …/contacts/picture con destino (y vista_previa: true para la miniatura) devuelve la foto con su estado: disponible (con id y una url temporal del CDN de WhatsApp, que vence en horas), oculta (su privacidad no se la muestra a este número; no es un error) o sin_foto. Gasta una consulta.
  • GET …/contacts/quota dice la cuota de la app hoy: cuota, usadas, maximo_por_consulta, renueva (00:00 UTC) y un desglose por tipo (numeros, perfiles, foto, perfil_comercial). El catálogo usa otra cuota (ver Catálogo).
Ventana de terminal
curl -X POST https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/contacts/info \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"destinos": [{"peer_ref": "4b7d9e01-0000-4000-8000-0000000000b1"}]}'
curl -X POST https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/contacts/picture \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"destino": {"telefono": "+NUMERO_DEL_CONTACTO"}, "vista_previa": true}'
curl https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/contacts/quota \
-H "Authorization: Bearer TU_API_KEY"

Este dominio no tiene comandos: las consultas responden en el momento y la lista cambia con lo que informa WhatsApp. Cada cambio de un contacto llega como un evento con la misma forma: el contacto completo (como en GET …/contacts/{peer_ref}), el campo que cambió, momento y origen (whatsapp: lo informó WhatsApp; consulta: lo trajo una consulta de perfil). full_sync: true marca lo que llegó en una sincronización completa.

Evento campo Cuándo
contact.updated libreta El teléfono guardó, renombró o borró el contacto de la libreta. Trae accion: guardado, renombrado o borrado (con contacto.nombre vacío).
contact.push_name push_name El contacto mandó otro nombre (llega con un mensaje suyo).
contact.business_name negocio Cambió su nombre verificado de negocio.
contact.picture foto Cambió su foto de perfil (contacto.foto_id).
contact.about recado Cambió su Info, si la comparte con este número.
contact.updated
{
"contacto": {
"contacto": { "peer_ref": "…", "jid": "CONTACTO@s.whatsapp.net", "clase": "usuario", "aliases": [] },
"telefono": "+NUMERO_DEL_CONTACTO",
"nombre": "Nombre nuevo en la libreta",
"en_libreta": true,
"push_name": "Nombre del contacto",
"creado_en": "2026-10-20T12:00:00Z",
"actualizado_en": "2026-10-24T15:30:00Z"
},
"campo": "libreta",
"accion": "renombrado",
"momento": "2026-10-24T15:30:00Z",
"origen": "whatsapp"
}
contact.about
{
"contacto": { "contacto": { "peer_ref": "…", … }, "recado": "De vacaciones", "en_libreta": true, … },
"campo": "recado",
"momento": "2026-10-24T15:35:00Z",
"origen": "whatsapp"
}
HTTP error_code Cuándo
403 — A tu key le falta contacts:read o contacts:lookup.
404 — Ese peer_ref no existe en esta instancia.
404 identidad_desconocida Esta instancia no comprobó esa identidad (no la busca en otras ni deduce un teléfono de un LID).
409 no_conectada Una consulta (check, info, picture, sync) sin conexión: no queda en cola.
422 — Números inválidos (con sus posiciones; no se consultó ninguno), más de los permitidos por consulta, un grupo como destino, un peer_ref que no es un UUID, o en identities ninguno o más de uno de telefono, jid y peer_ref.
429 cuota_agotada La app usó su cuota de hoy. Retry-After dice cuánto falta para las 00:00 UTC. No se consultó nada.
429 limitado_por_whatsapp WhatsApp limitó las consultas del número. Espera antes de volver a consultar (Retry-After: 60).

La cuota de fábrica es de 500 consultas por app y día (limites.consultas_identidad_dia) y de 20 números por consulta (limites.numeros_por_consulta, hasta 50). El operador puede darle a cada app la suya.

  • La cuota cuenta solo lo válido (caso A42, falla F24, 20/10/2026): con la cuota bajada para la prueba, un valor inválido dio 422 sin gastar nada, cada número válido gastó una consulta y, al agotarse, la siguiente dio 429 con Retry-After hasta las 00:00 UTC.
  • La libreta (caso A43, 22/10/2026): guardar, renombrar y borrar un contacto en el teléfono dejaron un contact.updated cada uno, en orden, con accion guardado, renombrado y borrado. El push_name nuevo llegó como contact.push_name con el primer mensaje del contacto.
  • La foto de otro (casos A43 y A49, 21 y 23/10/2026): poner o quitar la foto llegó como contact.picture en 1 s, pero solo a las instancias que tienen a ese contacto. La consulta de la foto dio disponible, oculta (con la privacidad de la foto en «Nadie» o en «Mis contactos», sin ser contacto) y sin_foto, y cada una gastó una consulta.
  • LID sin teléfono (caso A44, 16/10/2026): en un grupo grande, los participantes que ocultan su número aparecen solo con su LID; GET …/identities los devuelve con su peer_ref y sin teléfono. Ninguna otra instancia les pone número.
  • La Info del propio número se ve como contact.about en las otras instancias que lo tienen de contacto, pero no en la propia (falla F34). Ver Info propia cambiada desde el teléfono.