> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Graph-Anrufbot

## Überblick

Dieser Ansatz macht den Agenten zu einer **anrufbaren Teams-Identität**. Ein Nutzer sucht ihn per Namen und ruft ihn im 1:1-Gespräch an. Der Agent antwortet in Echtzeit – ohne Telefonnummer, PSTN oder Communications Credits. Dies ist der einzige Ansatz, bei dem ein Anruf per Namen möglich ist, und der aufwendigste im Betrieb.

Er nutzt einen **Microsoft Graph-Echtzeit-Medienbot** (die Cloud Communications Calling-Plattform). Das Media SDK (`Microsoft.Skype.Bots.Media`) unterstützt **nur .NET auf Windows Server** – für Roh-Audio in Teams-Anrufen gibt es keinen Linux- oder Nicht-.NET-Weg.

> **Note**
>
> Dies ist der einzige Ansatz, der in Teams **per Namen** anrufbar ist. Für eine einfachere Einrichtung
> verwenden Sie den [Widget- Tab](/docs/de/eleven-agents/phone-numbers/microsoft-teams/widget-tab) oder
> [ACS](/docs/de/eleven-agents/phone-numbers/microsoft-teams/azure-communication-services), wenn Sie
> ausdrücklich eine Telefonnummer benötigen.

## Funktionsweise

![Ein Teams-Nutzer ruft den Bot per Namen an; Teams leitet den Anruf an den Medienbot auf einer Windows-VM weiter, der Roh-Audio im PCM-16k-Format über einen WebSocket mit dem ElevenLabs-Agenten verbindet](/docs/_fern-files/elevenlabs.docs.buildwithfern.com/f411ec0b44d4c5ca1a61598fbd1e6162e46a1c74179eebb4b673ee1428823e84/assets/images/conversational-ai/teams-media-bot-architecture.svg)

Der Bot antwortet mit **anwendungsgehosteten Medien**, empfängt 50 Audio-Frames pro Sekunde (20 ms PCM mit 16 kHz), verbindet sie über einen WebSocket mit dem ElevenLabs-Agenten und streamt das Audio des Agenten zurück in den Anruf.

## Anforderungen

1. Eine **Azure Bot**-Registrierung + App (Entra-App-Registrierung).
2. **Graph-Anwendungsberechtigungen** mit Admin-Einwilligung: `Calls.AccessMedia.All` (Rohmedien) plus `Calls.Initiate.All`.
3. Eine **Windows Server-VM** (≥ **2 physische Kerne** – z. B. `Standard_D4s_v3`) mit öffentlicher IP und offenen Medienports.
4. Ein **von einer CA signiertes TLS-Zertifikat** auf einer öffentlichen FQDN für den Medien-/Signalisierungsendpunkt (die Medienplattform lehnt selbstsignierte Zertifikate ab).
5. Ein [ElevenLabs-Agent](/docs/de/eleven-agents/quickstart), der auf beiden Seiten auf **PCM 16000 Hz** eingestellt ist: TTS-Ausgabeformat im Tab **Voice**, Audioformat für Nutzereingaben im Tab **Advanced**.

> **Warning**
>
> Eine `D2s_v3` (2 vCPU = **1 physischer Kern**) schlägt mit `MediaPlatform needs a system with at least 2
>   cores` fehl. Verwenden Sie eine Größe mit ≥ 2 physischen Kernen (z. B. `D4s_v3`).

## Berechtigungen und Rollen

