Bot de llamadas de Graph
Llama o chatea con tu agente de ElevenLabs por su nombre dentro de Microsoft Teams, como si fuera un compañero.
Descripción general
Este enfoque convierte al agente en una identidad de Teams a la que se puede llamar. Un usuario lo busca por nombre y lo llama de forma individual, y el agente responde en tiempo real, sin número de teléfono, PSTN ni créditos de comunicaciones. Es el único enfoque al que se puede llamar por nombre y el más complejo de ejecutar.
Usa un bot de medios en tiempo real de Microsoft Graph (la plataforma de llamadas de Cloud Communications). El SDK de medios (Microsoft.Skype.Bots.Media) funciona solo con .NET en Windows Server; no hay una opción para Linux ni fuera de .NET para el audio sin procesar en llamadas de Teams.
Este es el único enfoque al que se puede llamar por nombre dentro de Teams. Te recomendamos la pestaña de widget para una configuración más sencilla, o ACS si específicamente quieres un número de teléfono.
Cómo funciona
El bot responde con medios alojados por la aplicación, recibe 50 tramas de audio por segundo (PCM de 16 kHz y 20 ms), las conecta con el agente de ElevenLabs mediante un WebSocket y transmite el audio del agente de vuelta a la llamada.
Requisitos
- Un registro y una aplicación de Azure Bot (registro de aplicación de Entra).
- Permisos de aplicación de Graph con consentimiento de administrador:
Calls.AccessMedia.All(medios sin procesar) yCalls.Initiate.All. - Una VM de Windows Server (≥ 2 núcleos físicos; por ejemplo,
Standard_D4s_v3) con una IP pública y puertos de medios abiertos. - Un certificado TLS firmado por una CA en un FQDN público para el endpoint de medios/señalización (la plataforma de medios rechaza los certificados autofirmados).
- Un agente de ElevenLabs configurado en PCM 16000 Hz en ambos lados: formato de salida de TTS en la pestaña Voz y formato de audio de entrada de usuario en la pestaña Avanzado.
Un D2s_v3 (2 vCPU = 1 núcleo físico) falla con MediaPlatform needs a system with at least 2 cores. Usa un tamaño con ≥ 2 núcleos físicos (por ejemplo, D4s_v3).
Permisos y roles
Paso 1: registrar el bot y los permisos de Graph
Crea un registro de aplicación y un Azure Bot asociado a él; después, concede y autoriza los permisos de llamadas (necesitas ser administrador global o administrador de roles con privilegios para dar el consentimiento):
Concede los dos roles de aplicación de Graph y el consentimiento de administrador (requiere administrador global o administrador de roles con privilegios), y confirma después que se hayan asignado:
Si admin-consent devuelve Consent validation failed, concede los roles de aplicación directamente en la entidad de servicio:
En el portal, verifica en el centro de administración de Entra, en Registros de aplicaciones → tu aplicación → Permisos de API, que ambos permisos muestren Concedido con marcas de verificación verdes.

