Azure Communication Services
Permettez aux utilisateurs d’appeler un numéro de téléphone auquel répond votre agent ElevenLabs, via ACS Call Automation.
Présentation
Cette approche attribue un numéro de téléphone à votre agent. Un appelant le compose, Azure Communication Services (ACS) répond avec un streaming multimédia bidirectionnel, puis un petit pont relaie l’audio PCM entre ACS et l’agent ElevenLabs à l’aide du protocole WebSocket d’agent standard. Il s’agit du modèle de centre de contact / IVR, similaire à un déploiement de SIP trunking, avec ACS comme opérateur.
Cette solution se connecte également à Teams de deux façons : un utilisateur Teams disposant d’un Calling Plan peut appeler directement le numéro ACS, ou vous pouvez placer Teams Phone Extensibility devant ce numéro afin que les appels vers un compte de ressource Teams soient acheminés vers ACS.
ACS provisionne des numéros PSTN uniquement dans un ensemble limité de pays. Si aucun numéro n’est disponible dans votre région, utilisez plutôt un fournisseur SIP avec le SIP trunking, ou le bot d’appel Graph.
Fonctionnement
L’audio est en PCM 16 kHz mono sur les deux liaisons, le format d’entrée/sortie de l’agent étant pcm_16000, il est donc transmis en base64 sans rééchantillonnage.
Le pont expose les routes suivantes :
Prérequis
- Un abonnement Azure payant (MCA / EA / Pay-As-You-Go), les abonnements gratuits, d’essai ou sponsorisés ne peuvent pas acheter de numéros.
- Une ressource Azure Communication Services.
- Un hôte HTTPS pour le pont avec un WebSocket public, Azure Container Apps, App Service ou une VM.
- Un agent ElevenLabs configuré en PCM 16000 Hz sur les deux liaisons : format de sortie TTS dans l’onglet Voice, format audio d’entrée utilisateur dans l’onglet Advanced.
Autorisations et rôles
Avec le rôle Contributor, et non Owner, az containerapp up ne peut pas créer l’attribution de rôle
ACR pull pour l’identité gérée. Activez plutôt l’utilisateur administrateur du registre et attachez-le, consultez l’avertissement de l’étape 2.
Étape 1 : provisionnez la ressource ACS et le numéro
Achetez un numéro dans la ressource, Portal → votre ressource ACS → Phone numbers → Get, ou utilisez le SDK phone-numbers. Pour un agent qui répond aux appels, un numéro avec les appels entrants suffit ; ajoutez la capacité sortante si vous souhaitez également utiliser /api/outboundCall.