| Bereich                     | Rolle / Berechtigung                                                                                                                              | Warum                                                                                                               |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Entra                       | **Anwendungsadministrator**                                                                                                                       | App-Registrierung + Azure Bot erstellen                                                                             |
| Entra                       | **Globaler Administrator / Administrator für privilegierte Rollen**                                                                               | Admin-Einwilligung für Graph-Anrufberechtigungen erteilen – App-Berechtigungen können nicht selbst genehmigt werden |
| Microsoft Graph (Anwendung) | `Calls.AccessMedia.All`, `Calls.Initiate.All`                                                                                                     | 1:1-Anrufe annehmen und auf Rohmedien zugreifen                                                                     |
| Azure RBAC                  | **Mitwirkender** für die Ressourcengruppe                                                                                                         | Windows-VM + Azure Bot erstellen                                                                                    |
| Teams-Administrator         | [**Upload benutzerdefinierter Apps**](https://admin.teams.microsoft.com/policies/manage-apps) erlauben; den **Calling**-Kanal des Bots aktivieren | App querladen und Anrufe empfangen                                                                                  |

## Schritt 1 – Bot + Graph-Berechtigungen registrieren

Erstellen Sie eine App-Registrierung und einen daran gebundenen Azure Bot. Erteilen und genehmigen Sie anschließend die Anrufberechtigungen (für die Einwilligung benötigen Sie Global Admin / Privileged Role Admin):

```bash
APPID=$(az ad app create --display-name "ElevenLabs Teams Agent" \
  --sign-in-audience AzureADMyOrg --query appId -o tsv)
az ad sp create --id "$APPID"
# create a client secret and record it
az ad app credential reset --id "$APPID" --display-name bot --query password -o tsv

# Azure Bot bound to the app
az bot create --resource-group $RG --name el-teams-agent-bot \
  --app-type SingleTenant --appid "$APPID" --tenant-id $TENANT \
  --endpoint "https://YOUR_FQDN/api/messages" --sku S1
```

Erteilen Sie die beiden Graph-Anwendungsrollen und die **Admin-Einwilligung** (erfordert Global Admin / Privileged Role Admin). Prüfen Sie anschließend, ob die Zuweisungen erfolgt sind:

```bash
# Graph app roles: Calls.AccessMedia.All, Calls.Initiate.All
az ad app permission add --id "$APPID" --api 00000003-0000-0000-c000-000000000000 \
  --api-permissions a7a681dc-756e-4909-b988-f160edc6655f=Role \
    284383ee-7f6e-4e40-a2a8-e85dcb029101=Role
az ad app permission admin-consent --id "$APPID"

# Verify — should print both role ids
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$APPID')/appRoleAssignments" \
  --query "value[].appRoleId" -o tsv
```

> **Note**
>
> Wenn `admin-consent` den Fehler `Consent validation failed` zurückgibt, erteilen Sie die App-Rollen stattdessen direkt für den Dienstprinzipal:
>
> ```bash
> GRAPH_SP=$(az ad sp show --id 00000003-0000-0000-c000-000000000000 --query id -o tsv)
> BOT_SP=$(az ad sp show --id "$APPID" --query id -o tsv)
> for ROLE in a7a681dc-756e-4909-b988-f160edc6655f 284383ee-7f6e-4e40-a2a8-e85dcb029101; do
>   az rest --method POST \
>     --url "https://graph.microsoft.com/v1.0/servicePrincipals/$GRAPH_SP/appRoleAssignedTo" \
>     --body "{\"principalId\": \"$BOT_SP\", \"resourceId\": \"$GRAPH_SP\", \"appRoleId\": \"$ROLE\"}"
> done
> ```

Prüfen Sie im Portal im [Entra Admin Center](https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/RegisteredAppsListBlade) unter **App registrations → Ihre App → API permissions**: Beide Berechtigungen sollten mit grünen Häkchen als **Granted** angezeigt werden.

![Die API-Berechtigungsansicht der App-Registrierung mit Calls.AccessMedia.All und Calls.Initiate.All
als erteilt](/docs/_fern-img/4abc1e220d3fc03845594aa4f835656425cc1c39448dae30dace98e672fd8fb1.webp)

## Schritt 2 – Windows-VM, Zertifikat und Ports bereitstellen

```bash
az vm create -g $RG -n teams-media-bot --image Win2022Datacenter \
  --size Standard_D4s_v3 --admin-username azureuser --admin-password '<strong-pw>' \
  --public-ip-sku Standard --public-ip-address-dns-name elevenmediabot
az vm open-port -g $RG -n teams-media-bot --port 80,443,8445,9441 --priority 300
```

Auf der VM (der native Code der Medienplattform benötigt diese Komponenten – Windows Server enthält sie standardmäßig nicht):

```powershell
# VC++ runtime + Media Foundation feature (required by NativeMedia.dll)
choco install -y vcredist140
Install-WindowsFeature Server-Media-Foundation

# CA cert for the VM's FQDN via win-acme (HTTP-01), then import to LocalMachine\My
& wacs.exe --target manual --host <vm-fqdn>.cloudapp.azure.com `
  --validation selfhosting --store pfxfile --pfxfilepath C:\bot\certs --accepttos
```

Öffnen Sie dieselben Ports in der **Windows-Firewall** und notieren Sie sich den **Fingerabdruck** des Zertifikats. Der Bot bindet Kestrel (443 + einen Benachrichtigungsport) und die Medienplattform (8445) daran.

> **Tip**
>
> Die eigene `*.cloudapp.azure.com`-FQDN der VM funktioniert für ein Let's-Encrypt-Zertifikat – eine
> separate Domain ist nicht erforderlich.

## Schritt 3 – Bot erstellen und ausführen

Starten Sie mit Microsofts [`microsoft-graph-comms-samples`](https://github.com/microsoftgraph/microsoft-graph-comms-samples) **PublicSamples/EchoBot**. Es zielt auf `net6.0` und wird mit dem .NET SDK erstellt (keine Visual Studio Build Tools erforderlich):

```powershell
git clone --depth 1 https://github.com/microsoftgraph/microsoft-graph-comms-samples.git C:\bot\samples
cd C:\bot\samples\Samples\PublicSamples\EchoBot\src
dotnet build EchoBot.sln -c Release
```

Konfigurieren Sie den Abschnitt `AppSettings` in `appsettings.json` mit Ihrer `AadAppId`, Ihrem `AadAppSecret`, `ServiceDnsName`/`MediaDnsName` (der FQDN der VM), `CertificateThumbprint` und den Ports (Anrufe `443`, Benachrichtigungen `9441`, Medien `8445`). Fügen Sie für die folgende ElevenLabs-Bridge zwei Einstellungen hinzu: `ElevenLabsAgentId` und `ElevenLabsOrigin` (`wss://api.elevenlabs.io` oder Ihren Residency-Host). Führen Sie sie als geplante Windows-Aufgabe bzw. Dienst aus, damit sie Neustarts übersteht.

> **Warning**
>
> Das standardmäßige **Ausführungszeitlimit (72 Stunden)** der Aufgabenplanung beendet langlaufende Aufgaben unbemerkt. Ein beim Start gestarteter Bot fällt drei Tage später aus und Anrufe schlagen mit „we couldn't connect you“ fehl. Deaktivieren Sie das Limit und fügen Sie einen Neustart bei Fehlern hinzu:
>
> ```powershell
> $s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Seconds 0) `
>   -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) -StartWhenAvailable
> Set-ScheduledTask -TaskName EchoBot -Settings $s
> ```

> **Warning**
>
> Der Standard-EchoBot stürzt bei einem Anruf über den Standardport 443 ab: `HttpHelpers.SetAbsoluteUri`
> ruft `req.Host.Port.Value` auf, was null ist, wenn der Host-Header keinen expliziten Port enthält.
> Ändern Sie dies in `req.Host.Port ?? (req.IsHttps ? 443 : 80)`.

### Echo durch ElevenLabs ersetzen

Die Audioschnittstelle von EchoBot ist klar: `SpeechService.AppendAudioBuffer(in)` und ein `OnSendMediaBufferEventArgs(out)`-Ereignis. Ersetzen Sie den Azure-Speech-Body durch eine ElevenLabs-Agent-WebSocket-Bridge mit derselben Oberfläche:

**`SpeechService.cs – ElevenLabs-Bridge (Kern)`**

```csharp title="SpeechService.cs – ElevenLabs-Bridge (Kern)"
public class SpeechService
{
    private readonly AppSettings _settings;
    private readonly ILogger _logger;
    private ClientWebSocket _ws;
    private bool _started;
    private bool _connecting;

