Crea un agente de voz en 20 minutos con ElevenLabs y Twilio
- Publicado
- Última actualización
EscucharEscucha este artículo
Un agente de voz puede recibir llamadas, transcribir en tiempo real a quienes llaman con
Para desarrolladores, la pila completa que vas a usar es ElevenLabs para síntesis de voz (Flash v2.5) y transcripción (Scribe v2 Realtime), Twilio para telefonía y OpenAI o Anthropic como LLM. Todos estos componentes se pueden intercambiar, así que puedes elegir los que mejor conozcas.
En este artículo verás cómo crear un agente de voz en 20 minutos usando Node.js y Typescript. Si prefieres una alternativa gestionada que se encargue de turnos, interrupciones y telefonía sin tener que mantener la cascada tú mismo, entra en ElevenAgents.
Cómo funciona la arquitectura de un agente de voz
Antes de escribir código, ayuda entender cómo se conectan los tres servicios de tu stack tecnológico.
- Twilio: gestiona la llamada y el transporte de audio.
- ElevenLabs: gestiona STT con Scribe v2 Realtime y TTS con Flash v2.5.
- LLM: se encarga de llamar a herramientas y generar la respuesta.
Cada etapa es un adaptador sencillo, por eso puedes cambiar un servicio por otro sin tocar el resto. Por ejemplo, puedes cambiar el LLM de OpenAI por Anthropic sin tener que reescribir los demás componentes.
Una llamada llega a tu servidor a través de Twilio. Twilio responde la llamada PSTN, abre un WebSocket hacia tu servidor y reenvía el audio del llamante como un flujo de frames mu-law codificados en base64. Tu servidor ejecuta la cascada y envía el audio sintetizado de vuelta por ese mismo WebSocket, y Twilio lo reproduce al llamante.

