Volver a Systems
Caso de éxito · Plataforma

Una plataforma de operaciones para contact center

Una plataforma a la medida que gestiona toda una operación de call center desde un solo lugar — capturando automáticamente cada llamada desde telefonía en la nube, convirtiéndola en un registro rastreable, y consolidando llamadas, tickets, campañas y seguimientos en dashboards y reportes en tiempo real.

Disciplina
Diseño e ingeniería
Sector
Contact center · Operaciones de soporte
Tipo
Plataforma a la medida
Stack principal
Next.js · NestJS · PostgreSQL
app.centerquest.do/operations/reports
Reporte de sede mostrando contactos, tickets, tasa de resolución y llamadas, además de gráficos de actividad de llamadas, tickets y registros manuales, y desgloses por disposición, dirección y prioridad.

La operación gestionaba campañas telefónicas entrantes y salientes — onboarding y cobros — en docenas de sedes de clientes en representación de sus propietarios. Llamadas, SMS, tickets de soporte, notas manuales y seguimientos vivían en lugares distintos. La actividad telefónica se registraba a mano, no existía un registro único del cliente, ni una vista en vivo de lo que estaba haciendo el piso en ese momento.

Diseñamos y construimos un único sistema central para toda la operación telefónica. La telefonía en la nube alimenta las llamadas automáticamente; cada interacción se convierte en un registro que un agente puede clasificar y dar seguimiento; y todo lo que la operación toca — clientes, campañas, sedes, tickets — se consolida en dashboards en tiempo real y reportes por sede que el equipo puede exportar bajo demanda.

De un teléfono sonando a un reporte

  1. Ocurre una llamada

    Entrante o saliente, cada llamada se realiza a través de telefonía en la nube — los agentes marcan y responden desde un softphone integrado directamente en la plataforma.

  2. La plataforma la captura automáticamente

    La telefonía envía cada evento a un webhook verificado. El sistema lo convierte en un registro de llamada, encuentra o crea al cliente, y asigna al agente — sin captura manual de datos.

  3. El agente lo gestiona en una sola vista

    En el Contact Center, llamadas, tickets y registros manuales comparten una sola lista. El agente define una disposición, un resultado de campaña, notas y un seguimiento.

  4. Todos lo ven en vivo

    Los cambios se transmiten a cada pantalla mediante WebSockets — indicadores de llamada en vivo, asignaciones de tickets, devoluciones de llamada pendientes — sin recargar la página ni hacer polling.

  5. Nada se pierde

    Tareas en segundo plano y programadores vigilan devoluciones de llamada, seguimientos y llamadas programadas vencidas, y notifican al agente correcto en el momento en que algo vence.

  6. Todo se consolida

    Llamadas, tickets y resultados se consolidan en dashboards por sede y por campaña — y en exportaciones a PDF y Excel generadas bajo demanda.

app.centerquest.do/operations/dashboard
Dashboard de marketing mostrando tasa de contacto, total de disposiciones, mensajes SMS y llamadas por sede, con gráficos de desempeño de campañas, opciones de campaña, interacción por SMS y mezcla de disposiciones.

Dos servicios, una sola puerta de entrada

El navegador solo se comunica con un backend-for-frontend (BFF) de Next.js, que mantiene la sesión y reenvía cada solicitud, servidor a servidor, a una API de NestJS. Esa API es dueña de los datos en PostgreSQL, con Redis para colas y limitación de tasa, y almacenamiento de objetos para grabaciones de llamadas y adjuntos. Un solo proxy se ubica frente a todo el backend — el navegador nunca guarda un token, y cada llamada pasa por un único camino reforzado.

Construido para mantenerse en vivo

Tiempo real sobre WebSockets

Un canal de Socket.IO envía cambios de llamadas en vivo, asignaciones de tickets y devoluciones de llamada pendientes exactamente al agente que las necesita. El cliente agrupa ráfagas de eventos en una sola actualización con debounce, para que la interfaz se mantenga al día sin hacer polling al servidor.

Webhooks de telefonía verificados

