Saltar al contenido principal

Sincronizar historial y contactos

Al conectar un número en coexistencia, puedes traer a la Cloud API el historial de conversaciones de los últimos 6 meses y la lista de contactos del dispositivo.

Lee esto antes de disparar

Tienes 24 horas contadas desde el Embedded Signup, y un solo intento por tipo de sincronización. Si se pasa el plazo o te equivocas, el cliente debe rehacer todo el onboarding. No hay recuperación.

Antes de disparar​

1. El cliente autorizó el compartir en su móvil. Durante el Embedded Signup, la aplicación pide permiso para compartir historial y contactos. Si lo rechazó, no se enviará nada — y el disparo no da error, simplemente no llega ningún webhook.

2. Los eventos están suscritos. Sin esto, los datos se envían y se descartan.

curl -X PATCH https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "events": ["messages", "history", "smb_app_state_sync"] }'

3. Tu servidor aguanta el volumen. Un solo webhook puede llevar miles de mensajes. Responde 2xx al instante y procesa en segundo plano.

Disparar​

Historial de conversaciones
curl -X POST https://api.joinotify.com/v1/106540352242922/smb_app_data \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "messaging_product": "whatsapp", "sync_type": "history" }'
Lista de contactos
curl -X POST https://api.joinotify.com/v1/106540352242922/smb_app_data \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "messaging_product": "whatsapp", "sync_type": "smb_app_state_sync" }'

La respuesta solo confirma la programación. Los datos llegan después, de forma asíncrona.

Cómo llega el historial​

En múltiples webhooks, divididos en fases y bloques.

webhook (1 POST)
└── value.history[]
└── threads[] ← 1 hilo = 1 conversación con 1 contacto
└── messages[] ← mensajes de esa conversación

Fases​

phasePeríodo nominal
0día 0 → día 1
1día 1 → día 90
2día 90 → día 180
Las fases se solapan

En la práctica, aparecen mensajes de cualquier fecha en cualquier fase. No uses la fase para inferir un período. Ordena todo por timestamp.

Bloques y progreso​

Cada fase puede llegar en varios bloques (chunk_order), no necesariamente en orden.

{ "phase": 1, "chunk_order": 3, "progress": 60 }

progress va de 0 a 100. progress: 100 en la última fase significa historial completo — es la señal de que puedes cerrar el procesamiento.

Deduplicación — obligatoria​

El mismo thread.id aparece en varios webhooks y el mismo mensaje puede llegar repetido.

  1. Reúne todos los mensajes con el mismo thread.id de todos los webhooks.
  2. Deduplica por wamid (message.id).
  3. Ordena por timestamp — Unix en segundos, 10 dígitos.
const conversaciones = new Map();

function procesarHistorial(value) {
for (const bloque of value.history ?? []) {
for (const hilo of bloque.threads ?? []) {
const actual = conversaciones.get(hilo.id) ?? new Map();
for (const msg of hilo.messages ?? []) {
actual.set(msg.id, msg); // dedup por wamid
}
conversaciones.set(hilo.id, actual);
}
}
}

function finalizar(hiloId) {
return [...conversaciones.get(hiloId).values()].sort((a, b) => Number(a.timestamp) - Number(b.timestamp));
}

Dirección del mensaje​

history_context.from_me: true = enviado por la empresa, false o ausente = recibido.

Medios en el historial​

Los medios no viajan dentro de los hilos. Aparecen como type: "media_placeholder", sin contenido:

{ "id": "wamid.HBgN...", "type": "media_placeholder", "timestamp": "1785312000" }

El archivo real llega en webhooks separados (value.messages o value.message_echoes), emparejados por el wamid.

Solo ~14 días de medios

Los archivos más antiguos no tienen contenido disponible — el marcador es todo lo que recibes. Es una limitación de Meta, no de Joinotify.

Cómo llegan los contactos​

Normalmente en un único webhook, en el array state_sync. Las cuentas grandes pueden generar varios, sin orden garantizado.

{
"field": "smb_app_state_sync",
"value": {
"state_sync": [
{
"type": "contact",
"action": "add",
"contact": { "phone_number": "5541999999999", "full_name": "María Silva" },
"metadata": { "timestamp": "1785312000000" }
}
]
}
}
  • action: "add" → añadir o actualizar
  • action: "remove" → eliminar
Unidad de tiempo distinta

Los contactos usan milisegundos (13 dígitos); el historial usa segundos (10 dígitos). Mezclarlos genera fechas en 1970 o en el año 57000.

Después de la sincronización inicial​

Los cambios posteriores en los contactos siguen llegando automáticamente por smb_app_state_sync — sin necesidad de volver a llamar al endpoint. La regla del disparo único vale solo para la carga inicial.

Lista de verificación​

  • El cliente autorizó el compartir en su móvil
  • history y smb_app_state_sync suscritos en el endpoint de webhook
  • El endpoint responde 2xx en segundos y procesa en segundo plano
  • Deduplicación por wamid implementada
  • Ordenación por timestamp, no por orden de llegada
  • media_placeholder tratado, con reconciliación posterior por wamid
  • Marcas de tiempo de contacto leídas en milisegundos
  • Disparo dentro de las 24 horas