    public event EventHandler<MediaStreamEventArgs> SendMediaBuffer; // agent audio -> call
    public event EventHandler FlushMedia;                            // barge-in: drop queued audio

    public SpeechService(AppSettings settings, ILogger logger) { _settings = settings; _logger = logger; }

    // Caller audio -> ElevenLabs
    public async Task AppendAudioBuffer(AudioMediaBuffer buffer)
    {
        if (!_started)
        {
            if (_connecting) return;            // a connect attempt is already in flight
            _connecting = true;
            try { await Connect(); _started = true; }
            catch (Exception ex) { _logger.Error(ex, "ElevenLabs connect failed; retry on next frame"); return; }
            finally { _connecting = false; }
        }
        if (_ws?.State != WebSocketState.Open || buffer.Length <= 0) return;
        var pcm = new byte[buffer.Length];
        Marshal.Copy(buffer.Data, pcm, 0, (int)buffer.Length);
        var msg = JsonSerializer.Serialize(new { user_audio_chunk = Convert.ToBase64String(pcm) });
        await _ws.SendAsync(Encoding.UTF8.GetBytes(msg), WebSocketMessageType.Text, true, default);
    }

    private async Task Connect()
    {
        _ws = new ClientWebSocket();
        // ElevenLabsOrigin: wss://api.elevenlabs.io, or a residency host (.eu./.in./.sg.)
        var url = $"{_settings.ElevenLabsOrigin}/v1/convai/conversation?agent_id={_settings.ElevenLabsAgentId}";
        await _ws.ConnectAsync(new Uri(url), default);
        await _ws.SendAsync(Encoding.UTF8.GetBytes(
            JsonSerializer.Serialize(new { type = "conversation_initiation_client_data" })),
            WebSocketMessageType.Text, true, default);
        _ = Task.Run(ReceiveLoop);
    }