Pour vérifier depuis la CLI, nécessite az extension add --name communication, et récupérer la chaîne de connexion que le pont utilise comme ACS_CONNECTION_STRING :
Étape 2 : déployez le pont
Le pont est une petite application Flask + flask-sock utilisant azure-communication-callautomation. Voici le cœur du flux entrant :
Sur le socket /ws, relayez PCM16 dans les deux sens : transférez les frames ACS AudioData à ElevenLabs sous la forme {"user_audio_chunk": "<base64>"}, puis renvoyez l’audio de l’agent sous la forme {"Kind":"AudioData","AudioData":{"Data":"<base64>"},"StopAudio":null}. La première frame envoyée par ACS est AudioMetadata, le format négocié, enregistrez-la dans les logs et ignorez-la. Le côté ElevenLabs utilise le protocole WebSocket d’agent standard.
ACS utilise une casse JSON différente dans chaque sens : les frames entrantes qu’il envoie sont en camelCase (kind,
audioData.data), tandis que les frames sortantes qu’il attend sont en PascalCase (Kind, AudioData.Data,
StopAudio). Conservez ces deux casses distinctes, le relais ci-dessous les reproduit.
Ce relais est volontairement minimal. Pour la production, ajoutez la journalisation, la reconnexion et une fermeture propre. La référence complète des messages se trouve dans la documentation WebSocket.
EL_WS se connecte à un agent public. Pour un agent privé, demandez au pont une URL signée de courte durée
côté serveur, GET /v1/convai/conversation/get-signed-url?agent_id=... avec votre clé API, puis connectez-vous plutôt à l’URL renvoyée. Pour la résidence des
données, définissez ELEVENLABS_ORIGIN sur votre
hôte de résidence (wss://api.eu.residency.elevenlabs.io, .in. ou .sg.), les requêtes d’URL signée
utilisent l’hôte https:// correspondant.
Déployez vers Azure Container Apps et récupérez le FQDN public :
Définissez ensuite BRIDGE_PUBLIC_HOST=$FQDN et la chaîne de connexion ACS, comme secret, dans l’application.
Avec le rôle Contributor, et non Owner, az containerapp up ne peut pas créer le rôle ACR
pull pour l’identité gérée. Activez l’utilisateur administrateur du registre (az acr update --admin-enabled true) et attachez-le
avec az containerapp registry set, puis utilisez az containerapp update --image ....
Étape 3 : acheminez IncomingCall vers le pont
Créez un abonnement Event Grid sur la ressource ACS qui publie IncomingCall vers le pont. La négociation de validation du pont, ci-dessus, finalise automatiquement l’abonnement.
L’abonnement apparaît dans le panneau Events de la ressource ACS :

Composez le numéro, l’agent répond.
Connexion à Teams
- Appel direct : un utilisateur Teams avec Teams Phone + un Calling Plan peut appeler le numéro ACS comme n’importe quel numéro externe.
- Compte de ressource Teams (TPE) : liez un compte de ressource Teams à la ressource ACS avec Teams Phone Extensibility afin que les appels vers le compte de ressource déclenchent le même flux
IncomingCall→ pont.
Fin d’appel
Lorsque l’agent termine la conversation, par exemple avec son outil End Call, ElevenLabs ferme le WebSocket. Raccrochez la liaison ACS afin que l’appelant ne reste pas sur une ligne inactive :
Transfert assisté vers un humain
Les outils de transfert natifs d’ElevenLabs s’appliquent uniquement lorsqu’ElevenLabs gère la téléphonie. Ici, l’agent déclenche donc un outil client personnalisé, par exemple transfer_to_human, que votre pont gère en ajoutant la personne à l’appel en cours avec add_participant, transfert assisté, plutôt qu’en effectuant un transfert aveugle :
ACS envoie les callbacks AddParticipantSucceeded / AddParticipantFailed à /api/callbacks. Renvoyez un client_tool_result à l’agent afin qu’il puisse prononcer sa phrase de transfert. Consultez les outils système pour la configuration côté agent.
Définissez la protection de transfert dès le déclenchement de l’outil, avant d’appeler add_participant, sinon une fermeture rapide du
WebSocket EL peut entrer en concurrence avec le raccrochage et interrompre l’appel avant que la personne ne rejoigne la conversation.
Résolution des problèmes
Aucun IncomingCall n'atteint le pont
Vérifiez que l’abonnement Event Grid a été provisionné (provisioningState: Succeeded) et que le
/api/incomingCall du pont a renvoyé l’écho de validation. Vérifiez que le numéro accepte les appels entrants
et appartient à la même ressource ACS que l’abonnement. Dans l’onglet Filters de l’abonnement,
les types d’événements doivent inclure Incoming Call :

CreateCallFailed / AddParticipantFailed pour un numéro international
CreateCallFailed / AddParticipantFailed pour un numéro international
Les appels sortants ACS vers certaines destinations, par exemple l’Inde, sont restreints ou intermittents. Utilisez une destination prise en charge, ou faites passer la liaison humaine par un numéro SIP/Operator. La logique du pont n’est pas affectée, il s’agit d’un échec au niveau de l’opérateur sur la liaison sortante.
L'audio est déformé ou la vitesse est incorrecte
Les deux côtés doivent être en PCM 16 kHz mono. Configurez le format d’entrée/sortie de l’agent sur pcm_16000 ; le
pont enregistre dans les logs le format négocié depuis conversation_initiation_metadata.
Impossible d'acheter un numéro / numéro indisponible dans mon pays
L’achat de numéros nécessite un type d’abonnement payant, MCA/EA/PAYG. Si ACS ne propose pas de numéros dans votre pays, utilisez plutôt un fournisseur SIP.