Azure Communication Services
Panoramica
Questo approccio assegna al tuo agente un numero di telefono. Un chiamante lo compone, Azure Communication Services (ACS) risponde con streaming multimediale bidirezionale e un piccolo bridge inoltra l’audio PCM tra ACS e l’agente ElevenLabs usando il protocollo WebSocket dell’agente standard. È il modello di contact center/IVR, analogo a un’implementazione con SIP trunking, con ACS come operatore.
Si connette inoltre a Teams in due modi: un utente Teams con un Calling Plan può chiamare direttamente il numero ACS, oppure puoi esporre il numero tramite Teams Phone Extensibility affinché le chiamate a un account risorsa Teams vengano instradate ad ACS.
ACS fornisce numeri PSTN solo in un numero limitato di paesi. Se nella tua area geografica non è disponibile un numero, usa invece un provider SIP con SIP trunking, oppure il bot di chiamata Graph.
Come funziona
L’audio è PCM 16 kHz mono su entrambi i lati (il formato di input/output dell’agente è pcm_16000), quindi viene trasferito come base64 senza ricampionamento.
Il bridge espone queste route:
Requisiti
- Una sottoscrizione Azure a pagamento (MCA / EA / Pay-As-You-Go): le sottoscrizioni gratuite, di prova o sponsorizzate non possono acquistare numeri.
- Una risorsa Azure Communication Services.
- Un host HTTPS per il bridge con un WebSocket pubblico (Azure Container Apps, App Service o una VM).
- Un agente ElevenLabs impostato su PCM 16000 Hz su entrambi i lati: formato di output TTS nella scheda Voce, formato audio di input dell’utente nella scheda Avanzate.
Autorizzazioni e ruoli
Con il ruolo Contributor (non Owner), az containerapp up non può creare l’assegnazione del
ruolo ACR pull per l’identità gestita. Abilita invece l’utente amministratore del registro e collegalo: vedi l’avviso nel passaggio 2.
Passaggio 1 — Esegui il provisioning della risorsa ACS e del numero
Acquista un numero nella risorsa (Portale → risorsa ACS → Numeri di telefono → Ottieni, oppure usa l’SDK phone-numbers). Per un agente che risponde alle chiamate, è sufficiente un numero con chiamate in entrata; aggiungi la funzionalità in uscita se vuoi usare anche /api/outboundCall.