    private async Task ReceiveLoop()
    {
        var buf = new byte[32768]; var sb = new StringBuilder();
        while (_ws.State == WebSocketState.Open)
        {
            sb.Clear(); WebSocketReceiveResult r;
            do { r = await _ws.ReceiveAsync(buf, default); sb.Append(Encoding.UTF8.GetString(buf, 0, r.Count)); }
            while (!r.EndOfMessage);

            using var doc = JsonDocument.Parse(sb.ToString());
            var type = doc.RootElement.GetProperty("type").GetString();
            if (type == "audio")            // ElevenLabs audio -> call
                Emit(Convert.FromBase64String(doc.RootElement
                    .GetProperty("audio_event").GetProperty("audio_base_64").GetString()));
            else if (type == "ping")
                await _ws.SendAsync(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new {
                    type = "pong", event_id = doc.RootElement.GetProperty("ping_event").GetProperty("event_id").GetInt32() })),
                    WebSocketMessageType.Text, true, default);
            else if (type == "interruption")  // barge-in: drop any agent audio still queued
                FlushMedia?.Invoke(this, EventArgs.Empty);
        }
    }

    // slice PCM into 20 ms / 640-byte frames the media platform expects
    private void Emit(byte[] pcm)
    {
        var all = new List<AudioMediaBuffer>(); long tick = DateTime.Now.Ticks;
        for (int off = 0; off < pcm.Length; off += 640)
        {
            var frame = new byte[640];
            Array.Copy(pcm, off, frame, 0, Math.Min(640, pcm.Length - off));
            all.AddRange(Utilities.CreateAudioMediaBuffers(frame, tick, _logger));
            tick += 20 * 10000;
        }
        if (all.Count > 0) SendMediaBuffer?.Invoke(this, new MediaStreamEventArgs { AudioMediaBuffers = all });
    }
}
```

Beide Seiten verwenden PCM mit 16 kHz und Mono. Daher ist es ein Base64-Passthrough – stellen Sie den Agenten auf `pcm_16000` ein. Bei einer ElevenLabs-`interruption` (Unterbrechung) löst die Bridge `FlushMedia` aus. Verbinden Sie dies mit Ihrem Medienstream, damit ausstehende `AudioMediaBuffer` verworfen werden. Andernfalls spricht der Agent weiter über den Anrufer hinweg. Die vollständige Nachrichtenreferenz finden Sie in der [WebSocket-Dokumentation](/docs/de/eleven-agents/libraries/web-sockets). Das Auflegen am Anrufende und die Warmübergabe werden in den folgenden Abschnitten behandelt.

> **Note**
>
> Die URL in `Connect()` erreicht einen **öffentlichen** Agenten. Für einen privaten Agenten fordern Sie
> serverseitig eine kurzlebige signierte URL an – `GET /v1/convai/conversation/get-signed-url?agent_id=...` mit
> Ihrem API-Key – und verbinden Sie sich stattdessen mit der zurückgegebenen URL. Setzen Sie bei [Datenresidenz](/docs/de/overview/administration/data-residency)
> `ElevenLabsOrigin` auf Ihren Residency-Host (`wss://api.eu.residency.elevenlabs.io`, `.in.` oder `.sg.`) – Anfragen
> nach signierten URLs verwenden den passenden `https://`-Host.

