Receta 3 — Sincronizar resultados (polling)¶
Lo que vas a lograr: mantener tu sistema (CRM, data warehouse, hoja de cálculo) al día con los resultados de las llamadas, mediante un loop de sincronización por polling con paginación por cursor.
Cómo se entregan los resultados hoy¶
Los resultados se obtienen consultando el endpoint GET /api/analytics/calls (polling).
No hay entrega push hacia tu sistema:
La entrega por webhooks (push) está en desarrollo; hoy la sincronización es por consulta (polling). No hay forma de registrar una URL de webhook vía la API pública.
El patrón recomendado es: paginar con cursor para traer un lote completo, persistir cada llamada de tu lado, y repetir cada N minutos filtrando solo lo nuevo por fecha o por campaña.
Concepto clave: paginación por cursor¶
GET /api/analytics/calls devuelve los resultados en páginas. Cada respuesta trae un objeto
pagination:
has_more:truesi quedan más resultados por traer.next_cursor: el token que pasas en el siguiente request para traer la página siguiente.
Para recorrer todas las páginas:
- Haz el primer request sin
cursor. - Si
has_moreestrue, repite el request agregandocursor=<next_cursor>. - Sigue hasta que
has_moreseafalse.
limit controla el tamaño de página (default 100, máximo 100).
Parte A — Recorrer todas las páginas (un barrido completo)¶
Con curl, paso a paso¶
Primera página (sin cursor):
Si la respuesta trae "has_more": true, copia el next_cursor y pide la siguiente:
Repite hasta que has_more sea false.
Pseudo-código del barrido completo¶
cursor = null
hacer:
url = "{API_BASE_URL}/api/analytics/calls?limit=100"
si cursor no es null:
url = url + "&cursor=" + cursor
respuesta = GET(url, header: "X-API-Key: {API_KEY}")
para cada llamada en respuesta.calls:
persistir(llamada) # upsert por call_id en tu sistema
cursor = respuesta.pagination.next_cursor
mientras respuesta.pagination.has_more == true
Ejemplo en Python¶
import requests
API_BASE_URL = "https://api.neuronstudio.ai"
API_KEY = "nrn_live_tu_key_aqui"
headers = {"X-API-Key": API_KEY}
def barrer_todas_las_llamadas(params=None):
params = dict(params or {})
params["limit"] = 100
cursor = None
while True:
if cursor:
params["cursor"] = cursor
resp = requests.get(
f"{API_BASE_URL}/api/analytics/calls",
headers=headers,
params=params,
)
resp.raise_for_status()
data = resp.json()
for llamada in data["calls"]:
persistir(llamada) # upsert por call_id de tu lado
pag = data["pagination"]
if not pag["has_more"]:
break
cursor = pag["next_cursor"]
Parte B — El loop de sincronización (incremental, cada N minutos)¶
Para no traer siempre todo el histórico, en cada corrida filtra por fecha (from_date) o
por campañas (campaign_ids).
Filtros útiles de GET /api/analytics/calls¶
| Parámetro | Para qué sirve |
|---|---|
from_date / to_date |
Acotar por rango de fechas (ISO 8601, ej. 2026-02-10T00:00:00Z). |
campaign_ids |
Solo ciertas campañas (IDs separados por coma, ej. 123,124). |
call_outcome |
Filtrar por resultado de la llamada. |
sentiment |
Filtrar por sentimiento. |
payment_filter |
Filtrar por promesa de pago. |
campaign_type |
Filtrar por tipo de campaña. |
limit |
Tamaño de página (default 100, máx 100). |
cursor |
Token de paginación. |
Estrategia incremental recomendada¶
- Guarda del lado de tu sistema el timestamp de la última sincronización exitosa.
- En cada corrida, barre con
from_date = última_sincronización(recorriendo todas las páginas con cursor, como en la Parte A). - Haz upsert por
call_idal persistir (así reprocesar una llamada ya vista no la duplica — los análisis pueden actualizarse poco después de terminar la llamada). - Al terminar el barrido sin error, actualiza la marca de última sincronización.
- Espera N minutos y repite.
Pseudo-código del loop¶
ultima_sync = leer_marca() # ej. "2026-02-10T08:00:00Z"; null la primera vez
repetir para siempre:
ahora = tiempo_actual_iso()
params = { from_date: ultima_sync } # omitir from_date si es la primera corrida
barrer_todas_las_paginas(params): # ver Parte A (cursor hasta has_more=false)
para cada llamada:
upsert_por_call_id(llamada)
guardar_marca(ahora) # solo si el barrido terminó sin error
esperar(N_minutos)
Ejemplo en Python (loop incremental)¶
import time
from datetime import datetime, timezone
def ahora_iso():
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
INTERVALO_MINUTOS = 10
def loop_sincronizacion():
ultima_sync = leer_marca() # None la primera vez
while True:
inicio = ahora_iso()
params = {}
if ultima_sync:
params["from_date"] = ultima_sync
barrer_todas_las_llamadas(params) # reutiliza la función de la Parte A
guardar_marca(inicio)
ultima_sync = inicio
time.sleep(INTERVALO_MINUTOS * 60)
Elige
Nsegún tu necesidad de frescura: cada 5–15 minutos suele ser razonable. Si recibes un429(límite de uso), respeta el headerRetry-Afterque viene en la respuesta y vuelve a intentar después de ese tiempo.
Detalle por llamada (cuando lo necesites)¶
GET /api/analytics/calls devuelve un resumen por llamada. Para la transcripción completa
y la grabación de una llamada puntual, pide su detalle:
Conviene traer el detalle solo de las llamadas que lo ameriten (por ejemplo, las que requieren seguimiento), no de todas en cada corrida.
Resumen¶
Barrido completo (Parte A):
GET /api/analytics/calls?limit=100
└─ has_more? → repetir con &cursor=next_cursor → hasta has_more=false
Loop incremental (Parte B):
cada N minutos:
from_date = última_sync
└─ barrer todas las páginas (cursor)
└─ upsert por call_id
guardar marca de sincronización
Cuando la entrega por webhooks (push) esté disponible, podrás complementar este loop o reemplazarlo. Por ahora, el polling con cursor + filtro por fecha es el patrón soportado.