Appearance
Inicio rápido: de cero a la primera llamada
English version: Quickstart — zero to first call.
Esta guía te lleva de una máquina vacía a tu primer intento de llamada con puerta de cumplimiento, de punta a punta, contra el stack local: el mismo harness docker-compose sobre el que corre la propia suite e2e de la plataforma. Cada llamada que hace es real (SIP a través de Kamailio/FreeSWITCH, filas de CDR reales, evidencia de cumplimiento real); lo único simulado es el operador telefónico del otro extremo, así que no suena ningún teléfono de verdad y nada cuesta dinero.
Vas a: levantar el stack → obtener un token de API → crear una campaña → importar deudas → poner un asiento de agente en línea → ver cómo el dialer hace el primer intento de llamada → leer el resultado durable y el veredicto de cumplimiento.
Tiempo total: ~1 hora la primera vez (casi todo es una construcción de imágenes que se hace una sola vez); ~15 minutos en las siguientes.
Listo para copiar y pegar
Cada paso es un curl ejecutable. Los ejemplos usan jq para capturar ids en variables de shell; si no lo tienes, copia los ids a mano de las respuestas JSON.
Requisitos previos (~10 min)
- Linux (o macOS) con Docker + docker compose v2,
make,git,curl. - Una cuenta de GitHub con acceso a los repositorios de
DialerDigitaly un token conread:packages(el harness descarga la imagen publicada de FreeSWITCH desde GHCR). - Puertos libres en el host:
4000(API de core),5060/5061/5070/5080/8021/8443/22222(plano telecom),55432/56379/55060/58080/58090(mocks). Un softphone que ya corra en el host se queda con5060: muévelo o ciérralo.
El fichero compose construye repos hermanos desde el código fuente, así que clónalos lado a lado:
bash
mkdir dd && cd dd
gh repo clone DialerDigital/integration-tests
gh repo clone DialerDigital/core
gh repo clone DialerDigital/kamailio
gh repo clone DialerDigital/rtpengine
gh repo clone DialerDigital/dashboard
docker login ghcr.io # PAT with read:packages, for the FreeSWITCH image
cd integration-testsPaso 1: levanta el stack (~10–30 min la primera vez, ~2 min después)
bash
make up-telecomEsto arranca los mocks (Postgres, Dragonfly, un operador telefónico simulado con SIPp, STT/TTS), construye y levanta el plano telecom (Kamailio, rtpengine, FreeSWITCH, core), ejecuta las migraciones de base de datos de core y espera hasta que la API responda. La primera ejecución compila la imagen de rtpengine desde el código fuente: cuenta con 10–30 minutos. Las siguientes usan la caché de capas de Docker.
Verifica:
bash
make smoke
curl -s http://127.0.0.1:4000/healthz
# -> {"status":"ok","service":"dialer-core","version":"..."}El dashboard del propietario también arranca, en http://127.0.0.1:8080: útil para ver el piso en vivo en un navegador mientras ejecutas los pasos siguientes.
Paso 2: obtén tu token de API (~1 min)
En local emites la primera clave del tenant con la semilla idempotente (en producción la primera clave se emite en el alta: ver Autenticación). La semilla crea el tenant de demostración local-dev, su clave de API, un asiento de agente en la extensión SIP 1000 y una pequeña campaña de demostración:
bash
docker compose --profile mocks --profile telecom run --rm --no-deps \
-e DATABASE_USER=dialer -e DATABASE_PASSWORD=testpass \
-e SEED_API_TOKEN=localdev-m2-demo-token \
core-migrate eval 'Dialer.Release.seed()'Fijar SEED_API_TOKEN hace el token reproducible entre ejecuciones. Exporta tus variables de sesión:
bash
API=http://127.0.0.1:4000
TOKEN=localdev-m2-demo-tokenComprueba la clave: GET /v1/me devuelve el tenant autenticado:
bash
curl -s $API/v1/me -H "Authorization: Bearer $TOKEN"
# -> {"tenant":{"id":"...","name":"local-dev","status":"active",...}}Un 401 unauthorized aquí significa que la semilla no corrió o que el token no coincide: vuelve a ejecutar el comando de la semilla de arriba.
Paso 3: crea una campaña (~1 min)
Las campañas se crean en draft y se inician de forma explícita. Dos ajustes propios del laboratorio, ambos honestos sobre lo que hacen:
calling_window_*totalmente abierta: es política de la campaña, no la ley: la puerta de horas de silencio del motor de cumplimiento ([08:00, 21:00) en la zona horaria del consumidor) sigue aplicándose de forma independiente en cada marcación.abandonment_threshold: 0.9: si corres sin cabeza (sin softphone que conteste, siguiente paso), los puentes al agente fallan y cuentan comoabandoned; el valor por defecto real de 3 % congelaría el pacing tras el primero. Mantén el valor por defecto en cualquier despliegue real.
bash
CID=$(curl -s -X POST $API/v1/campaigns \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "quickstart-'$(date +%s)'",
"calling_window_start": "00:00:00",
"calling_window_end": "23:59:59",
"default_timezone": "America/Chicago",
"abandonment_threshold": 0.9,
"caller_ids": ["+13125550199"]
}' | jq -r .campaign.id)
echo $CIDcampaign_type vale predictive por defecto: el runner hace el pacing y marca por sí solo una vez iniciada. Consulta el esquema de campaña para ver todos los campos.
Paso 4: importa deudas (~2 min)
Los leads entran por la importación JSON masiva: idempotente (las deudas se upsertan por external_ref), con tolerancia a fallos por elemento. Importamos dos a propósito:
- un contacto de línea fija que el motor puede marcar, y
- un contacto móvil sin consentimiento TCPA registrado, que el motor debe rechazar: ese rechazo es tu primer veredicto de cumplimiento.
Una cosa que hay que acertar: la puerta de horas de silencio lee la zona horaria del contacto. Elige una donde ahora mismo sea entre las 08:00 y las 21:00 hora local:
bash
TZ=America/Chicago date +%H:%M # inside 08:00-21:00? use it. Otherwise try
TZ=Europe/Madrid date +%H:%M # ...or Asia/Tokyo, Pacific/Auckland, etc.
LEAD_TZ=America/Chicago # <- your pickbash
curl -s -X POST $API/v1/debts/import \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"debts": [
{
"external_ref": "quickstart-0001",
"consumer_ref": "quickstart-consumer-1",
"debt_type": "other",
"amount_cents": 50000,
"currency": "USD",
"campaign_id": "'$CID'",
"contacts": [
{ "phone_e164": "+13125551001", "line_type": "landline",
"timezone": "'$LEAD_TZ'", "us_state": "IL", "is_primary": true }
]
},
{
"external_ref": "quickstart-0002",
"consumer_ref": "quickstart-consumer-2",
"debt_type": "other",
"amount_cents": 75000,
"currency": "USD",
"campaign_id": "'$CID'",
"contacts": [
{ "phone_e164": "+13125551002", "line_type": "mobile",
"timezone": "'$LEAD_TZ'", "us_state": "IL", "is_primary": true }
]
}
]
}'
# -> {"imported":2,"failed":[]}external_ref es la clave del contador de frecuencia de la Reg F: mantenla estable y única por cuenta. Detalles en la guía de la API REST.
Comprobación opcional: previsualiza el siguiente lead marcable sin marcar:
bash
curl -s $API/v1/campaigns/$CID/next-lead -H "Authorization: Bearer $TOKEN"
# -> {"lead":{"debt_ref":"quickstart-0001","contact_phone":"+13125551001",...}}Paso 5: pon un asiento de agente en línea (~1 min)
La semilla ya creó un asiento en la extensión SIP 1000. Regístralo para que las llamadas conectadas tengan dónde puentear:
bash
curl -s -X POST $API/v1/agents/checkin \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"sip_extension": "1000", "device_mode": "external"}'
# -> {"agent":{...,"presence":"available"}, "sip":{...}}Dos formas de ejecutar el siguiente paso:
- Sin cabeza (sin audio, totalmente por script). No registres ningún dispositivo. La pata del operador telefónico igualmente se completa; el puente al asiento falla y el intento se registra como
abandoned. Aun así obtienes una fila durable de CDR con su instantánea de cumplimiento: sirve para una primera ejecución. - Escúchalo. Registra un softphone SIP como
1000/ contraseñalocaldev123contra el perfil interno de FreeSWITCH en<your-host-LAN-IP>:5070, y contesta cuando suene: el intento se registra comoanswered_human. Los detalles de configuración están en elREADME-LOCAL.mddel harness («Zoiper setup»).
Paso 6: inicia la campaña y mira la primera llamada (~3 min)
bash
curl -s -X POST $API/v1/campaigns/$CID/start \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"gateway": "default", "batch_size": 2, "tick_ms": 1000}'
# -> {"campaign":{...,"state":"running"}}gateway: "default" es la troncal del operador telefónico simulado del stack local: el runner envía un originate ESL real a FreeSWITCH, que marca al operador telefónico simulado SIPp: este contesta, reproduce ~3 s de medios y cuelga. Sondea los contadores en vivo (el mismo payload se emite cada ~2 s por el piso en vivo WebSocket):
bash
curl -s $API/v1/campaigns/$CID/stats -H "Authorization: Bearer $TOKEN"En unos ~20 segundos deberías ver dialed ≥ 1 (el lead de línea fija) y blocked ≥ 1 (el lead móvil que rechazó la puerta de consentimiento). Si dialed se queda en 0 y blocked sube, la zona horaria de tu lead está fuera de las horas de silencio: vuelve al Paso 4.
Paso 7: lee el resultado y el veredicto de cumplimiento (~3 min)
La fila del CDR: solo-anexar, una fila por intento, con la decisión de cumplimiento congelada en el momento de marcar:
bash
curl -s "$API/v1/call_attempts?campaign_id=$CID&limit=10" \
-H "Authorization: Bearer $TOKEN"Mira el primer intento: disposition (answered_human si contestó un softphone, abandoned en la ruta sin cabeza), started_at/answered_at/ended_at, hangup_cause y, lo que importa en una auditoría, compliance_snapshot: la evidencia congelada de la decisión que permitió esta marcación, inmune a cambios posteriores en los datos.
La evidencia del rechazo: el lead móvil nunca se marcó, y ese rechazo también es una fila durable, escrita antes de saltar el lead:
bash
curl -s "$API/v1/stats/blocked?campaign_id=$CID" -H "Authorization: Bearer $TOKEN"
# -> {"total":1,"by_gate":{"tcpa_no_consent":1},"daily":[...]}Ese par (la llamada que hiciste con su instantánea congelada, y la llamada que no hiciste con su motivo) es el modelo de evidencia de la plataforma en miniatura. Consulta Errores y cumplimiento y los endpoints de evidencia de cumplimiento (informe de violaciones evitadas, paquete de defensa por deuda).
Paso 8: apaga todo (~1 min)
bash
curl -s -X POST $API/v1/campaigns/$CID/stop -H "Authorization: Bearer $TOKEN"
curl -s -X POST $API/v1/agents/checkout \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"sip_extension": "1000"}'
make down # stop the whole stack (state persists in the compose volumes)Solución de problemas
| Síntoma | Causa probable / arreglo |
|---|---|
401 unauthorized en todas las llamadas | La semilla no corrió, o TOKEN no coincide con SEED_API_TOKEN: repite el Paso 2. |
stats es null en las estadísticas de la campaña | El runner no está levantado: ¿devolvió 200 el POST .../start? |
dialed: 0, blocked subiendo | Puerta de horas de silencio: la zona horaria del lead está fuera de 08:00–21:00 locales. Reimporta con una zona horaria de día (los leads bloqueados reintentan tras un enfriamiento, 5 min por defecto). |
| Ninguna fila de CDR | Puede que core aún no tenga enlace ESL con FreeSWITCH: make logs, busca «ESL ready». |
make up-telecom falla por puertos | Algo ocupa un puerto telecom: sudo ss -lunp | grep -E ':5060|:5070|:5080|:8021'. |
| Falla la descarga de la imagen de FreeSWITCH | docker login ghcr.io con un PAT que tenga read:packages. |
Adónde ir después
- Autenticación: rotación de claves, autenticación WebSocket, modelo de tenancy.
- Guía de la API REST: dispositions tipadas (registra una promesa de pago en tu primera llamada), callbacks, supervisión, DIDs.
- Eventos WebSocket: sustituye el sondeo del Paso 6 por push.
- El
make demodel harness: una demostración de punta a punta, por script, del ciclo vendible completo sobre este mismo stack.
Endpoints de producción
Todo lo anterior se corresponde 1:1 con producción, con tres diferencias:
https://api.usa.dialerdigital.com # production (USA cell)- Claves: las claves de API de producción (
dd_...) se emiten en el alta y las rotas tú mismo con/v1/api_keys: no hay semilla. - Operador telefónico: sin simulación: las llamadas salen por la configuración de operador telefónico aprovisionada en tu cuenta, a números reales.
- Lo que está en juego: corren las mismas puertas de cumplimiento, pero contra consumidores reales. Los atajos de laboratorio de esta guía (ventana de llamadas totalmente abierta, umbral de abandono 0.9) no tienen cabida ahí.