Ciclo de vida de una campaña¶
Esta es la columna vertebral de la integración: una campaña de punta a punta, en el orden real en que ocurren las cosas. Si vas a conectar tu sistema con la plataforma, empieza aquí.
Cada paso enlaza a la referencia detallada. Las recetas son profundizaciones sobre casos puntuales; esta guía es el hilo que las conecta.
A lo largo de la guía usamos la base URL canónica mediante la variable API_BASE_URL:
1. Qué vas a construir¶
Un flujo que carga una cartera, lanza las llamadas, sigue el avance, y baja los resultados a tu propio sistema — sin que nadie tenga que abrir el panel.
Esta guía le habla a quien integra: la persona que guarda la API Key y escribe el código. Si lo que buscas es operar campañas a mano, eso se hace desde el panel y no necesita nada de aquí.
2. El ciclo, de un vistazo¶
flowchart TD
A["API Key"] --> B["GET /api/agents<br/>elegir agent"]
B --> C["GET /api/csv-template<br/>armar la cartera"]
C --> D{"¿cómo lanzas?"}
D -->|"CSV en el cuerpo"| E["POST /api/campaign/start"]
D -->|"archivo"| F["POST /api/campaign/start-upload"]
D -->|"más tarde"| G["POST /api/campaign/schedule"]
E --> H["GET /api/campaign/status"]
F --> H
G --> H
H --> I["pause / resume / stop"]
H --> J["GET /api/analytics/stats<br/>GET /api/analytics/calls<br/>GET /api/analytics/call/id"]
J --> K["GET /api/analytics/export<br/>Excel"]
J --> L["GET /api/campaigns/id/skip-log<br/>a quién NO se llamó"]
Los dos últimos son los que más se piden después de la primera campaña: el Excel para compartir con el resto del equipo, y el skip-log para responder "¿por qué no llamaron a esta persona?".
3. Antes de empezar¶
Necesitas tres cosas:
| Qué | Cómo se consigue | |
|---|---|---|
API Key con scope full |
Lanzar campañas es escritura | La emite un administrador. No es self-serve, y se muestra una sola vez |
| Al menos un agent asignado | Es quien conversa | Lo asigna tu equipo de Neuron a tu organización |
| Una cartera en CSV | A quién llamar | La armas tú, con la plantilla del paso 5 |
⚠️ No existe modo sandbox ni dry-run. Un
startexitoso dispara llamadas reales de inmediato, a los números que estén en el CSV. No hay forma de "ensayar" contra un entorno de prueba.Para la primera vez, usa una lista de un solo contacto con tu propio número. El procedimiento seguro completo está en la receta Probar sin impacto.
4. Autenticar¶
El chequeo más rápido de que tu key funciona es listar los agents. Es una lectura, así que sirve con cualquier scope:
{
"success": true,
"agents": [
{ "agent_id": "agent_abc123", "agent_name": "Cobranza Español", "agent_type": "collections" }
]
}
Anota el agent_id: lo vas a necesitar para lanzar.
| Respuesta | Qué significa |
|---|---|
200 con la lista |
La key está activa |
401 |
Key inválida, o falta el header X-API-Key |
403 |
La key existe pero no tiene scope para esta operación |
200 con agents: [] |
Tu organización todavía no tiene agents asignados |
No uses
POST /api/auth/loginpara integrar. Ese endpoint es la sesión del panel, con token de vida corta pensado para un navegador. Tu integración va conX-API-Key, que no expira por tiempo y ya viene atada a tu organización.
Detalle completo en Autenticación.
5. Armar la cartera¶
Cada tipo de campaña espera columnas distintas. Baja la plantilla del tipo que vas a usar:
curl "$API_BASE_URL/api/csv-template?type=collections" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890" \
-o plantilla.csv
Tipo (type) |
Columnas requeridas |
|---|---|
collections (default) |
to_number, lead_id, client_name, debt |
sdr |
to_number, lead_id, client_name |
cx |
to_number, lead_id, client_name |
interviewer |
to_number, lead_id, client_name, position |
to_numberen formato E.164 (ej.+525512345678).lead_ides tu identificador único, y es el que vas a usar para cruzar los resultados contra tu base.- Las columnas extra se preservan y viajan hasta el motor de llamadas. Úsalas para meter contexto propio.
- Las filas sin
to_numbero sinlead_idse saltan silenciosamente. Cuenta las filas de tu CSV contra eltotal_leadsde la respuesta.
6. Lanzar¶
Tres formas, según de dónde salga la cartera y cuándo quieras que suene el teléfono:
| Endpoint | Cuándo | Respuesta |
|---|---|---|
POST /api/campaign/start |
El CSV va en el cuerpo del request | 200 |
POST /api/campaign/start-upload |
Subes un archivo (multipart/form-data) |
200 |
POST /api/campaign/schedule |
Quieres que arranque más tarde | 201 |
El
201descheduleno es un descuido: crea un recurso programado que todavía no existía.startystart-uploaddevuelven200. Si tu cliente HTTP valida el código exacto, contempla los dos.
curl -X POST "$API_BASE_URL/api/campaign/start" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890" \
-H "Content-Type: application/json" \
-d '{
"name": "Cobranza agosto",
"csv_content": "to_number,lead_id,client_name,debt\n+525512345678,L001,Ana Pérez,1500",
"agent_id": "agent_abc123",
"campaign_type": "collections"
}'
{
"success": true,
"campaign_id": 123,
"total_leads": 1,
"agent_id": "agent_abc123",
"campaign_type": "collections",
"message": "Campaign 'Cobranza agosto' started"
}
Guarda el campaign_id. Es la llave de todo lo que sigue.
Si
total_leadsviene en0, la campaña quedó creada pero sin nadie a quien llamar — normalmente por un CSV mal formado. Revisa las columnas requeridas antes de reintentar.
Referencia completa de parámetros en Campañas.
7. Monitorear¶
curl "$API_BASE_URL/api/campaign/status" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890"
{
"success": true,
"status": { "state": "running", "campaign_id": 123, "active_calls": 3, "queue_size": 47 },
"campaign": { "...": "..." },
"global_status": { "...": "..." }
}
El campo que importa es state. Mientras diga running, la campaña sigue viva. Cuando
queue_size llega a 0 y active_calls también, terminó.
Control durante la corrida:
| Acción | Endpoint | Efecto |
|---|---|---|
| Pausar | POST /api/campaign/pause |
Deja de tomar contactos nuevos; las llamadas en curso terminan |
| Reanudar | POST /api/campaign/resume |
Vuelve a tomar de la cola donde quedó |
| Detener | POST /api/campaign/stop |
Corta la campaña. No se reanuda |
8. Leer los resultados¶
No hay entrega por webhooks todavía: los resultados se obtienen consultando. El orden natural es de lo general a lo particular.
Primero el resumen, para saber si vale la pena bajar el detalle:
curl "$API_BASE_URL/api/analytics/stats?campaign_ids=123" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890"
Después el detalle, paginado por cursor:
curl "$API_BASE_URL/api/analytics/calls?campaign_ids=123&limit=100" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890"
{
"success": true,
"calls": [
{
"call_id": "call_abc",
"campaign_id": 123,
"lead_id": "L001",
"client_name": "Ana Pérez",
"call_status": "completed",
"call_outcome": "promised_payment",
"sentiment": "positive",
"payment_promised": true,
"summary": "..."
}
],
"pagination": { "limit": 100, "count": 100, "has_more": true, "next_cursor": "eyJ..." }
}
Repite pasando cursor=<next_cursor> hasta que has_more sea false. No pagines por
número de página: el cursor es lo único estable cuando la campaña sigue escribiendo.
Y el detalle de una llamada puntual, cuando quieres la transcripción completa:
curl "$API_BASE_URL/api/analytics/call/call_abc" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890"
El patrón completo de sincronización incremental está en la receta Sincronizar resultados.
9. Bajar el Excel¶
Cuando el destinatario del resultado es una persona y no un sistema, este endpoint devuelve directamente el archivo — el mismo que produce el botón de exportar del panel.
curl "$API_BASE_URL/api/analytics/export?campaign_ids=123" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890" \
-o resultados.xlsx
La respuesta es el archivo binario, con el nombre sugerido en el header
Content-Disposition (por ejemplo analytics_miorg_20260817_143022.xlsx).
Filtros disponibles — todos opcionales, y combinables entre sí:
| Parámetro | Valor | Qué hace |
|---|---|---|
campaign_ids |
"1,2,3" |
Una o varias campañas, separadas por coma |
campaign_type |
collections, sdr, cx, interviewer |
Por tipo de campaña |
call_outcome |
ej. promised_payment |
Por resultado de la llamada |
sentiment |
ej. positive |
Por sentimiento detectado |
payment_filter |
committed | none |
Solo quienes se comprometieron, o solo quienes no |
from_date / to_date |
YYYY-MM-DD |
Rango de fechas |
exclude_inbound |
true | false (default false) |
Deja afuera las llamadas entrantes |
El libro trae dos hojas cuando corresponde: el detalle de llamadas, y una segunda hoja "Promesas de Pago" con solo las filas donde hubo un compromiso. Si no hubo ninguna promesa, esa hoja no se agrega.
Sobre exclude_inbound: viene apagado a propósito. Si tu organización trabaja solo llamadas
entrantes, prenderlo te devuelve un archivo vacío.
| Respuesta | Cuándo |
|---|---|
200 |
El archivo .xlsx |
400 {"error": "Invalid campaign_ids format"} |
campaign_ids trae algo que no es un número |
404 {"error": "No data to export"} |
Los filtros no dejaron ninguna fila |
429 |
Superaste el límite de exportaciones |
Límite: 60 exportaciones por hora, por organización. Al pasarlo, la respuesta trae
retry_after_seconds con cuánto esperar:
{
"error": "Too many exports. Please wait before exporting again.",
"error_code": "RATE_LIMITED",
"retry_after_seconds": 3600
}
Tope de filas: 250.000 por archivo. Si tu selección lo supera, el archivo se corta ahí. Para volúmenes mayores, exporta por rango de fechas o por campaña en vez de todo junto.
Y ten en cuenta que armar un libro grande toma tiempo — decenas de segundos para carteras de decenas de miles de llamadas. Pon un timeout generoso en tu cliente HTTP.
10. Por qué no se llamó a un contacto¶
Esta es la pregunta que más aparece cuando alguien compara su CSV contra los resultados: "subí 5.000 contactos y aparecen 4.200 llamadas, ¿qué pasó con los otros 800?"
Antes de discar, la plataforma descarta contactos que no corresponde llamar — porque ya prometieron pagar, porque hay un callback agendado, porque pidieron no ser contactados. Cada descarte queda registrado con su motivo:
curl "$API_BASE_URL/api/campaigns/123/skip-log" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890"
{
"rows": [
{
"id": 9012,
"campaign_id": 123,
"contact_phone": "+525512345678",
"contact_id": "3f1c…",
"reason": "promise_future",
"detail": "Payment promised for 2026-08-25",
"skipped_at": "2026-08-17T14:02:11+00:00"
}
],
"counts": { "promise_future": 512, "callback_pending": 203, "refused": 85 },
"total_skipped": 800
}
counts es lo que quieres para reportar — el desglose agregado por motivo. rows trae el
detalle contacto por contacto, ordenado del más reciente al más viejo, con tope de 500 filas
por defecto. Ajústalo con ?limit=.
Los motivos posibles:
reason |
Qué pasó |
|---|---|
promise_future |
Ya se comprometió a pagar en una fecha futura |
promise_recent |
Se comprometió sin fecha, y todavía está dentro del plazo de gracia |
callback_pending |
Tiene una devolución de llamada agendada |
cooldown_answered |
Ya atendió una llamada por ese mismo crédito en las últimas 24 horas |
refused |
Pidió no ser contactado |
unreachable |
Marcado como inalcanzable de forma permanente |
retry_backoff |
Agotó los reintentos, o está esperando el próximo turno |
detail trae la explicación en texto de ese caso puntual.
| Respuesta | Cuándo |
|---|---|
200 |
El desglose, aunque esté vacío |
404 {"error": "Campaign not found"} |
La campaña no existe, o no es de tu organización |
403 {"error": "Not authorized"} |
La key no alcanza para esa campaña |
Un
total_skippeden0significa que no se descartó a nadie — no que el registro haya fallado. Si tu CSV y tus llamadas no cuadran y el skip-log está vacío, la diferencia está en el CSV: revisa las filas sinto_numbero sinlead_id, que se descartan antes de llegar aquí.
11. Programar en vez de disparar ahora¶
Si no quieres que suene el teléfono en el momento del request:
curl -X POST "$API_BASE_URL/api/campaign/schedule" \
-H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890" \
-H "Content-Type: application/json" \
-d '{ "...": "..." }'
Devuelve 201. Consulta y cancela lo programado con GET /api/campaign/scheduled y
DELETE /api/campaign/scheduled/{campaign_id}.
Parámetros y manejo de husos horarios en Campañas.
Dónde está cada detalle¶
| Si buscas | Ve a |
|---|---|
| El contrato exacto de un endpoint | Endpoints |
Todos los endpoints en una pantalla, y probar los GET |
Referencia interactiva |
| Probar sin llamar a nadie de verdad | Receta 02 — Probar sin impacto |
| Sincronizar resultados a tu base | Receta 03 — Sincronizar resultados |
| Subir carteras grandes por archivo | Receta 04 — Carga CSV |
| Cuántas requests puedes hacer | Límites de uso |
| Qué significa un código de error | Errores y troubleshooting |
| El esquema completo, en máquina | openapi.yaml |