## Schritt 4 – In Teams anrufbar machen

1. Aktivieren Sie **Calling** im Teams-Kanal des Azure Bots und setzen Sie den **Calling-Webhook** auf `https://YOUR_FQDN/api/calling`:

   ```bash
   az bot msteams create -g $RG -n el-teams-agent-bot \
     --enable-calling --calling-web-hook "https://YOUR_FQDN/api/calling"
   ```

   Im Portal finden Sie dies unter Ihrer Azure-Bot-Ressource → **Channels** → **Microsoft Teams** → Tab **Calling**:

   ![Die Azure-Bot-Channel-Ansicht mit dem Microsoft-Teams-Kanal als
   fehlerfrei](/docs/_fern-img/01115d26ab645dd6ec6569a36a918cd4de0214c79dcb0070d7a3c3a5e544c93b.webp)

   ![Der Calling-Tab des Teams-Kanals mit aktivierter Option Enable calling und gesetztem Calling-Webhook](/docs/_fern-img/7c4819f64878bfb152661adc948c1170f9efd48ba3d68401c4a4d4ba075951b0.webp)

2. Erstellen Sie ein **Teams-App-Manifest** mit `bots[0].supportsCalling: true` und der App-ID des Bots und laden Sie es quer (**Apps → Manage your apps → Upload a custom app**), oder veröffentlichen Sie es ohne Benutzeroberfläche organisationsweit: `New-TeamsApp -DistributionMethod organization -Path ./bot-app.zip` (MicrosoftTeams-PowerShell-Modul).

Suchen Sie die App in Teams per Namen und rufen Sie sie an – der Bot antwortet und der ElevenLabs-Agent spricht.

![Ein aktiver Teams-Anruf mit dem ElevenLabs-Agenten-
Bot](/docs/_fern-img/0f1fad08a091bcd20ffbbf5c3949b5503f423d8eb6298f464a870b3a0a254726.webp)

> **Note**
>
> Für 1:1-Anrufe per Name ist keine Telefonnummer oder Ressourcenidentität erforderlich – diese werden nur für PSTN-
> Einwahl benötigt. `Calls.AccessMedia.All` aktiviert die Roh-Audio-Bridge.

## Textchat (derselbe Bot)

Derselbe Azure Bot kann in Teams auch auf **Text** antworten — Nutzer können den Agenten also anrufen oder mit ihm chatten. Anrufe und Nachrichten sind unabhängige Kanäle des Bots: Der Calling-Webhook verarbeitet Sprache, und ein Bot-Framework-**Messaging-Endpunkt** (`/api/messages`) verarbeitet Chats.

![Ein Teams-Chat, in dem der ElevenLabs-Agenten-Bot auf Textnachrichten antwortet](/docs/_fern-img/fb75ae17848916790d29c1c0a703051946902893e46ed62884742db36be453b2.webp)

Richten Sie den Messaging-Endpunkt des Bots auf den Host, der ihn bereitstellt (den Medienbot oder einen anderen Dienst — es muss nicht die Windows-VM sein):

```bash
az bot update -g $RG -n el-teams-agent-bot --endpoint "https://YOUR_FQDN/api/messages"
```

