Graph calling-bot
Bel of chat met je ElevenLabs-agent op naam in Microsoft Teams, net als met een collega.
Overzicht
Met deze aanpak wordt de agent een belbare Teams-identiteit. Een gebruiker zoekt deze op naam en belt deze 1-op-1, waarna de agent realtime antwoordt — zonder telefoonnummer, PSTN of Communications Credits. Dit is de enige aanpak waarbij je op naam kunt bellen, en ook de meest complexe om te draaien.
Deze gebruikt een Microsoft Graph-realtime-mediabot (het Cloud Communications-belplatform). De media-SDK (Microsoft.Skype.Bots.Media) werkt alleen met .NET op Windows Server — er is geen Linux- of niet-.NET-optie voor ruwe audio in Teams-gesprekken.
Dit is de enige aanpak die je op naam kunt bellen in Teams. Kies de widget- tab voor een eenvoudigere configuratie, of ACS als je specifiek een telefoonnummer wilt.
Hoe het werkt
De bot beantwoordt gesprekken met media die door de applicatie wordt gehost, ontvangt 50 audioframes/sec (20 ms PCM 16 kHz), stuurt deze via een WebSocket door naar de ElevenLabs-agent en streamt de audio van de agent terug naar het gesprek.
Vereisten
- Een Azure Bot-registratie + app (Entra-appregistratie).
- Graph-applicatiemachtigingen met toestemming van de beheerder:
Calls.AccessMedia.All(ruwe media) plusCalls.Initiate.All. - Een Windows Server-VM (≥ 2 fysieke cores — bijvoorbeeld
Standard_D4s_v3) met een openbaar IP-adres en geopende mediapoorten. - Een door een CA ondertekend TLS-certificaat op een openbare FQDN voor het media-/signaling-endpoint (het mediaplatform weigert zelfondertekende certificaten).
- Een ElevenLabs-agent die op beide kanten is ingesteld op PCM 16000 Hz: TTS-uitvoerformaat op het tabblad Voice, invoeraudioformaat van de gebruiker op het tabblad Advanced.
Een D2s_v3 (2 vCPU = 1 fysieke core) faalt met MediaPlatform needs a system with at least 2 cores. Gebruik een formaat met ≥ 2 fysieke cores (bijvoorbeeld D4s_v3).
Machtigingen en rollen
Stap 1 — Registreer de bot + Graph-machtigingen
Maak een appregistratie en een daaraan gekoppelde Azure Bot, en verleen vervolgens toestemming voor de belmachtigingen (je hebt Global Admin / Privileged Role Admin nodig om toestemming te geven):
Verleen de twee Graph-applicatierollen en beheerderstoestemming (vereist Global Admin / Privileged Role Admin), en controleer vervolgens of de toewijzingen zijn doorgevoerd:
Als admin-consent Consent validation failed retourneert, verleen de approllen dan rechtstreeks aan de service-principal:
Controleer in de portal in het Entra-beheercentrum onder App registrations → je app → API permissions: beide machtigingen moeten met groene vinkjes als Granted worden weergegeven.

