> 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.

# Bot de chamadas do Graph

## Visão geral

Essa abordagem transforma o agente em uma **identidade do Teams que pode receber chamadas**. Um usuário o busca pelo nome e faz uma chamada 1:1, e o agente responde em tempo real — sem número de telefone, PSTN ou Créditos de Comunicação. É a única abordagem que permite chamadas pelo nome e também a mais complexa de executar.

Ela usa um **bot de mídia em tempo real do Microsoft Graph** (a plataforma de chamadas do Cloud Communications). O SDK de mídia (`Microsoft.Skype.Bots.Media`) funciona **apenas com .NET no Windows Server** — não há suporte para Linux ou alternativas fora do .NET para áudio bruto em chamadas do Teams.

> **Note**
>
> Esta é a única abordagem que permite chamadas **pelo nome** no Teams. Para uma configuração mais
> simples, prefira a [aba do widget](/docs/pt/eleven-agents/phone-numbers/microsoft-teams/widget-tab) ou use o
> [ACS](/docs/pt/eleven-agents/phone-numbers/microsoft-teams/azure-communication-services) se você
> quiser especificamente um número de telefone.

## Como funciona

![Um usuário do Teams chama o bot pelo nome; o Teams encaminha a chamada para o bot de mídia em uma VM Windows, que conecta o áudio PCM 16k bruto ao agente da ElevenLabs por WebSocket](/docs/_fern-files/elevenlabs.docs.buildwithfern.com/f411ec0b44d4c5ca1a61598fbd1e6162e46a1c74179eebb4b673ee1428823e84/assets/images/conversational-ai/teams-media-bot-architecture.svg)

O bot atende com **mídia hospedada pelo aplicativo**, recebe 50 frames de áudio por segundo (PCM de 16 kHz e 20 ms), conecta-os ao agente da ElevenLabs por WebSocket e transmite o áudio do agente de volta para a chamada.

## Requisitos

1. Um registro de **Azure Bot** + aplicativo (registro de aplicativo do Entra).
2. **Permissões de aplicativo do Graph** com consentimento de administrador: `Calls.AccessMedia.All` (mídia bruta) e `Calls.Initiate.All`.
3. Uma **VM Windows Server** (≥ **2 núcleos físicos** — por exemplo, `Standard_D4s_v3`) com IP público e portas de mídia abertas.
4. Um **certificado TLS assinado por uma CA** em um FQDN público para o endpoint de mídia/sinalização (a plataforma de mídia rejeita certificados autoassinados).
5. Um [agente da ElevenLabs](/docs/pt/eleven-agents/quickstart) configurado como **PCM 16000 Hz** nos dois lados: formato de saída de TTS na aba **Voice** e formato de áudio de entrada do usuário na aba **Advanced**.

> **Warning**
>
> Uma `D2s_v3` (2 vCPU = **1 núcleo físico**) falha com `MediaPlatform needs a system with at least 2
>   cores`. Use um tamanho com ≥ 2 núcleos físicos (por exemplo, `D4s_v3`).

## Permissões e funções