Implementieren Sie den Endpunkt mit dem Bot Framework SDK und leiten Sie jede Nachricht im **Textmodus** über denselben für Sprache verwendeten Conversation-WebSocket an den Agenten weiter — senden Sie ein [`user_message`](/docs/de/eleven-agents/customization/events/client-to-server-events)-Event und lesen Sie das [`agent_response`](/docs/de/eleven-agents/customization/events/client-events)-Event. Aktivieren Sie zuerst das Feld **erste Nachricht** in den [Override-](/docs/de/eleven-agents/customization/personalization/overrides)Einstellungen des Agenten — der folgende Code überschreibt es mit einem leeren Wert, sodass die Antwort auf die Nachricht des Nutzers und nicht die Begrüßung des Agenten zurückgegeben wird:

**`ChatBot.cs — Teams-Textchat -> ElevenLabs (Textmodus)`**

```csharp title="ChatBot.cs — Teams-Textchat -> ElevenLabs (Textmodus)"
public class ChatBot : ActivityHandler
{
    private readonly AppSettings _settings;
    public ChatBot(AppSettings settings) => _settings = settings;

    protected override async Task OnMessageActivityAsync(
        ITurnContext<IMessageActivity> turn, CancellationToken ct)
    {
        var reply = await AskAgent(turn.Activity.Text, ct);
        await turn.SendActivityAsync(MessageFactory.Text(reply), ct);
    }

    private async Task<string> AskAgent(string text, CancellationToken ct)
    {
        using var ws = new ClientWebSocket();
        var url = $"{_settings.ElevenLabsOrigin}/v1/convai/conversation?agent_id={_settings.ElevenLabsAgentId}";
        await ws.ConnectAsync(new Uri(url), ct);
        // Suppress the agent's greeting: with no override, the first agent_response is the
        // configured first message, not the answer to this user_message.
        await Send(ws, new
        {
            type = "conversation_initiation_client_data",
            conversation_config_override = new { agent = new { first_message = "" } },
        }, ct);
        await Send(ws, new { type = "user_message", text }, ct);

        var buf = new byte[16384]; var sb = new StringBuilder();
        while (ws.State == WebSocketState.Open)
        {
            sb.Clear(); WebSocketReceiveResult r;
            do { r = await ws.ReceiveAsync(buf, ct); sb.Append(Encoding.UTF8.GetString(buf, 0, r.Count)); }
            while (!r.EndOfMessage);

            using var doc = JsonDocument.Parse(sb.ToString());
            switch (doc.RootElement.GetProperty("type").GetString())
            {
                case "agent_response":
                    return doc.RootElement.GetProperty("agent_response_event")
                              .GetProperty("agent_response").GetString();
                case "ping":
                    await Send(ws, new { type = "pong", event_id = doc.RootElement
                        .GetProperty("ping_event").GetProperty("event_id").GetInt32() }, ct);
                    break;
            }
        }
        return "Sorry, I couldn't reach the agent.";
    }

    private static Task Send(ClientWebSocket ws, object msg, CancellationToken ct) =>
        ws.SendAsync(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(msg)),
            WebSocketMessageType.Text, true, ct);
}
```

Registrieren Sie ihn auf die übliche Weise (einen `CloudAdapter`, den Bot über `AddTransient<IBot, ChatBot>()` und einen `/api/messages`-Controller) und fügen Sie dem Bot-Eintrag des Manifests Chat-Bereiche hinzu:

```json
"bots": [
  { "botId": "YOUR_APP_ID", "supportsCalling": true, "scopes": ["personal", "team", "groupChat"] }
]
```

> **Note**
>
> Das Snippet öffnet für jede Nachricht eine neue Conversation, daher ist jeder Turn unabhängig. Für
> **Chat-Speicher** halten Sie einen WebSocket pro Teams-`conversation.id` geöffnet (verwenden Sie ihn über mehrere Turns hinweg) und bereinigen Sie
> inaktive Sitzungen — der Agent erinnert sich dann an frühere Nachrichten in diesem Chat. Der Override für `first_message`
> muss in den [Override-](/docs/de/eleven-agents/customization/personalization/overrides)Einstellungen des Agenten aktiviert sein — der Server
> **schließt die Conversation**, wenn ein nicht erlaubter Override gesendet wird. Falls Sie ihn nicht aktivieren können, lassen Sie den
> Override weg und verwerfen stattdessen die erste `agent_response` jeder Sitzung (die Begrüßung) und geben Sie
> die nächste zurück.

