2CXVoIP Communications API — guía de integración para el CRM
Esta API te da: login de agentes, un token de sesión para el softphone embebido, un canal en tiempo real para llamadas/SMS/presencia, y envío de SMS. La sesión del usuario (quién está logueado, en qué pestaña, su historial de conversación por contacto) la maneja tu CRM — esta API solo provee la infraestructura de comunicaciones (línea telefónica, saldo, y los eventos de la llamada).
Base URL: https://webphone.2cxvoip.com
Todas las respuestas de error usan el formato: { "error": "mensaje" }
1. Autenticación
POST /auth/login
// body
{ "email": "agente@tuempresa.com", "password": "..." }
// 200
{
"token": "<jwt, válido 7 días>",
"user": {
"id": "uuid", "email": "...", "name": "...", "role": "agent|supervisor|admin",
"status": "active", "team": "Ventas", "line": "+19165551234"
}
}
Guarda token y mándalo como Authorization: Bearer <token>
en cada request subsecuente (incluyendo el mensaje de auth del WebSocket, ver §3).
Rate limit: 5 intentos por IP cada 15 minutos.
GET /auth/me
Header: Authorization: Bearer <token>. Devuelve el mismo shape
que user arriba — úsalo para validar el token al cargar tu app.
2. Softphone embebido
GET /api/webphone/token
Header: Authorization: Bearer <token>.
// 200
{ "session_token": "...", "line": "+19165551234", "expires_in": 900 }
session_token es válido ~15 minutos — pide uno nuevo antes de que
expire (por ejemplo cada 10 min mientras el agente esté activo). Si el agente
todavía no tiene línea asignada, la API responde
503 { "error": "No phone line assigned yet..." }.
Widget del softphone
No instales ningún SDK de terceros. Carga el widget de 2CXVoIP directo desde el CDN:
<script src="https://cdn.2cxvoip.com/softphone.js"></script>
Esto expone window.TwoCXSoftphone:
TwoCXSoftphone.connect(session_token) // del GET /api/webphone/token de arriba
TwoCXSoftphone.on('ready', () => {...})
TwoCXSoftphone.on('incoming', ({ from }) => {...}) // muestra tu popup de llamada entrante
TwoCXSoftphone.on('callStateChange', ({ state }) => {...})
TwoCXSoftphone.on('error', (err) => {...})
TwoCXSoftphone.dial('+19165551234') // llamada saliente
TwoCXSoftphone.answer()
TwoCXSoftphone.reject()
TwoCXSoftphone.hangup()
TwoCXSoftphone.disconnect()
Nota honesta: el widget saca cualquier dependencia del carrier de tu propio
código y package.json — es la exposición práctica que más importa. No
garantiza que el tráfico de red del navegador sea 100% irreconocible ante alguien
inspeccionando la pestaña Network a propósito; eso queda fuera de lo que un
wrapper del lado del cliente puede controlar.
3. Canal en tiempo real — WS /ws/presence
Conecta un socket por agente logueado. Primer mensaje siempre debe ser auth:
→ { "type": "auth", "token": "<jwt de /auth/login>" }
← { "type": "auth_ok", "userId": "uuid" }
// o { "type": "auth_error" } seguido de cierre del socket
Mensajes que puedes enviar
{ "type": "status", "status": "available" | "busy" | "in_call" | "offline" }
{ "type": "ping" } // heartbeat, responde { "type": "pong" }
Eventos que el servidor te empuja
presence_update — snapshot completo cada vez que cualquier agente cambia de estado (útil para un wallboard):
{ "type": "presence_update", "agents": [
{ "id": "uuid", "name": "...", "email": "...", "role": "agent",
"team": "Ventas", "line": "+1916...", "status": "available" }
] }
call_incoming — una llamada está entrando a la línea del
agente. Es solo la señal para mostrar el popup de "llamada entrante" con datos del
caller en tu UI — el softphone (widget del navegador, autenticado con
session_token) es quien realmente contesta/timbra:
{ "type": "call_incoming", "from": "+1...", "to": "+1916...", "callControlId": "..." }
sms_inbound — mensaje SMS entrante ya guardado, para refrescar el hilo de conversación del contacto:
{ "type": "sms_inbound", "conversationId": "uuid", "from": "+1...", "to": "+1916...",
"text": "...", "contactId": "uuid" }
Cada evento solo se entrega al socket del agente dueño de la línea — no hay broadcast entre agentes.
GET /api/presence
Snapshot inicial (mismo shape que presence_update.agents) para
cuando tu app carga y todavía no tiene el WebSocket conectado.
4. SMS
POST /api/sms/send
// body
{ "contact_id": "uuid", "body": "texto del mensaje" }
Envía desde la línea asignada al agente autenticado. 404 si el
contacto no existe, 503 si el agente no tiene línea.
GET /api/contacts/:id/thread
Historial completo (SMS + llamadas) de un contacto:
{
"contact": { "id": "...", "phone_e164": "...", "name": "...", "email": "..." },
"messages": [{ "id", "direction", "body", "sent_at", "agent_name" }],
"calls": [{ "id", "direction", "status", "duration_seconds", "started_at", "ended_at", "agent_name" }]
}
Útil si tu CRM todavía no tiene su propio store de conversaciones y quiere apoyarse en este endpoint — no es obligatorio, es una conveniencia.
5. Lo que NO es parte de esta API
POST /hooks/events— webhook interno del carrier. No lo expongas ni lo documentes hacia afuera; no aplica autenticación de agente.POST /api/users/sync— usado por tu backend (no por el navegador del agente) para crear/desactivar agentes, protegido con headerx-sync-secretcompartido fuera de banda. No es parte del flujo del agente.
6. CORS
Tu dominio de frontend debe estar en el allowlist del servidor antes de poder llamar esta API desde el navegador. Escribí a support@2cxvoip.com con tu(s) dominio(s) y tu número de cliente para que los agreguemos.