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/segments y /v3/reminder_messages responden 403 con este cuerpo:

    {
      "error": "forbidden",
      "detail": "company_not_enabled"
    }

    Reintentar no sirve: pídele a AgendaPro que habilite tu compañía. Un 403 con "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:read y sales: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 responde 404 con {"error": "not_found", "detail": "lead"}; para una conversación o un segmento, detail es conversation o segment.

  • 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:

PasoRequestQué te entrega
1GET /v3/leadsEl lead, con su id y su client_id (null si el lead todavía no es cliente)
2GET /v3/leads/{id}/conversationsTodas las conversaciones del teléfono del lead, con su id
3GET /v3/conversations/{id}/tool_callsLas acciones del agente; booking_ids trae las reservas que creó, movió o anuló
4GET /v3/bookings/{id}La reserva, con su client_id, su creative_source y el objeto sale
5GET /v3/clients/{id}El cliente
6GET /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, para GET /v3/clients/{id}.
  • status, service y service_provider como objetos: lee los ids en status.id, service.id y service_provider.id. El listado y el detalle traen service_provider; las respuestas de crear y actualizar una reserva no.
  • creative_source, el canal que creó la reserva (julia cuando la creó Cortex).
  • sale, con id, status y cart_id, o null si la reserva no tiene venta. Con sale.id llamas a GET /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:

RecursosPaginaciónCómo pides la página siguiente
Leads, conversaciones de un lead, mensajes, tool calls, segmentos y sus miembros, recordatoriosPor cursorRepite 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 ventaPor páginapage 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.

  1. Lee todas las páginas del historial y ordénalas de la más antigua a la más reciente.
  2. Una transición de estado es una entrada cuyo status.id es distinto del de la entrada anterior en el tiempo. La entrada más antigua también cuenta como transición.
  3. Quien marcó Asiste (status.id 3) o No Asiste (status.id 6) es el user.id de la transición más reciente a ese estado, y su created_at dice cuándo.
  4. 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; en 1 → 3 (A) → 1 → 3 (B) la marcó B.
  5. Si no hay ninguna transición a ese estado, nadie la marcó. El status.id actual 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 400 con invalid_created_at o invalid_updated_at. Para periodos más largos, recórrelos en ventanas consecutivas de 31 días o menos.
  • Un *_from solo llega hasta ahora. Por eso updated_from solo, 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 *_to sin su *_from se rechaza.
  • Las reservas anuladas sólo vuelven si pides su estado en status_id. Para sincronizar anulaciones con updated_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

StatusCuándoQué hacer
400 invalid_created_at / invalid_updated_atLa ventana de fechas excede 31 días, está invertida o trae *_to sin *_fromAcorta la ventana o pártela
400 invalid_format / invalid_range / requiredEn leads, conversaciones, segmentos y recordatorios: limit, cursor, from o to no son válidos o falta uno. detail dice cuálCorrige el parámetro
401 invalid_api_key / api_config_inactiveLa API Key falta, es inválida o el acceso API está inactivoRevisa la key en Configuraciones > Integraciones
403 scope_deniedLa API Key no tiene el scope del recursoUsa una key con el scope
403 company_not_enabledLa compañía no está habilitada para leer datos de CortexPide la habilitación a AgendaPro; reintentar no sirve
404 not_foundEl recurso no existe o es de otra compañía. detail dice cuál (lead, conversation, segment, booking, sale)No reintentes
429Excediste el límite por minuto o la cuota diariaEspera lo que indique Retry-After
502 upstream_unavailableEl 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ñaReintenta 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.