| Escopo                       | Função / permissão                                                                                                                                | Motivo                                                                                                                                |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Entra                        | **Administrador de Aplicativos**                                                                                                                  | criar o registro do aplicativo + Azure Bot                                                                                            |
| Entra                        | **Administrador Global / Administrador de Funções Privilegiadas**                                                                                 | conceder consentimento de administrador para as permissões de chamadas do Graph — permissões de aplicativo não permitem autoconcessão |
| Microsoft Graph (aplicativo) | `Calls.AccessMedia.All`, `Calls.Initiate.All`                                                                                                     | atender chamadas 1:1 e acessar mídia bruta                                                                                            |
| Azure RBAC                   | **Colaborador** no grupo de recursos                                                                                                              | criar a VM Windows + Azure Bot                                                                                                        |
| Administrador do Teams       | permitir o [**upload de aplicativo personalizado**](https://admin.teams.microsoft.com/policies/manage-apps); habilitar o canal **Calling** do bot | carregar o aplicativo localmente e receber chamadas                                                                                   |

## Etapa 1 — Registrar o bot + permissões do Graph

Crie um registro de aplicativo e um Azure Bot vinculado a ele. Em seguida, conceda as permissões de chamada e dê consentimento a elas (você precisa ser Administrador Global / Administrador de Funções Privilegiadas para consentir):

```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
```

Conceda as duas funções de aplicativo do Graph e o **consentimento de administrador** (requer Administrador Global / Administrador de Funções Privilegiadas) e confirme que as atribuições foram aplicadas:

```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**
>
> Se `admin-consent` retornar `Consent validation failed`, conceda as funções do aplicativo diretamente na entidade de serviço:
>
> ```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
> ```

No portal, verifique na [central de administração do Entra](https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/RegisteredAppsListBlade), em **App registrations → seu aplicativo → API permissions**: ambas as permissões devem aparecer como **Granted**, com marcas de verificação verdes.

![A tela de permissões de API do registro do aplicativo mostrando Calls.AccessMedia.All e Calls.Initiate.All
concedidas](/docs/_fern-img/4abc1e220d3fc03845594aa4f835656425cc1c39448dae30dace98e672fd8fb1.webp)

## Etapa 2 — Provisionar a VM Windows, o certificado e as portas

```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
```

Na VM (o código nativo da plataforma de mídia precisa deles — o Windows Server não os inclui por padrão):

```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
```

Abra as mesmas portas no **firewall do Windows** e anote a **impressão digital** do certificado — o bot vincula o Kestrel (443 + uma porta de notificações) e a plataforma de mídia (8445) a ele.

> **Tip**
>
> O próprio FQDN `*.cloudapp.azure.com` da VM funciona com um certificado Let's Encrypt — não é
> necessário ter um domínio separado.

## Etapa 3 — Compilar e executar o bot

Comece pelo **PublicSamples/EchoBot** do [`microsoft-graph-comms-samples`](https://github.com/microsoftgraph/microsoft-graph-comms-samples) da Microsoft — ele usa `net6.0` e é compilado com o SDK do .NET (não precisa das Visual Studio Build Tools):

```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
```

Configure a seção `AppSettings` do `appsettings.json` com seu `AadAppId`, `AadAppSecret`, `ServiceDnsName`/`MediaDnsName` (o FQDN da VM), `CertificateThumbprint` e as portas (chamadas `443`, notificações `9441`, mídia `8445`). Adicione duas configurações para a ponte da ElevenLabs abaixo: `ElevenLabsAgentId` e `ElevenLabsOrigin` (`wss://api.elevenlabs.io` ou seu host de residência). Execute-o como uma tarefa agendada / serviço do Windows para que sobreviva a reinicializações.

> **Warning**
>
> O **limite de tempo de execução padrão do Agendador de Tarefas (72 horas)** encerra silenciosamente tarefas de longa duração — um bot iniciado na inicialização para de funcionar três dias depois e as chamadas falham com "we couldn't connect you". Desative o limite e adicione reinicialização em caso de falha:
>
> ```powershell
> $s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Seconds 0) `
>   -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) -StartWhenAvailable
> Set-ScheduledTask -TaskName EchoBot -Settings $s
> ```

> **Warning**
>
> O EchoBot padrão falha em uma chamada feita para a porta padrão 443: `HttpHelpers.SetAbsoluteUri`
> chama `req.Host.Port.Value`, que é nulo quando o cabeçalho Host não tem uma porta explícita. Corrija-o para
> `req.Host.Port ?? (req.IsHttps ? 443 : 80)`.

### Substitua o eco pela ElevenLabs

A integração de áudio do EchoBot é simples: `SpeechService.AppendAudioBuffer(in)` e um evento `OnSendMediaBufferEventArgs(out)`. Substitua o corpo do Azure Speech por uma ponte WebSocket do agente da ElevenLabs que mantenha a mesma interface:

**`SpeechService.cs — ponte da ElevenLabs (principal)`**

```csharp title="SpeechService.cs — ponte da ElevenLabs (principal)"
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 });
    }
}
```

Os dois lados usam PCM mono de 16 kHz, portanto é uma passagem direta em base64 — configure o agente como `pcm_16000`. Em uma `interruption` (interrupção de fala) da ElevenLabs, a ponte gera `FlushMedia`; conecte isso ao seu fluxo de mídia para descartar quaisquer `AudioMediaBuffer`s em fila. Caso contrário, o agente continuará falando sobre o interlocutor. A referência completa de mensagens está na [documentação de WebSocket](/docs/pt/eleven-agents/libraries/web-sockets). O desligamento no fim da chamada e a transferência assistida são abordados nas seções abaixo.

> **Note**
>
> A URL em `Connect()` acessa um agente **público**. Para um agente privado, solicite uma URL assinada
> de curta duração no servidor — `GET /v1/convai/conversation/get-signed-url?agent_id=...` com sua
> chave de API — e conecte-se à URL retornada. Em [residência de dados](/docs/pt/overview/administration/data-residency), defina `ElevenLabsOrigin` como seu host de residência
> (`wss://api.eu.residency.elevenlabs.io`, `.in.` ou `.sg.`) — as solicitações de URL assinada usam o
> host `https://` correspondente.