Per verificare dalla CLI (richiede az extension add --name communication) e recuperare la stringa di connessione che il bridge usa come ACS_CONNECTION_STRING:
Passaggio 2 — Distribuisci il bridge
Il bridge è una piccola app Flask + flask-sock che usa azure-communication-callautomation. Ecco il nucleo del flusso in entrata:
Nel socket /ws, inoltra PCM16 in entrambe le direzioni: invia i frame ACS AudioData a ElevenLabs come {"user_audio_chunk": "<base64>"} e rimanda l’audio dell’agente come {"Kind":"AudioData","AudioData":{"Data":"<base64>"},"StopAudio":null}. Il primo frame inviato da ACS è AudioMetadata (il formato negoziato): registralo e ignoralo. Il lato ElevenLabs usa il protocollo WebSocket dell’agente standard.
ACS usa maiuscole/minuscole JSON diverse per ciascuna direzione: i frame in entrata che invia sono in camelCase (kind,
audioData.data), mentre i frame in uscita che si aspetta sono in PascalCase (Kind, AudioData.Data,
StopAudio). Mantieni distinti i due formati: il relay seguente rispecchia questa differenza.
Questo relay è volutamente essenziale. Per la produzione, aggiungi logging, riconnessione e chiusura pulita. Il riferimento completo dei messaggi è nella documentazione WebSocket.
EL_WS si connette a un agente pubblico. Per un agente privato, fai in modo che il bridge richieda lato server un URL firmato di breve durata:
GET /v1/convai/conversation/get-signed-url?agent_id=... con la tua chiave API, quindi connettiti all’URL restituito. Per la residenza dei dati, imposta ELEVENLABS_ORIGIN sull’host della tua area di residenza (wss://api.eu.residency.elevenlabs.io, .in. o .sg.): le richieste di URL firmati usano l’host https:// corrispondente.
Distribuisci su Azure Container Apps e acquisisci l’FQDN pubblico:
Quindi imposta BRIDGE_PUBLIC_HOST=$FQDN e la stringa di connessione ACS (come secret) nell’app.
Con il ruolo Contributor (non Owner), az containerapp up non può creare il ruolo ACR pull
dell’identità gestita. Abilita l’utente amministratore del registro (az acr update --admin-enabled true) e collegalo con az containerapp registry set, quindi esegui az containerapp update --image ....
Passaggio 3 — Instrada IncomingCall al bridge
Crea una sottoscrizione Event Grid sulla risorsa ACS che invii IncomingCall al bridge. L’handshake di convalida del bridge (sopra) completa automaticamente la sottoscrizione.
La sottoscrizione appare nel pannello Events della risorsa ACS:

Chiama il numero: risponderà l’agente.
Collegamento a Teams
- Chiamata diretta: un utente Teams con Teams Phone + un Calling Plan può chiamare il numero ACS come qualsiasi numero esterno.
- Account risorsa Teams (TPE): collega un account risorsa Teams alla risorsa ACS con Teams Phone Extensibility, affinché le chiamate all’account risorsa attivino lo stesso flusso
IncomingCall→ bridge.
Fine della chiamata
Quando l’agente termina la conversazione (ad esempio con lo strumento End Call), ElevenLabs chiude il WebSocket. Riaggancia il lato ACS affinché il chiamante non resti su una linea inattiva:
Trasferimento assistito a un operatore
Gli strumenti di trasferimento nativi di ElevenLabs si applicano solo quando ElevenLabs gestisce la telefonia. In questo caso, quindi, l’agente attiva uno strumento client personalizzato (ad esempio transfer_to_human) che il bridge gestisce aggiungendo l’operatore alla chiamata in corso con add_participant (trasferimento assistito), anziché effettuare un trasferimento cieco:
ACS invia i callback AddParticipantSucceeded / AddParticipantFailed a /api/callbacks. Restituisci un client_tool_result all’agente, così potrà pronunciare il messaggio di passaggio. Consulta gli strumenti di sistema per la configurazione lato agente.
Imposta la protezione del trasferimento non appena si attiva lo strumento (prima di chiamare add_participant), altrimenti una rapida chiusura del WebSocket EL può entrare in conflitto con il riaggancio e interrompere la chiamata prima che l’operatore si unisca.
Risoluzione dei problemi
Nessun IncomingCall raggiunge il bridge
Verifica che sia stato effettuato il provisioning della sottoscrizione Event Grid (provisioningState: Succeeded) e che /api/incomingCall del bridge abbia restituito l’eco di convalida. Verifica che il numero supporti le chiamate in entrata e appartenga alla stessa risorsa ACS su cui è presente la sottoscrizione. Nella scheda Filters della sottoscrizione, i tipi di evento devono includere Incoming Call:

CreateCallFailed / AddParticipantFailed per un numero internazionale
CreateCallFailed / AddParticipantFailed per un numero internazionale
Le chiamate in uscita ACS verso alcune destinazioni (ad esempio l’India) sono limitate o intermittenti. Usa una destinazione supportata oppure anteponi al lato dell’operatore un numero SIP/Operator. La logica del bridge non cambia: si tratta di un errore a livello di operatore sul lato della chiamata in uscita.
L'audio è distorto o ha una velocità errata
Entrambi i lati devono usare PCM 16 kHz mono. Imposta il formato di input/output dell’agente su pcm_16000; il bridge registra il formato negoziato da conversation_initiation_metadata.
Non riesco ad acquistare un numero / il numero non è disponibile nel mio Paese
L’acquisto di numeri richiede un tipo di sottoscrizione a pagamento (MCA/EA/PAYG). Se ACS non offre numeri nel tuo Paese, usa invece un provider SIP.