Paso 2: aprovisionar la VM de Windows, el certificado y los puertos
En la VM (el código nativo de la plataforma de medios los necesita; Windows Server no los incluye de forma predeterminada):
Abre los mismos puertos en el firewall de Windows y anota la huella digital del certificado: el bot vincula Kestrel (443 y un puerto de notificaciones) y la plataforma de medios (8445) a él.
El FQDN *.cloudapp.azure.com de la propia VM funciona con un certificado de Let’s Encrypt; no necesitas un dominio independiente.
Paso 3 — Compila y ejecuta el bot
Empieza con microsoft-graph-comms-samples PublicSamples/EchoBot de Microsoft: utiliza net6.0 y se compila con el SDK de .NET (no necesitas Visual Studio Build Tools):
Configura la sección AppSettings de appsettings.json con tu AadAppId, AadAppSecret, ServiceDnsName/MediaDnsName (el FQDN de la VM), CertificateThumbprint y los puertos (llamadas 443, notificaciones 9441, contenido multimedia 8445). Añade dos ajustes para el puente de ElevenLabs que aparece a continuación: ElevenLabsAgentId y ElevenLabsOrigin (wss://api.elevenlabs.io o tu host de residencia). Ejecútalo como una tarea programada o servicio de Windows para que sobreviva a los reinicios.
El límite de tiempo de ejecución predeterminado (72 horas) del Programador de tareas finaliza silenciosamente las tareas de larga duración: un bot iniciado al arrancar deja de funcionar tres días después y las llamadas fallan con “we couldn’t connect you”. Desactiva el límite y añade el reinicio en caso de error:
El EchoBot predeterminado se bloquea en una llamada realizada al puerto estándar 443: HttpHelpers.SetAbsoluteUri
llama a req.Host.Port.Value, que es nulo cuando la cabecera Host no tiene un puerto explícito. Cámbialo por
req.Host.Port ?? (req.IsHttps ? 443 : 80).
Sustituye el eco por ElevenLabs
La interfaz de audio de EchoBot es sencilla: SpeechService.AppendAudioBuffer(in) y un evento OnSendMediaBufferEventArgs(out). Sustituye su cuerpo de Azure Speech por un puente WebSocket de agentes de ElevenLabs que mantenga la misma interfaz:
Ambos lados utilizan PCM mono de 16 kHz, por lo que es una transferencia directa en base64: configura el agente en pcm_16000. Cuando se produce una interruption de ElevenLabs (interrupción), el puente genera FlushMedia; conéctalo a tu flujo multimedia para que descarte los AudioMediaBuffer en cola; de lo contrario, el agente seguirá hablando sobre la persona que llama. Consulta la referencia completa de mensajes en la documentación de WebSocket. La finalización de la llamada y la transferencia asistida se explican en las secciones siguientes.
La URL de Connect() accede a un agente público. Para un agente privado, solicita en el servidor una URL firmada de corta duración: GET /v1/convai/conversation/get-signed-url?agent_id=... con tu clave de API, y conéctate a la URL devuelta. En la residencia de datos, configura ElevenLabsOrigin con tu host de residencia (wss://api.eu.residency.elevenlabs.io, .in. o .sg.): las solicitudes de URL firmada usan el host https:// correspondiente.
Paso 4 — Haz que se pueda llamar desde Teams
-
Activa Calling en el canal de Teams del Azure Bot y configura el webhook de llamadas como
https://YOUR_FQDN/api/calling:En el portal, se encuentra en tu recurso de Azure Bot → Channels → Microsoft Teams → pestaña Calling:

Azure Bot → Channels — el canal de Microsoft Teams conectado 
Canal de Microsoft Teams → Calling — llamadas activadas con el webhook del bot -
Crea un manifiesto de app de Teams con
bots[0].supportsCalling: truey el ID de app del bot; después, cárgalo de forma local (Apps → Manage your apps → Upload a custom app) o publícalo para toda la organización sin usar la interfaz:New-TeamsApp -DistributionMethod organization -Path ./bot-app.zip(módulo PowerShell de MicrosoftTeams).
Busca la app por nombre en Teams y llámala: el bot responde y el agente de ElevenLabs habla.

No necesitas un número de teléfono ni una cuenta de recurso para las llamadas 1:1 por nombre: solo son necesarios para la marcación PSTN. Calls.AccessMedia.All es lo que activa el puente de audio sin procesar.
Chat de texto (el mismo bot)
El mismo Azure Bot también puede responder texto en Teams, así que los usuarios pueden llamar al agente o chatear con él. Las llamadas y la mensajería son canales independientes en el bot: el webhook de llamadas gestiona la voz y un punto de conexión de mensajería de Bot Framework (/api/messages) gestiona el chat.

Dirige el punto de conexión de mensajería del bot al host que lo sirva (el bot multimedia o cualquier otro servicio; no tiene que ser la VM de Windows):
Implementa el punto de conexión con el SDK de Bot Framework y retransmite cada mensaje al agente en modo texto a través del mismo WebSocket de conversación utilizado para la voz: envía un evento user_message y lee el evento agent_response. Primero, activa el campo first message en los ajustes de overrides del agente: el código siguiente lo sobrescribe con un valor vacío para que la respuesta sea la contestación al mensaje del usuario en lugar del saludo del agente:
Regístralo de la forma habitual (un CloudAdapter, el bot mediante AddTransient<IBot, ChatBot>() y un controlador /api/messages) y añade ámbitos de chat a la entrada del bot en el manifiesto:
El fragmento abre una conversación nueva por mensaje, por lo que cada turno es independiente. Para tener memoria en el chat, mantén un WebSocket abierto por cada conversation.id de Teams (reutilízalo entre turnos) y elimina las sesiones inactivas: así, el agente recordará los mensajes anteriores de ese chat. La anulación de first_message debe estar activada en los ajustes de overrides del agente: el servidor cierra la conversación si se envía una anulación no permitida. Si no puedes activarla, omite la anulación y descarta el primer agent_response de cada sesión (el saludo); después, devuelve el siguiente.
Si las respuestas de chat no llegan, activa el evento de cliente agent_response en los ajustes Advanced del agente: las respuestas de texto se envían mediante ese evento.
Fin de la llamada
Cuando ElevenLabs termina la conversación (su herramienta End Call cierra el WebSocket), cuelga el tramo de Teams:
Transferencia asistida a una persona
El agente activa una herramienta de cliente personalizada transfer_to_human; el bot invita a un usuario de Teams a la llamada en curso (incorporación consultiva) y luego se retira:
La transferencia consultiva (replacesCallId) requiere que ambas partes sean usuarios de Teams del mismo tenant; los destinos de transferencia PSTN requieren una instancia de aplicación. Para informar primero a la persona, pasa un parámetro reason desde el agente y reprodúceselo antes de conectarla.
Resolución de problemas
MediaPlatform necesita un sistema con al menos 2 núcleos
MediaPlatform necesita un sistema con al menos 2 núcleos
La VM solo tiene un núcleo físico. Redimensiónala a ≥ 2 núcleos físicos (por ejemplo, D4s_v3) y reiníciala.
No se puede cargar la DLL 'NativeMedia'
No se puede cargar la DLL 'NativeMedia'
Instala VC++ Redistributable (vcredist140) y la característica de Windows Server-Media-Foundation; después, reinicia el bot.
La llamada entrante devuelve 500 / no se puede conectar la llamada
El error de puerto nulo de EchoBot en 443: corrige HttpHelpers.SetAbsoluteUri (consulta el paso 3). Confirma también que el certificado esté firmado por una CA y sea accesible por el puerto 443.
Al llamar al bot se escucha 'we couldn't connect you'
Confirma que Calling esté activado en el canal de Teams con el webhook /api/calling correcto, que se haya concedido el consentimiento para el permiso de Graph Calls.AccessMedia.All y que los puertos 443/8445/9441 estén abiertos tanto en el NSG como en el firewall de Windows. Si las llamadas antes funcionaban y han dejado de hacerlo, comprueba que el proceso del bot siga ejecutándose en la VM: el límite de ejecución predeterminado de 72 horas del Programador de tareas lo finaliza unos días después del arranque (consulta la advertencia del paso 3).