> **Tip**
>
> Falls Chat-Antworten nie eintreffen, aktivieren Sie das **`agent_response`**-[Client- Event](/docs/de/eleven-agents/customization/events/client-events) in den **erweiterten**
> Einstellungen des Agenten — Textantworten werden über dieses Event bereitgestellt.

## Anruf beenden

Wenn ElevenLabs die Conversation beendet (das Tool **Anruf beenden** schließt den WebSocket), legen Sie die Teams-Verbindung auf:

```csharp
await this.Call.DeleteAsync(); // after a short delay so the goodbye audio finishes
```

## Warme Übergabe an einen Mitarbeiter

Der Agent löst ein benutzerdefiniertes `transfer_to_human`-Client-Tool aus; der Bot **lädt einen Teams-Nutzer** in den laufenden Anruf ein (beratendes Hinzufügen) und tritt dann zurück:

```csharp
var target = new IdentitySet { User = new Identity { Id = humanObjectId } };
await this.Call.Participants.InviteAsync(target, replacesCallId: null);
// suppress the end-call hangup while transferring, and mute the bot
```

> **Tip**
>
> Die beratende Übergabe (`replacesCallId`) setzt voraus, dass beide Parteien Teams-Nutzer im **selben
> Tenant** sind; PSTN-Übergabeziele erfordern eine Anwendungsinstanz. Um den Mitarbeiter zuerst zu informieren, übergeben Sie einen
> `reason`-Parameter vom Agenten und spielen Sie ihn dem Mitarbeiter vor dem Verbinden vor.

## Fehlerbehebung

#### \`MediaPlatform benötigt ein System mit mindestens 2 Kernen\`

Die VM hat nur einen physischen Kern. Ändern Sie die Größe auf ≥ 2 physische Kerne (z. B. `D4s_v3`) und starten Sie neu.

#### \`DLL 'NativeMedia' kann nicht geladen werden\`

Installieren Sie das **VC++ Redistributable** (`vcredist140`) und das Windows-Feature **Server-Media-Foundation**,
und starten Sie dann den Bot neu.

#### Eingehender Anruf gibt 500 zurück / Anruf wird nicht verbunden

Der EchoBot-Port-Null-Bug auf 443 — patchen Sie `HttpHelpers.SetAbsoluteUri` (siehe Schritt 3). Prüfen Sie außerdem,
ob das Zertifikat von einer CA signiert und über 443 erreichbar ist.

#### Beim Anrufen des Bots heißt es: 'Verbindung konnte nicht hergestellt werden'

Prüfen Sie, ob Calling im Teams-Kanal mit dem korrekten `/api/calling`-Webhook aktiviert ist, die
Graph-Berechtigung `Calls.AccessMedia.All` genehmigt wurde und die Ports 443/8445/9441 sowohl in der
NSG als auch in der Windows-Firewall geöffnet sind. Falls Anrufe **früher funktionierten und dann nicht mehr**, prüfen Sie, ob der Bot-Prozess
noch auf der VM läuft — das standardmäßige Ausführungslimit der Aufgabenplanung von 72 Stunden beendet ihn einige
Tage nach dem Start (siehe Warnung in Schritt 3).

## Nützliche Links

* [Übersicht zu Anruf- und Besprechungsbots](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/calls-meetings-bots-overview)
* [Konzepte für Echtzeitmedien](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/real-time-media-concepts)
* [Einen Calling-Bot registrieren](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/registering-calling-bot)
* [microsoft-graph-comms-samples (EchoBot)](https://github.com/microsoftgraph/microsoft-graph-comms-samples)
* [call: Übergabe (beratend)](https://learn.microsoft.com/en-us/graph/api/call-transfer)
* [Agent-WebSocket-Protokoll](/docs/de/eleven-agents/libraries/web-sockets)