Saltar a contenido

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:

API_BASE_URL="https://api.neuronstudio.ai"

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 start exitoso 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:

curl "$API_BASE_URL/api/agents" \
  -H "X-API-Key: nrn_live_abcdef1234567890abcdef1234567890"
{
  "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/login para integrar. Ese endpoint es la sesión del panel, con token de vida corta pensado para un navegador. Tu integración va con X-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_number en formato E.164 (ej. +525512345678).
  • lead_id es 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_number o sin lead_id se saltan silenciosamente. Cuenta las filas de tu CSV contra el total_leads de 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 201 de schedule no es un descuido: crea un recurso programado que todavía no existía. start y start-upload devuelven 200. 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_leads viene en 0, 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_skipped en 0 significa 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 sin to_number o sin lead_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