Leer conversaciones e historial de mensajes
Traé a tu sistema las conversaciones que cambiaron y el historial de mensajes de cada contacto, paginando sin perder nada.
Vas a sincronizar las conversaciones de WhatsApp de tu cuenta con tu sistema: qué conversaciones tuvieron movimiento y qué se dijo en cada una. Usa Listar conversaciones con updated_since, Listar contactos para encontrar a un contacto por su teléfono y Listar los mensajes de un contacto para traer su historial.
Al terminar esta guía, tu sistema trae solo lo que cambió desde la última sincronización, recorre todas las páginas con el cursor y guarda el historial de mensajes de cada contacto sin duplicados.
Antes de empezar
- Una clave con
conversations:read. Todo lo de esta guía es lectura. Si todavía no tenés una, seguí la guía rápida. - La clave en la variable
FC_API_KEY, entre comillas si la guardás en un.env(lleva un|). - Opcional: el receptor de Recibir los mensajes de WhatsApp en tu sistema, para enterarte en el momento de cada mensaje nuevo y pedir solo ese historial.
1. Listá las conversaciones que cambiaron
Listar conversaciones devuelve las conversaciones de la cuenta, con filtros. Con updated_since pedís solo las que cambiaron desde una fecha; con perPage (de 1 a 100, 25 si no lo mandás), el tamaño de cada página.
curl -G "https://api.funnelchat.com/api/v1/conversations" \
--data-urlencode "updated_since=2026-10-01T00:00:00Z" \
--data-urlencode "perPage=50" \
-H "Authorization: Bearer $FC_API_KEY" \
-H "Accept: application/json"La respuesta trae data (una conversación por elemento), links y meta. Para paginar te importa meta (según la especificación):
{
"meta": {
"path": "https://api.funnelchat.com/api/v1/conversations",
"per_page": 50,
"next_cursor": "…",
"prev_cursor": null
}
}Otros filtros que acepta, combinables con updated_since: deviceId (el id de uno de tus números), unreadMessages, activeConversation, advisorId, contactId y kanbanSettingId, entre otros. La página de Listar conversaciones en la referencia los lista todos.
Dos detalles que la especificación no fija
- El formato de
updated_since. La especificación lo declara como texto, sin formato. En los ejemplos usamos una fecha ISO 8601 en UTC; antes de depender de ella, confirmá con tu clave que filtra como esperás. - Los campos de cada conversación.
dataes una lista de objetos sin esquema documentado. Leé los campos de una respuesta real y tolerá que cambien: la v0 cambia sin aviso.
2. Recorré todas las páginas con el cursor
Este listado pagina por cursor: no hay números de página ni total. Para pedir la página siguiente, mandá cursor con el valor de meta.next_cursor, el mismo perPage y los mismos filtros. Cuando meta.next_cursor llega en null, no hay más páginas.
# La segunda página: el cursor es el meta.next_cursor de la respuesta anterior
curl -G "https://api.funnelchat.com/api/v1/conversations" \
--data-urlencode "updated_since=2026-10-01T00:00:00Z" \
--data-urlencode "perPage=50" \
--data-urlencode "cursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $FC_API_KEY" \
-H "Accept: application/json"Armá vos la URL de cada página, con los mismos parámetros, en lugar de depender de los enlaces de links: así no importa qué parámetros conserven.
3. Encontrá al contacto
El historial se pide por contacto, con su id de la API. Si partís de un teléfono, por ejemplo el data.contact.phone de un evento message.received, buscalo con Listar contactos:
- Quitale el
+antes de buscar. Los eventos traen el teléfono con+y la API lo guarda sin él. - Quedate con el que tenga ese
phoneexacto. La búsqueda porphonetambién devuelve contactos cuyo teléfono contiene el número buscado. - Mandá siempre
perPage.
El detalle está en El teléfono en los eventos y en la API.
curl -G "https://api.funnelchat.com/api/v1/contacts" \
--data-urlencode "phone=5491100000000" \
--data-urlencode "perPage=25" \
-H "Authorization: Bearer $FC_API_KEY" \
-H "Accept: application/json"Respuesta de ejemplo (recortada a un contacto, a algunos de sus campos y sin links):
{
"data": [
{
"id": 102578466,
"name": "Ana Pérez",
"email": "",
"phone": "5491100000000",
"whatsapp_id": "",
"has_phone": true,
"country_code": "",
"status": 1,
"created_at": "2026-10-04T21:23:45.000000Z",
"active_conversation": 0,
"unread_messages": 0
}
],
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://api.funnelchat.com/api/v1/contacts",
"per_page": 25,
"to": 1,
"total": 1
}
}4. Traé el historial de mensajes del contacto
Listar los mensajes de un contacto devuelve sus mensajes, paginados por cursor como en el paso 2: perPage (de 1 a 100), cursor para la página siguiente y updated_since para pedir solo lo nuevo. {contactId} es el id del paso anterior.
curl -G "https://api.funnelchat.com/api/v1/contacts/102578466/messages" \
--data-urlencode "perPage=100" \
-H "Authorization: Bearer $FC_API_KEY" \
-H "Accept: application/json"Respuesta de ejemplo (recortada a un mensaje, a algunos de sus campos y sin links; … marca valores cuyo formato la especificación no documenta):
{
"data": [
{
"id": 734512,
"uid": "…",
"chat_id": "…",
"contact_id": 102578466,
"device_id": 4521,
"text": "Hola, quiero más información",
"file": "",
"time": "…",
"reference_message_uid": null,
"deleted_at": null,
"reactions": []
}
],
"meta": {
"path": "https://api.funnelchat.com/api/v1/contacts/102578466/messages",
"per_page": 100,
"next_cursor": "…",
"prev_cursor": null
}
}Cada mensaje trae también own, type, status y origin, entre otros. Guardalos por su id (un entero): si un mensaje vuelve a llegar en otra sincronización, actualizalo en lugar de duplicarlo.
- Qué significa cada valor de
type,status,originyown. Son enteros (typede 0 a 21,statusde 0 a 10,originde 0 a 3), sin la lista de qué es cada uno. Ojo: en el eventomessage.received,data.message.typees texto (text,image…), y la especificación no dice cómo se corresponde con el entero de la API. - El formato de
time,uidychat_id, y en qué orden vienen los mensajes. No ordenes por posición en la página: ordená vos con el dato que elijas después de confirmarlo en respuestas reales. - Si el
idde un mensaje en la API es el mismodata.message.iddel evento. No los uses como la misma clave sin comprobarlo.
5. Mantenelo al día
Con los pasos anteriores ya tenés todo para una sincronización incremental:
- Primera carga: recorré Listar conversaciones sin
updated_sincepara saber qué conversaciones hay, y traé el historial completo de cada contacto que te interese (pasos 3 y 4). - Cada vez que sincronices: anotá la hora en que empezás y pedí solo lo cambiado desde la sincronización anterior, con
updated_since. Pedí con un margen de unos minutos hacia atrás: los repetidos los descartás porid. - En el momento: si tenés el receptor de
message.received, cuando llega un mensaje buscá al contacto por teléfono (paso 3) y pedí sus mensajes conupdated_since(paso 4). Dejá la sincronización periódica como respaldo, por si tu receptor estuvo caído más que los reintentos.
Si falla
| Qué ves | Qué pasó | Qué hacer |
|---|---|---|
401 | Falta la clave, no existe o fue revocada. | Revisá la cabecera Authorization: Bearer con la clave completa. |
403 con error: "permission_denied" | A la clave le falta conversations:read. | Creá una clave con ese permiso. |
403 sin error | Lo rechazó la autorización propia de conversations, no el portero. | Leé message. |
404 en los mensajes | No hay un contacto con ese id en la cuenta. | Volvé a buscarlo por teléfono (paso 3). |
422 | Un parámetro no pasó la validación, por ejemplo perPage fuera de 1 a 100 o un valor de orden que el listado no admite. | errors dice qué parámetro corregir (por ejemplo errors.perPage). |
500 en las conversaciones, con error: "No se pudieron cargar las conversaciones" | Falló la carga del listado. | Reintentá más tarde, con la misma página. |
503 | El portero no pudo evaluar la llamada (gate_unavailable). | Esperá lo que indica Retry-After y reintentá. |
Decidí por el código HTTP y por error, no por message: su texto cambia según el dominio. El detalle está en Errores.
Relacionado
- Recibir los mensajes de WhatsApp en tu sistema: enterate en el momento de cada mensaje entrante.
- Enviar mensajes: el envío con clave se abre con
conversations:sendy las reglas de WhatsApp; qué pasa hoy si una clave intenta responder. - Contar conversaciones sin leer: para un contador en tu bandeja.
- Ver un contacto: la ficha completa, con campos personalizados y último mensaje.
- Conocido en v0: diferencias ya identificadas entre las especificaciones y lo que responde la API.
Recibir los mensajes de WhatsApp en tu sistema
Cada vez que un contacto le escribe a un número de tu cuenta, tu servidor recibe el mensaje firmado, sin consultar la API en bucle.
Enviar mensajes
Mandar un mensaje con tu clave, saber qué pasó con él y qué hacer cuando una regla de WhatsApp lo frena o la llamada no responde.