## Etapa 4 — Permitir chamadas no Teams

1. **Habilite Calling** no canal Teams do Azure Bot e defina o **webhook de chamadas** como `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"
   ```

   No portal, isso fica em seu recurso do Azure Bot → **Channels** → **Microsoft Teams** → aba **Calling**:

   ![A tela Channels do Azure Bot listando o canal Microsoft Teams como
   íntegro](/docs/_fern-img/01115d26ab645dd6ec6569a36a918cd4de0214c79dcb0070d7a3c3a5e544c93b.webp)

   ![A aba Calling do canal Teams com Enable calling marcado e o webhook de chamadas
   configurado](/docs/_fern-img/7c4819f64878bfb152661adc948c1170f9efd48ba3d68401c4a4d4ba075951b0.webp)

2. Crie um **manifesto de aplicativo do Teams** com `bots[0].supportsCalling: true` e o ID do aplicativo do bot. Em seguida, carregue-o localmente (**Apps → Manage your apps → Upload a custom app**) ou publique-o para toda a organização sem usar a interface: `New-TeamsApp -DistributionMethod organization -Path ./bot-app.zip` (módulo PowerShell MicrosoftTeams).

Busque o aplicativo pelo nome no Teams e ligue para ele — o bot atende e o agente da ElevenLabs fala.

![Uma chamada ativa do Teams com o bot do agente da ElevenLabs
](/docs/_fern-img/0f1fad08a091bcd20ffbbf5c3949b5503f423d8eb6298f464a870b3a0a254726.webp)

> **Note**
>
> Não é necessário ter número de telefone ou conta de recurso para chamadas 1:1 pelo nome — eles são
> necessários apenas para chamadas PSTN. `Calls.AccessMedia.All` é o que habilita a ponte de áudio bruto.

## Chat de texto (mesmo bot)

O mesmo bot do Azure também pode responder por **texto** no Teams — assim, os usuários podem ligar para o agente ou conversar com ele por chat. Chamadas e mensagens são canais independentes no bot: o webhook de chamadas processa a voz, e um **endpoint de mensagens** (`/api/messages`) do Bot Framework processa o chat.

![Um chat no Teams com o bot agente da ElevenLabs respondendo a mensagens de texto](/docs/_fern-img/fb75ae17848916790d29c1c0a703051946902893e46ed62884742db36be453b2.webp)

Aponte o endpoint de mensagens do bot para o host que o atende (o bot de mídia ou qualquer outro serviço — não precisa ser a VM do Windows):

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

Implemente o endpoint com o SDK do Bot Framework e encaminhe cada mensagem para o agente no **modo de texto** pelo mesmo WebSocket de conversa usado para voz — envie um evento [`user_message`](/docs/pt/eleven-agents/customization/events/client-to-server-events) e leia o evento [`agent_response`](/docs/pt/eleven-agents/customization/events/client-events). Primeiro, ative o campo de **primeira mensagem** nas configurações de [substituições](/docs/pt/eleven-agents/customization/personalization/overrides) do agente — o código abaixo o substitui por um valor vazio para que a resposta seja a resposta à mensagem do usuário, em vez da saudação do agente:

**`ChatBot.cs — chat de texto do Teams -> ElevenLabs (modo de texto)`**

```csharp title="ChatBot.cs — chat de texto do Teams -> ElevenLabs (modo de texto)"
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);
}
```

Registre-o da forma padrão (um `CloudAdapter`, o bot via `AddTransient<IBot, ChatBot>()` e um controlador `/api/messages`) e adicione escopos de chat à entrada do bot no manifesto:

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

> **Note**
>
> O exemplo abre uma nova conversa por mensagem, então cada turno é independente. Para ter
> **memória** no chat, mantenha um WebSocket aberto por `conversation.id` do Teams (reutilize-o entre os turnos) e encerre
> sessões inativas — assim, o agente se lembra das mensagens anteriores nesse chat. A substituição de
> `first_message` precisa estar ativada nas configurações de
> [substituições](/docs/pt/eleven-agents/customization/personalization/overrides) do agente — o servidor
> **fecha a conversa** se uma substituição não permitida for enviada. Se não puder ativá-la, omita a
> substituição e, em vez disso, descarte o primeiro `agent_response` de cada sessão (a saudação) e retorne
> o seguinte.

> **Tip**
>
> Se as respostas do chat nunca chegarem, ative o [evento do cliente `agent_response`](/docs/pt/eleven-agents/customization/events/client-events) nas configurações
> **Avançadas** do agente — as respostas de texto são enviadas por esse evento.

## Fim da chamada

Quando a ElevenLabs encerra a conversa (a ferramenta **Encerrar chamada** fecha o WebSocket), desligue a conexão do Teams:

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

## Transferência assistida para uma pessoa

O agente dispara uma ferramenta de cliente personalizada `transfer_to_human`; o bot **convida um usuário do Teams** para a chamada em andamento (adição consultiva) e então sai da chamada:

```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**
>
> A transferência consultiva (`replacesCallId`) exige que ambas as partes sejam usuários do Teams no \*\*mesmo
> locatário \*\*; destinos de transferência PSTN exigem uma instância de aplicativo. Para contextualizar a pessoa primeiro, envie um
> parâmetro `reason` do agente e reproduza-o para a pessoa antes de conectar as chamadas.

## Solução de problemas

#### \`MediaPlatform precisa de um sistema com pelo menos 2 núcleos\`

A VM tem apenas um núcleo físico. Redimensione para ≥ 2 núcleos físicos (por exemplo, `D4s_v3`) e reinicie.

#### \`Não foi possível carregar a DLL 'NativeMedia'\`

Instale o **VC++ Redistributable** (`vcredist140`) e o recurso do Windows **Server-Media-Foundation** e,
em seguida, reinicie o bot.

#### A chamada recebida retorna 500 / a chamada não conecta

O bug de porta nula do EchoBot na 443 — corrija `HttpHelpers.SetAbsoluteUri` (consulte a Etapa 3). Confirme também
que o certificado é assinado por uma CA e está acessível na 443.

#### Ao ligar para o bot, é exibida a mensagem 'não foi possível conectar você'

Confirme que Calling está ativado no canal do Teams com o webhook `/api/calling` correto, que a permissão do
Graph `Calls.AccessMedia.All` foi concedida e que as portas 443/8445/9441 estão abertas tanto no
NSG quanto no firewall do Windows. Se as chamadas **funcionavam antes e pararam**, verifique se o processo do bot
ainda está em execução na VM — o limite padrão de execução de 72 horas do Agendador de Tarefas o encerra alguns
dias após a inicialização (consulte o aviso na Etapa 3).

## Links úteis

* [Visão geral dos bots de chamadas e reuniões](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/calls-meetings-bots-overview)
* [Conceitos de mídia em tempo real](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/real-time-media-concepts)
* [Registrar um bot de chamadas](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: transferência (consultiva)](https://learn.microsoft.com/en-us/graph/api/call-transfer)
* [Protocolo WebSocket do agente](/docs/pt/eleven-agents/libraries/web-sockets)