Cada evento de telefonía llega a un webhook autenticado con una verificación de token de tiempo constante. Un motor de ingesta idempotente absorbe el desorden del tráfico telefónico real — eventos duplicados, transferencias, buzón de voz vs. llamada perdida, tramos de puente — y normaliza los números para que un cliente nunca se duplique.

Tareas en segundo plano y programación

Una cola de tareas respaldada por Redis (BullMQ) y redes de seguridad con cron marcan los elementos vencidos, deduplican notificaciones a nivel de base de datos, y llegan al agente correcto — para que una devolución de llamada prometida nunca se pierda silenciosamente.

Integraciones

Telefonía en la nube para llamadas y SMS con un softphone integrado; un servicio de correo transaccional para mensajes de cuenta y restablecimiento; y almacenamiento de objetos para grabaciones y adjuntos, servidos mediante URLs firmadas de corta duración.

El stack completo

CapaTecnologías
Frontend
  • Next.js 16 (App Router)
  • React 19 + React Compiler
  • TypeScript
  • Tailwind CSS v4
  • shadcn/ui + Radix
  • SWR
  • Socket.IO client
  • Recharts
Backend
  • NestJS 11
  • Node + Express
  • TypeScript
  • TypeORM
  • class-validator
  • Swagger / OpenAPI
Datos y tiempo real
  • PostgreSQL
  • Redis
  • Socket.IO
  • BullMQ
Infraestructura
  • Railway (API)
  • Vercel (web)
  • S3 object storage
  • Brevo email
Calidad
  • Jest + Supertest
  • ESLint + Prettier
  • Unlighthouse

Reforzado en cada capa

ControlCapaQué protege
Sesión BFF HttpOnlySesiónEl token de autenticación vive en una cookie HttpOnly y Secure que el JavaScript del navegador nunca puede leer; un backend-for-frontend la adjunta servidor a servidor en cada solicitud.
JWT + bcryptAutenticaciónAutenticación JWT sin estado con contraseñas hasheadas con bcrypt. La API se niega a iniciar sin su clave secreta de firma.
Acceso basado en rolesAccesoTres roles — agente, admin, dev — aplicados tanto en la API con guards como en el edge con controles de ruta.
Tokens de socket con alcance limitadoAccesoLos WebSockets usan un token separado de ~2 minutos que la API REST rechaza explícitamente, para que un token de socket filtrado nunca pueda alcanzar tus datos.
Limitación de tasa distribuidaLímites de tasaUn limitador respaldado por Redis compartido entre réplicas del servidor, con clave por usuario, con límites más estrictos en el inicio de sesión y el restablecimiento de contraseña.
Webhooks de tiempo constanteWebhooksLos webhooks entrantes de telefonía se autentican con una comparación de tokens de tiempo constante que resiste ataques de temporización.
Validación estricta de entradasValidaciónCada cuerpo de solicitud se valida y se reduce a una lista de campos permitidos antes de llegar a cualquier lógica de negocio.
Transporte reforzadoTransporteCabeceras de seguridad Helmet con una política de seguridad de contenido, CORS restringido al origen del frontend, y timeouts por solicitud.
Manejo seguro de archivosAlmacenamientoLas subidas tienen un límite de tamaño y se almacenan de forma privada; los archivos se sirven solo mediante URLs firmadas de corta duración — nunca como objetos públicos.
Manejo cuidadoso de secretos y datosSecretosLos secretos se leen del entorno y se enmascaran en los logs, la sincronización destructiva del esquema está deshabilitada por defecto, los campos sensibles se excluyen de las respuestas, y se usan borrados suaves en todo el sistema.
Claves de API con alcance limitadoClaves de APIEl acceso programático usa claves de API hasheadas con alcances explícitos, expiración y seguimiento de último uso.

Un solo sistema, toda la operación

  • Llamadas
  • Tickets
  • Registros manuales
  • Llamadas programadas
  • Clientes
  • Campañas
  • Sedes
  • Propietarios
  • Líneas telefónicas
  • Analítica de SMS
  • Notificaciones
  • Usuarios y agentes
  • Reportes y exportaciones
  • Claves de API
  • Diccionarios configurables

Más de la plataforma

¿Piensas en un sistema como este?

Cada proyecto empieza con una conversación sobre cómo funciona realmente tu operación — no con una plantilla.