Leads, conversaciones e historiales
Cómo recorrer un lead de Cortex hasta sus conversaciones, las reservas que agendó el agente, el cliente y sus ventas, y cómo leer en los historiales quién marcó la asistencia y quién anuló una venta.
Antes de empezar
Cortex es el agente conversacional de AgendaPro. La API v3 expone en modo lectura sus leads, sus conversaciones, los segmentos del CRM y los recordatorios que envía.
-
El acceso a los datos de Cortex se habilita por compañía. Si tu compañía no está habilitada,
GET /v3/leads,/v3/conversations/...,/v3/segmentsy/v3/reminder_messagesresponden403con este cuerpo:{ "error": "forbidden", "detail": "company_not_enabled" }Reintentar no sirve: pídele a AgendaPro que habilite tu compañía. Un
403con"detail": "scope_denied"es otra cosa: tu API Key no tiene el scope. -
Scopes:
leads:read(leads y sus conversaciones),conversations:read(mensajes y tool calls),segments:read,reminder_messages:read,bookings:read,clients:readysales:read. Una API Key sin scopes tiene acceso completo; si la tuya está restringida, necesita todos los del recorrido que vas a hacer. -
Errores: estos endpoints responden con el formato
{error, detail}de la API. Por ejemplo, un lead que no existe o es de otra compañía responde404con{"error": "not_found", "detail": "lead"}; para una conversación o un segmento,detailesconversationosegment. -
Los teléfonos vienen tal como Cortex los guardó, sin normalizar (por ejemplo,
+521…para un WhatsApp de México).
Del lead a la venta
El recorrido completo une seis recursos. Cada paso usa un id que entrega el anterior:
| Paso | Request | Qué te entrega |
|---|---|---|
| 1 | GET /v3/leads | El lead, con su id y su client_id (null si el lead todavía no es cliente) |
| 2 | GET /v3/leads/{id}/conversations | Todas las conversaciones del teléfono del lead, con su id |
| 3 | GET /v3/conversations/{id}/tool_calls | Las acciones del agente; booking_ids trae las reservas que creó, movió o anuló |
| 4 | GET /v3/bookings/{id} | La reserva, con su client_id, su creative_source y el objeto sale |
| 5 | GET /v3/clients/{id} | El cliente |
| 6 | GET /v3/sales/{id} o GET /v3/sales?client_id=… | La venta de la reserva o todas las ventas del cliente |
1. Lista los leads
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/leads?limit=50"Puedes filtrar por stage_key, source y campaign_id. Cada lead trae, entre otros campos, client_id, origin (paid u organic), campaign_id, meta_ad_id y meta_source_type.
meta_ad_id en null puede ser una ausencia legítima: la campaña no es un anuncio de Meta (es una publicación de Meta o viene de Google) o ninguna conversación del lead tiene esa campaña. No es un error y no hay que reintentar. Cuando Cortex no logra resolver el origen de la campaña, nunca responde null: responde 502 para toda la página, y ahí sí reintentas el mismo request.
2. Lee las conversaciones del lead
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/leads/lead_ejemplo_1/conversations"Trae todas las conversaciones del teléfono del lead, no sólo la última. Cada una tiene kind (message, inbound_campaign, outbound_campaign, reminder o draft), channel_type y su propio meta_ad_id, con la misma regla del paso anterior. Si quieres el texto, GET /v3/conversations/{id}/messages lo trae del más antiguo al más reciente. Ese texto contiene datos personales del cliente: trátalo como tal.
3. Encuentra las reservas que agendó el agente
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/conversations/conv_ejemplo_1/tool_calls"{
"data": [
{
"id": "tc_ejemplo_1",
"tool_name": "create_booking",
"status": "ok",
"args": { "service_id": 10 },
"result": { "ok": true },
"error": null,
"at": "2026-09-10T14:02:11Z",
"booking_ids": [5512]
}
],
"pagination": { "next_cursor": null }
}booking_ids lista las reservas que la acción creó, movió o anuló. Viene vacío cuando la herramienta no opera reservas o falló, y trae varios ids cuando la reserva es de varios servicios. Una llamada fallida tiene status: error, result: null y el mensaje en error.
4, 5 y 6. Reserva, cliente y ventas
Con cada id de booking_ids, GET /v3/bookings/{id} te da la reserva. Ahí tienes:
client_id, paraGET /v3/clients/{id}.status,serviceyservice_providercomo objetos: lee los ids enstatus.id,service.idyservice_provider.id. El listado y el detalle traenservice_provider; las respuestas de crear y actualizar una reserva no.creative_source, el canal que creó la reserva (juliacuando la creó Cortex).sale, conid,statusycart_id, onullsi la reserva no tiene venta. Consale.idllamas aGET /v3/sales/{id}.
Si el plan es una membresía, una sola venta cubre varias reservas: no sumes montos por sale.id.
La venta (GET /v3/sales/{id}) no trae el local; el listado GET /v3/sales sí lo trae, en location.id.
Para ir en el otro sentido, del lead a sus reservas sin pasar por las conversaciones, usa el client_id del lead: GET /v3/bookings?client_id=<client_id> y mira el creative_source de cada reserva. Las ventas del cliente salen de GET /v3/sales?client_id=<client_id>. Ese listado incluye las ventas anuladas: filtra con status (por ejemplo status=paid) si no las quieres.
Paginación
Hay dos formas de paginar, según el recurso:
| Recursos | Paginación | Cómo pides la página siguiente |
|---|---|---|
| Leads, conversaciones de un lead, mensajes, tool calls, segmentos y sus miembros, recordatorios | Por cursor | Repite el request con cursor=<next_cursor>. next_cursor es null en la última página. Usa limit para el tamaño de la página |
| Reservas, ventas, historial de una reserva, historial de una venta | Por página | page y per_page; el objeto pagination trae next_page, total_pages y total_records |
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/leads?limit=50&cursor=eyJpZCI6IjEyMyJ9"El cursor es opaco: úsalo tal como llega, sin interpretarlo ni construirlo.
Quién marcó la asistencia de una reserva
GET /v3/bookings/{id}/history devuelve los cambios de la reserva, del más reciente al más antiguo. Cada entrada trae action, un texto descriptivo como «Creada por Calendario» que no conviene interpretar, status (id y name), user (sólo {id}, o null si el cambio no tuvo usuario, como un proceso del sistema o una reserva en línea) y created_at. El historial no se filtra por estado: una reserva tiene pocas entradas, así que lee todas sus páginas y deriva los cambios de tu lado.
- Lee todas las páginas del historial y ordénalas de la más antigua a la más reciente.
- Una transición de estado es una entrada cuyo
status.ides distinto del de la entrada anterior en el tiempo. La entrada más antigua también cuenta como transición. - Quien marcó Asiste (
status.id3) o No Asiste (status.id6) es eluser.idde la transición más reciente a ese estado, y sucreated_atdice cuándo. - Una entrada posterior que mantiene el estado, por ejemplo una edición de una reserva que ya estaba en Asiste, no es una transición. En
1 → 3 (A) → 3 (B)la marcó A; en1 → 3 (A) → 1 → 3 (B)la marcó B. - Si no hay ninguna transición a ese estado, nadie la marcó. El
status.idactual de la reserva (GET /v3/bookings/{id}) te dice si esa marca sigue vigente.
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/bookings/5512/history?per_page=100"Quién anuló una venta
GET /v3/sales/{id}/history devuelve los cambios de la venta del más antiguo al más reciente. Cada entrada trae action (create, update o destroy), changes, user_id (null si el cambio no tuvo usuario) y at, en UTC. En un update, changes es {campo: [antes, después]}.
En AgendaPro, anular y eliminar una venta son la misma operación. La anulación es la entrada update cuyo changes.status termina en canceled: su user_id es quien la anuló y su at, cuándo.
{
"id": 502,
"action": "update",
"changes": { "status": ["paid", "canceled"] },
"user_id": 7790,
"at": "2026-03-16T09:00:00.000Z"
}Sincronizar reservas por fecha de creación o de modificación
GET /v3/bookings acepta created_from/created_to y updated_from/updated_to, que filtran por created_at y updated_at en UTC. Cada límite es una fecha (YYYY-MM-DD) o un instante UTC con segundos (Z, +00:00 o sin offset). Codifica el + como %2B.
- Cada ventana abarca como máximo 31 días calendario. Si la excedes, la respuesta es
400coninvalid_created_atoinvalid_updated_at. Para periodos más largos, recórrelos en ventanas consecutivas de 31 días o menos. - Un
*_fromsolo llega hasta ahora. Por esoupdated_fromsolo, con un instante de los últimos 31 días (por ejemplo, el de tu última sincronización), sirve para consultar los cambios de toda la compañía sin otro filtro. Un*_tosin su*_fromse rechaza. - Las reservas anuladas sólo vuelven si pides su estado en
status_id. Para sincronizar anulaciones conupdated_from, incluye esos estados.
Una ventana cerrada de dos semanas:
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/bookings?updated_from=2026-10-01T00:00:00Z&updated_to=2026-10-15T00:00:00Z"Una sincronización incremental, con el instante UTC de tu última consulta (dentro de los últimos 31 días):
curl -H "Authorization: Bearer apk_live_tu_api_key" \
"https://connect.agendapro.com/v3/bookings?updated_from=<instante-de-tu-ultima-sincronizacion>"GET /v3/reminder_messages tiene el mismo tope: from y to son obligatorios juntos y el rango abarca como máximo 31 días.
Errores que vas a ver
| Status | Cuándo | Qué hacer |
|---|---|---|
400 invalid_created_at / invalid_updated_at | La ventana de fechas excede 31 días, está invertida o trae *_to sin *_from | Acorta la ventana o pártela |
400 invalid_format / invalid_range / required | En leads, conversaciones, segmentos y recordatorios: limit, cursor, from o to no son válidos o falta uno. detail dice cuál | Corrige el parámetro |
401 invalid_api_key / api_config_inactive | La API Key falta, es inválida o el acceso API está inactivo | Revisa la key en Configuraciones > Integraciones |
403 scope_denied | La API Key no tiene el scope del recurso | Usa una key con el scope |
403 company_not_enabled | La compañía no está habilitada para leer datos de Cortex | Pide la habilitación a AgendaPro; reintentar no sirve |
404 not_found | El recurso no existe o es de otra compañía. detail dice cuál (lead, conversation, segment, booking, sale) | No reintentes |
| 429 | Excediste el límite por minuto o la cuota diaria | Espera lo que indique Retry-After |
502 upstream_unavailable | El servicio de origen no está disponible o respondió un error (detail: upstream_error). En leads y en las conversaciones de un lead, también cuando Cortex no logra resolver el origen de una campaña | Reintenta el mismo request |
El detalle de cada endpoint, con sus parámetros y su diccionario de errores, está en la referencia: secciones Leads, Conversations, Segments, Reminder Messages, Bookings y Sales.
Updated about 23 hours ago