Stap 2 — Richt de Windows-VM, het certificaat en de poorten in
Op de VM (de native code van het mediaplatform heeft deze nodig — Windows Server bevat ze standaard niet):
Open dezelfde poorten in de Windows Firewall en noteer de thumbprint van het certificaat — de bot koppelt Kestrel (443 + een meldingspoort) en het mediaplatform (8445) hieraan.
De eigen *.cloudapp.azure.com-FQDN van de VM werkt voor een Let’s Encrypt-certificaat — een apart domein
is niet nodig.
Stap 3 — Bouw en voer de bot uit
Begin met Microsoft’s microsoft-graph-comms-samples PublicSamples/EchoBot — deze is gericht op net6.0 en bouwt met de .NET SDK (geen Visual Studio Build Tools nodig):
Configureer de sectie AppSettings van appsettings.json met je AadAppId, AadAppSecret, ServiceDnsName/MediaDnsName (de FQDN van de VM), CertificateThumbprint en poorten (bellen 443, meldingen 9441, media 8445). Voeg voor de ElevenLabs-bridge hieronder twee instellingen toe: ElevenLabsAgentId en ElevenLabsOrigin (wss://api.elevenlabs.io, of je residency-host). Voer deze uit als een Windows-geplande taak / service, zodat deze na herstarts blijft draaien.
De standaard uitvoertijdlimiet (72 uur) van Task Scheduler beëindigt langlopende taken stilzwijgend — een bot die bij het opstarten wordt gestart, stopt drie dagen later en gesprekken mislukken met “we couldn’t connect you”. Schakel de limiet uit en voeg opnieuw starten bij een fout toe:
De standaard EchoBot crasht bij een gesprek naar de standaardpoort 443: HttpHelpers.SetAbsoluteUri
roept req.Host.Port.Value aan, wat null is als de Host-header geen expliciete poort heeft. Pas dit aan naar
req.Host.Port ?? (req.IsHttps ? 443 : 80).
Vervang de echo door ElevenLabs
De audionaad van EchoBot is helder: SpeechService.AppendAudioBuffer(in) en een event OnSendMediaBufferEventArgs(out). Vervang de Azure-Speech-body door een ElevenLabs-agent-WebSocket-bridge die hetzelfde oppervlak behoudt:
Beide kanten gebruiken mono PCM 16 kHz, dus het is een base64-doorvoer — stel de agent in op pcm_16000. Bij een ElevenLabs-interruption (barge-in) activeert de bridge FlushMedia; koppel dit aan je mediastream zodat alle wachtrijstaande AudioMediaBuffers worden verwijderd, anders blijft de agent over de beller heen praten. De volledige berichtreferentie staat in de WebSocket-documentatie. Het ophangen aan het einde van een gesprek en warme overdracht worden in de onderstaande secties behandeld.
De URL in Connect() bereikt een openbare agent. Vraag voor een privéagent server-side een kortdurende
ondertekende URL aan — GET /v1/convai/conversation/get-signed-url?agent_id=... met je API-
sleutel — en maak in plaats daarvan verbinding met de geretourneerde URL. Stel bij data-
residency ElevenLabsOrigin in op je residency-
host (wss://api.eu.residency.elevenlabs.io, .in., of .sg.) — aanvragen voor ondertekende URL’s gebruiken de
bijbehorende https://-host.
Stap 4 — Maak de bot belbaar in Teams
-
Schakel Calling in op het Teams-kanaal van de Azure Bot en stel de calling webhook in op
https://YOUR_FQDN/api/calling:In de portal vind je dit bij je Azure Bot-resource → Channels → Microsoft Teams → tabblad Calling:

Azure Bot → Channels — het verbonden Microsoft Teams-kanaal 
Microsoft Teams-kanaal → Calling — bellen ingeschakeld met de webhook van de bot -
Bouw een Teams-appmanifest met
bots[0].supportsCalling: trueen de app-ID van de bot, en sideload dit (Apps → Manage your apps → Upload a custom app), of publiceer het organisatiebreed zonder de UI:New-TeamsApp -DistributionMethod organization -Path ./bot-app.zip(MicrosoftTeams PowerShell-module).
Zoek de app op naam in Teams en bel deze — de bot antwoordt en de ElevenLabs-agent spreekt.

Voor 1-op-1-bellen op naam heb je geen telefoonnummer of resource account nodig — die zijn alleen voor PSTN-
inbellen. Calls.AccessMedia.All maakt de raw-audiobridge mogelijk.
Tekstchat (dezelfde bot)
Dezelfde Azure Bot kan ook tekst beantwoorden in Teams — gebruikers kunnen de agent dus bellen of ermee chatten. Bellen en berichten versturen zijn onafhankelijke kanalen op de bot: de calling webhook verwerkt spraak en een Bot Framework-messaging endpoint (/api/messages) verwerkt chat.

Wijs het messaging endpoint van de bot naar de host die dit aanbiedt (de mediabot of een andere service — dit hoeft niet de Windows-VM te zijn):
Implementeer het endpoint met de Bot Framework SDK en stuur elk bericht in tekstmodus door naar de agent via dezelfde conversation WebSocket die voor spraak wordt gebruikt — verstuur een event user_message en lees het event agent_response. Schakel eerst het veld first message in onder de instellingen voor overrides van de agent — de onderstaande code overschrijft dit met leeg, zodat het antwoord een reactie is op het bericht van de gebruiker in plaats van de begroeting van de agent:
Registreer deze op de standaardmanier (een CloudAdapter, de bot via AddTransient<IBot, ChatBot>() en een controller voor /api/messages) en voeg chatscopes toe aan de botvermelding in het manifest:
Het codefragment opent een nieuw gesprek per bericht, dus elke beurt staat op zichzelf. Houd voor chat-
geheugen één WebSocket open per Teams-conversation.id (hergebruik deze tussen beurten) en ruim
inactieve sessies op — de agent onthoudt dan eerdere berichten in die chat. De override first_message
moet zijn ingeschakeld onder de instellingen voor
overrides van de agent — de server
sluit het gesprek als een niet-toegestane override wordt verstuurd. Als je deze niet kunt inschakelen, laat je de
override weg en negeer je in plaats daarvan de eerste agent_response van elke sessie (de begroeting), waarna je
de volgende terugstuurt.
Als chatantwoorden nooit aankomen, schakel dan het agent_response-client-
event in de Advanced-
instellingen van de agent in — tekstreacties worden via dat event geleverd.
Einde van het gesprek
Wanneer ElevenLabs het gesprek beëindigt (de tool End Call sluit de WebSocket), hang je de Teams-kant op:
Warme overdracht naar een persoon
De agent activeert een aangepaste clienttool transfer_to_human; de bot nodigt een Teams-gebruiker uit voor het actieve gesprek (consultatieve toevoeging) en trekt zich vervolgens terug:
Consultatieve overdracht (replacesCallId) vereist dat beide partijen Teams-gebruikers in dezelfde
tenant zijn; PSTN-overdrachtsdoelen vereisen een application instance. Geef de persoon eerst een briefing door een
parameter reason van de agent door te geven en deze aan de persoon af te spelen voordat je de verbinding maakt.
Problemen oplossen
MediaPlatform needs a system with at least 2 cores
MediaPlatform needs a system with at least 2 cores
De VM heeft slechts één fysieke core. Wijzig de grootte naar ≥ 2 fysieke cores (bijvoorbeeld D4s_v3) en start opnieuw op.
Unable to load DLL 'NativeMedia'
Unable to load DLL 'NativeMedia'
Installeer de VC++ Redistributable (vcredist140) en de Windows-
functie Server-Media-Foundation, en start de bot vervolgens opnieuw.
Inkomend gesprek retourneert 500 / gesprek maakt geen verbinding
De EchoBot-poort-null-bug op 443 — pas HttpHelpers.SetAbsoluteUri aan (zie stap 3). Controleer ook of
het certificaat door een CA is ondertekend en bereikbaar is op 443.
Bij het bellen van de bot wordt 'we couldn't connect you' weergegeven
Controleer of Calling is ingeschakeld op het Teams-kanaal met de juiste /api/calling-webhook, of de
Graph-machtiging Calls.AccessMedia.All toestemming heeft en of poorten 443/8445/9441 zowel in de
NSG als in de Windows Firewall openstaan. Als bellen eerder werkte en is gestopt, controleer dan of het botproces
nog steeds op de VM draait — de standaarduitvoertijdlimiet van 72 uur van Task Scheduler beëindigt het enkele
dagen na het opstarten (zie de waarschuwing in stap 3).