Este es el flujo que vas a seguir para crear un agente de voz:
Un llamante marca tu número de Twilio. Twilio obtiene un documento TwiML de tu webhook. El TwiML indica a Twilio que abra un Media Stream hacia tu endpoint WebSocket. Twilio envía el audio entrante como eventos JSON con payloads mu-law base64 (ulaw_8000).
Tu servidor reenvía los fragmentos de audio a Scribe v2 en Tiempo Real para la transcripción en streaming. Cuando se finaliza el turno del llamante, envías la transcripción al LLM y luego sintetizas la respuesta con Flash v2.5 en ulaw_8000. Envías los frames mu-law sintetizados de vuelta a Twilio por el WebSocket, codificados en base64, y Twilio los reproduce al llamante.
Scribe v2 Realtime emite transcripciones parciales con unos 150ms de latencia, y Flash v2.5 tarda unos 75ms en inferencia de modelo, sin contar la latencia de red y aplicación. El LLM es el componente más grande y menos predecible en el tiempo hasta el primer audio, y ahí es donde va la mayor parte de la latencia. Para acortar ese tiempo, transmitimos la salida del LLM token a token y empezamos a sintetizar antes de que termine la frase.
Para ver los motivos detrás de estas elecciones, consulta la descripción de modelos y la explicación sobre cómo funciona la latencia.
Qué necesitas antes de empezar a crear un agente de voz
Esta guía asume que tienes cuatro cosas preparadas. Cada una se configura rápido, pero si falta alguna, el servidor no podrá funcionar.
Estos son los requisitos previos que debes comprobar:
- Un número de Twilio con capacidad de voz: Apunta el número junto con tu Account SID y Auth Token desde la consola de Twilio.
- Clave API de ElevenLabs: La creas en tu panel de ElevenLabs. La clave viaja en la cabecera xi-api-key y es secreta, así que mantenla solo en el servidor. Consulta
- Clave de API del LLM:Este tutorial trata Anthropic Claude y OpenAI como backends intercambiables, así que elige uno.
- Ngrok (o cualquier túnel) para desarrollo local: Twilio necesita acceder a tu servidor por una URL pública HTTPS y WSS, y ngrok lo facilita sin desplegar nada.
Configura tus secretos como variables de entorno y nunca los subas.
Luego inicia un túnel apuntando al puerto que usará tu servidor:
Cómo funciona el protocolo Twilio Media Streams
Twilio no te da un socket de audio en bruto. En su lugar, envuelve todo en un protocolo JSON estructurado sobre WebSocket. Entender los cuatro tipos de eventos y el formato de envío hará que el handler WebSocket del Paso 2 tenga sentido antes de escribirlo.
Cuando Twilio se conecta a tu WebSocket, envía una secuencia de mensajes de texto JSON, que pueden tener cuatro tipos de evento.
El evento connected llega primero y confirma que el WebSocket está activo. El evento start se envía una vez cuando empieza el media stream; lleva un streamSid que debes guardar, ya que lo necesitas para enviar audio de vuelta, y también incluye metadatos de la llamada en start.customParameters y start.callSid.
El evento media es recurrente: media.payload es un fragmento de audio mu-law a 8kHz codificado en base64, 20ms por frame, y media.track es inbound para el audio del llamante. Por último, stop se envía cuando termina el stream, normalmente porque se cuelga la llamada.
Para reproducir audio, envías un mensaje de tipo media con el mismo streamSid y un payload mu-law en base64. Para interrumpir audio ya encolado, envías un mensaje clear con el streamSid, que vacía el buffer de salida de Twilio.
Las codificaciones de entrada y salida son idénticas (ulaw_8000). Pedimos ulaw_8000 a ElevenLabs Texto a Voz y reenviamos los bytes directamente a Twilio sin hacer resampling.
Paso 1: Servir el webhook TwiML
Cuando llega una llamada, Twilio hace una petición HTTP a tu webhook y tú respondes con TwiML que conecta la llamada a tu Media Stream. El verbo <Connect><Stream> abre un WebSocket bidireccional. Usa <Connect> en vez de <Start>: así la llamada sigue activa durante todo el stream y puedes enviar audio de vuelta, que es el objetivo de este sistema.
El TwiML que devuelve el webhook es:
En Express, esto es un único handler POST que rellena el host y devuelve el documento:
En la consola de Twilio, pon el webhook de "A call comes in" del número en https://your-subdomain.ngrok.app/incoming-call usando HTTP POST.
Paso 2: Aceptar el WebSocket del Media Stream
El handler del WebSocket lee los eventos de Twilio, gestiona la cascada y escribe el audio de vuelta.
Mantenemos un pequeño estado por llamada: el streamSid, una conexión STT y una bandera para saber si el agente está hablando. El handler decodifica cada frame media entrante de base64 y reenvía los bytes mu-law en bruto a STT:
Paso 3: Transcribir con Scribe v2 Realtime
Scribe v2 Realtime acepta fragmentos de audio en streaming y devuelve transcripciones parciales y finales, y soporta codificación mu-law directamente, así que le pasamos los frames de Twilio tal cual.
También ofrece Detección de Actividad de Voz para segmentar por silencios y control manual para finalizar un segmento. Para un
Estos son los pasos: abre un stream STT al empezar la llamada. Envía cada fragmento mu-law entrante. Reacciona a las transcripciones finalizadas invocando el LLM.
La interfaz del cliente STT en tiempo real sigue evolucionando, así que el ejemplo de abajo está detrás de un pequeño adaptador TypeScript (openRealtimeStt) que implementas contra la API en vivo en vez de un conjunto fijo de campos. Considera onFinal como el hook que entrega un turno finalizado al siguiente paso.
La latencia de reconocimiento en tiempo real es de unos 150ms para parciales, lo que mantiene pequeño el hueco entre que el llamante termina y el agente empieza. Para la versión batch y todas las funciones, consulta la documentación de Voz a Texto y la página de Voz a Texto en tiempo real del producto.
Paso 4: Generar una respuesta con un LLM
Esta es la etapa que produce la respuesta. El LLM toma el historial de la conversación y devuelve el texto del asistente. Transmite la respuesta para poder empezar la síntesis con la primera frase.
Aquí está respaldado por OpenAI:
El modelo de arriba, gpt-4.1-mini, es un ejemplo de baja latencia; claude-haiku-4-5 es una opción similar en Anthropic. Cualquiera de los dos puede usarse en el mismo contrato llmReply; cambia el cuerpo de la función y el resto del agente sigue igual.
El prompt del sistema limita la longitud de la respuesta, lo cual es importante por teléfono: las respuestas largas parecen lentas y son incómodas de interrumpir.
Paso 5: Sintetizar con Flash TTS en ulaw_8000
Ahora el texto debe convertirse en audio que Twilio pueda reproducir. Pide Flash v2.5 con outputFormat: "ulaw_8000" para que los bytes coincidan con la codificación que espera Twilio, luego transmite el audio y reenvía cada fragmento por el WebSocket como evento media.
Agrupa los tokens del LLM en fragmentos del tamaño de una frase y sintetiza cada uno al completarse, en vez de esperar a la respuesta entera. Así reduces el tiempo hasta el primer audio, porque el llamante oye la primera frase mientras el modelo sigue generando la segunda. Para más control sobre la síntesis incremental, la guía de TTS en tiempo real por WebSocket muestra cómo enviar texto a un único socket de síntesis abierto; el enfoque HTTP streaming de abajo es más simple y suficiente para turnos cortos.
Prepara tu agente de voz IA para producción
Después de estos cinco pasos, tienes un agente funcional. Pero esto no es lo mismo que un despliegue en producción.
Hay varios aspectos que debes tener en cuenta antes de poner el agente en una línea real.
Valida las firmas del webhook de Twilio
Cualquiera que conozca la URL de tu webhook puede hacerle POST, así que lo primero es confirmar que la petición viene realmente de Twilio. Twilio firma cada petición con tu Auth Token en la cabecera X-Twilio-Signature, y debes rechazar todo lo que no pase la validación. La firma se calcula sobre la URL completa y los parámetros POST, así que tienes que calcularla igual que Twilio.
El helper de Twilio lo hace por ti:
Gestiona bien los secretos
Guarda ELEVENLABS_API_KEY, la clave del LLM y TWILIO_AUTH_TOKEN en un gestor de secretos, no en el código fuente ni en archivos env en texto plano subidos al repo. Limita la clave de ElevenLabs solo a las rutas que necesita este servicio y ponle un cupo de crédito para que, si se filtra, el impacto sea limitado.
Los planes Enterprise pueden además restringir una clave a rangos de IP concretos con listas blancas. Este servidor usa la clave de API directamente porque nunca sale de tu backend; si alguna lógica de audio pasara a navegador o móvil, usarías tokens de un solo uso para que la clave nunca se exponga en el cliente.
Entiende el límite de concurrencia
Cada plan tiene un límite de concurrencia que varía según la familia de modelos, y el límite cuenta cuántas peticiones están generando audio al mismo tiempo.
Para un agente telefónico, esto juega a tu favor. La generación de audio es más rápida que la reproducción, así que cada llamada solo consume concurrencia TTS durante los breves momentos en que se sintetiza una respuesta, no durante toda la llamada. Como referencia, un límite de concurrencia de cinco puede soportar unas 100 conversaciones simultáneas, porque la generación termina mucho antes que la reproducción.
Aun así, monitoriza el margen en vez de adivinar. Las respuestas de ElevenLabs incluyen las cabeceras current-concurrent-requests y maximum-concurrent-requests; regístralas y lanza alertas si te acercas al máximo. Si superas el límite, las peticiones se ponen en cola por prioridad, lo que suele añadir unos 50ms, y si la sobrecarga se mantiene, devuelve HTTP 429.
Gestiona las respuestas HTTP 429 con un pequeño backoff. Si persisten, sube los límites desde la página de precios o, si eres cliente Enterprise, a través de tu account manager.
Gestiona barge-in e interrupciones
Un llamante que empieza a hablar mientras el agente está hablando espera que el agente se detenga. Esto es barge-in, y gestionarlo bien es clave para que el agente suene natural y no robótico.
Detecta la voz del llamante durante la reproducción del agente usando la señal VAD de STT. Cuando la detectes, haz dos cosas: primero, deja de enviar fragmentos TTS, que la bandera agentSpeaking en speak ya gestiona cortando el bucle. Segundo, envía a Twilio un mensaje clear para vaciar el audio ya encolado en su lado.
Si te saltas el clear, Twilio sigue reproduciendo el audio en buffer aunque hayas dejado de enviar, así que el agente parece hablar por encima del llamante.
Registra, monitoriza y gestiona errores de forma elegante
Instrumenta cada etapa para poder atribuir la latencia si una llamada parece lenta. Mide el tiempo desde la transcripción final al primer token del LLM, del primer token del LLM al primer byte TTS, y del primer byte TTS al frame enviado a Twilio. Verás que la mayor parte de la latencia variable está en la etapa LLM; las etapas STT y TTS son más estables.
Y planifica para fallos parciales. El LLM puede agotar el tiempo, el stream STT puede caerse y el viaje de red a ElevenLabs varía entre 20 y 200ms según la geografía. Aloja tu servidor cerca de tus llamantes, no solo cerca de ElevenLabs, ya que ElevenLabs ya enruta al clúster más cercano en Norteamérica, Europa o Sudeste Asiático.
Si falla una etapa, no dejes al llamante en silencio: sintetiza una frase corta de respaldo ("Perdona, ¿puedes repetir?") y mantén la llamada activa. Envuelve cada etapa en un timeout y try/catch para que un fallo no cierre todo el WebSocket.
Hay algunos valores por defecto que merece la pena fijar antes de lanzar:
- Limita la longitud de la respuesta en el prompt del sistema, como se muestra, para que los turnos sean cortos e interrumpibles.
- Limita el historial de la conversación para que las llamadas largas no hagan crecer el contexto del LLM sin límite.
- Pon una duración máxima de llamada como protección ante sesiones atascadas que consuman concurrencia.
Para seguir ajustando las partes que sí controlas, el documento de latencia explica de dónde viene el tiempo hasta el primer audio, la descripción de modelos cubre los compromisos entre velocidad y calidad, y la guía de TTS en tiempo real por WebSocket muestra cómo reducir la latencia de síntesis con entrada de texto incremental.
Crea agentes de voz listos para producción con ElevenAPI
Ahora que han pasado 20 minutos, tienes todas las capas de un
Si prefieres no mantener la cascada tú mismo, ElevenAgents ofrece turnos, gestión de interrupciones e integración telefónica como servicio gestionado, usando los mismos modelos que acabas de conectar.
Para seguir ajustando una pila que controlas, explora la página de producto de ElevenAPI para ver planes, límites de concurrencia y la biblioteca de voces. O bien, regístrate y empieza hoy mismo a hacer tu primera llamada.
.webp&w=3840&q=80)
.webp&w=3840&q=80)

.webp&w=3840&q=80)
