Microsoft Teams (plugin)

“Abandon all hope, ye who enter here.”

Atualizado: 2026-01-21

Status: texto + anexos em DM são suportados; envio de arquivos em canais/grupos requer sharePointSiteId + permissões do Graph (veja Envio de arquivos em chats de grupo). Enquetes são enviadas via Adaptive Cards.

Plugin necessário

O Microsoft Teams é distribuído como plugin e não vem incluído na instalação principal.

Mudança incompatível (2026.1.15): MS Teams foi removido do core. Se você o utiliza, precisa instalar o plugin.

Justificativa: mantém a instalação core mais leve e permite que as dependências do MS Teams sejam atualizadas independentemente.

Instale via CLI (registro npm):

openclaw plugins install @openclaw/msteams

Checkout local (ao executar de um repositório git):

openclaw plugins install ./extensions/msteams

Se você escolher o Teams durante a configuração/onboarding e um checkout git for detectado, o OpenClaw oferecerá o caminho de instalação local automaticamente.

Detalhes: Plugins

Configuração rápida (iniciante)

  1. Instale o plugin do Microsoft Teams.
  2. Crie um Azure Bot (App ID + client secret + tenant ID).
  3. Configure o OpenClaw com essas credenciais.
  4. Exponha /api/messages (porta 3978 por padrão) via URL pública ou túnel.
  5. Instale o pacote do app Teams e inicie o gateway.

Configuração mínima:

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      appPassword: "<APP_PASSWORD>",
      tenantId: "<TENANT_ID>",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

Observação: chats de grupo são bloqueados por padrão (channels.msteams.groupPolicy: "allowlist"). Para permitir respostas em grupo, defina channels.msteams.groupAllowFrom (ou use groupPolicy: "open" para permitir qualquer membro, com filtragem por menção).

Objetivos

  • Conversar com o OpenClaw via DMs, chats de grupo ou canais do Teams.
  • Manter o roteamento determinístico: respostas sempre voltam para o canal de onde vieram.
  • Ter comportamento de canal seguro por padrão (menções obrigatórias, a menos que configurado de outra forma).

Escritas de configuração

Por padrão, o Microsoft Teams pode escrever atualizações de configuração acionadas por /config set|unset (requer commands.config: true).

Desativar com:

{
  channels: { msteams: { configWrites: false } },
}

Controle de acesso (DMs + grupos)

Acesso a DMs

  • Padrão: channels.msteams.dmPolicy = "pairing". Remetentes desconhecidos são ignorados até serem aprovados.
  • channels.msteams.allowFrom deve usar IDs de objeto AAD estáveis.
  • UPNs/nomes de exibição são mutáveis; correspondência direta é desativada por padrão e só é ativada com channels.msteams.dangerouslyAllowNameMatching: true.
  • O assistente pode resolver nomes para IDs via Microsoft Graph quando as credenciais permitem.

Acesso a grupos

  • Padrão: channels.msteams.groupPolicy = "allowlist" (bloqueado a menos que você adicione groupAllowFrom). Use channels.defaults.groupPolicy para sobrescrever o padrão quando não definido.
  • channels.msteams.groupAllowFrom controla quais remetentes podem acionar em chats de grupo/canais (recorre a channels.msteams.allowFrom).
  • Defina groupPolicy: "open" para permitir qualquer membro (ainda com filtragem por menção por padrão).
  • Para não permitir nenhum canal, defina channels.msteams.groupPolicy: "disabled".

Exemplo:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["[email protected]"],
    },
  },
}

Lista de permitidos de teams + canais

  • Restrinja respostas de grupo/canal listando teams e canais em channels.msteams.teams.
  • As chaves devem usar IDs estáveis de team e IDs de conversa de canal.
  • Quando groupPolicy="allowlist" e uma lista de permitidos de teams está presente, apenas teams/canais listados são aceitos (com filtragem por menção).
  • O assistente de configuração aceita entradas Team/Channel e as armazena para você.
  • Na inicialização, o OpenClaw resolve nomes de team/canal e da lista de permitidos de usuários para IDs (quando as permissões do Graph permitem) e registra o mapeamento; nomes de team/canal não resolvidos são mantidos como digitados mas ignorados para roteamento por padrão, a menos que channels.msteams.dangerouslyAllowNameMatching: true esteja ativado.

Exemplo:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      teams: {
        "My Team": {
          channels: {
            General: { requireMention: true },
          },
        },
      },
    },
  },
}

Como funciona

  1. Instale o plugin do Microsoft Teams.
  2. Crie um Azure Bot (App ID + secret + tenant ID).
  3. Construa um pacote de app Teams que referencia o bot e inclui as permissões RSC abaixo.
  4. Faça upload/instale o app Teams em um team (ou escopo pessoal para DMs).
  5. Configure msteams em ~/.openclaw/openclaw.json (ou variáveis de ambiente) e inicie o gateway.
  6. O gateway escuta tráfego de webhook do Bot Framework em /api/messages por padrão.

Configuração do Azure Bot (Pré-requisitos)

Antes de configurar o OpenClaw, você precisa criar um recurso Azure Bot.

Passo 1: Crie o Azure Bot

  1. Acesse Criar Azure Bot

  2. Preencha a aba Basics:

    CampoValor
    Bot handleNome do bot, ex.: openclaw-msteams (deve ser único)
    SubscriptionSelecione sua assinatura Azure
    Resource groupCrie novo ou use existente
    Pricing tierFree para dev/teste
    Type of AppSingle Tenant (recomendado - veja nota abaixo)
    Creation typeCreate new Microsoft App ID

Aviso de descontinuação: A criação de novos bots multi-tenant foi descontinuada após 2025-07-31. Use Single Tenant para novos bots.

  1. Clique em Review + createCreate (aguarde ~1-2 minutos)

Passo 2: Obtenha as credenciais

  1. Vá ao recurso Azure Bot → Configuration
  2. Copie Microsoft App ID → este é o seu appId
  3. Clique em Manage Password → vá para o App Registration
  4. Em Certificates & secretsNew client secret → copie o Value → este é o seu appPassword
  5. Vá em Overview → copie Directory (tenant) ID → este é o seu tenantId

Passo 3: Configure o Messaging Endpoint

  1. No Azure Bot → Configuration
  2. Defina o Messaging endpoint com a URL do webhook:
    • Produção: https://your-domain.com/api/messages
    • Dev local: use um túnel (veja Desenvolvimento Local abaixo)

Passo 4: Ative o Canal Teams

  1. No Azure Bot → Channels
  2. Clique em Microsoft Teams → Configure → Save
  3. Aceite os Termos de Serviço

Desenvolvimento Local (Tunelamento)

O Teams não consegue acessar localhost. Use um túnel para desenvolvimento local:

Opção A: ngrok

ngrok http 3978
# Copie a URL https, ex.: https://abc123.ngrok.io
# Defina o messaging endpoint como: https://abc123.ngrok.io/api/messages

Opção B: Tailscale Funnel

tailscale funnel 3978
# Use sua URL do Tailscale Funnel como messaging endpoint

Portal de Desenvolvedores do Teams (Alternativa)

Em vez de criar manualmente um ZIP de manifesto, você pode usar o Portal de Desenvolvedores do Teams:

  1. Clique em + New app
  2. Preencha as informações básicas (nome, descrição, informações do desenvolvedor)
  3. Vá em App featuresBot
  4. Selecione Enter a bot ID manually e cole o App ID do Azure Bot
  5. Marque os escopos: Personal, Team, Group Chat
  6. Clique em DistributeDownload app package
  7. No Teams: AppsManage your appsUpload a custom app → selecione o ZIP

Isso geralmente é mais fácil do que editar manifestos JSON manualmente.

Testando o bot

Opção A: Azure Web Chat (verifique o webhook primeiro)

  1. No Portal Azure → recurso Azure Bot → Test in Web Chat
  2. Envie uma mensagem - você deve ver uma resposta
  3. Isso confirma que o endpoint do webhook funciona antes da configuração do Teams

Opção B: Teams (após instalação do app)

  1. Instale o app Teams (sideload ou catálogo da organização)
  2. Encontre o bot no Teams e envie uma DM
  3. Verifique os logs do gateway para atividade recebida

Configuração (texto mínimo)

  1. Instale o plugin do Microsoft Teams

    • Via npm: openclaw plugins install @openclaw/msteams
    • Via checkout local: openclaw plugins install ./extensions/msteams
  2. Registro do bot

    • Crie um Azure Bot (veja acima) e anote:
      • App ID
      • Client secret (App password)
      • Tenant ID (single-tenant)
  3. Manifesto do app Teams

    • Inclua uma entrada bot com botId = <App ID>.
    • Escopos: personal, team, groupChat.
    • supportsFiles: true (obrigatório para tratamento de arquivos no escopo pessoal).
    • Adicione permissões RSC (abaixo).
    • Crie ícones: outline.png (32x32) e color.png (192x192).
    • Compacte os três arquivos juntos: manifest.json, outline.png, color.png.
  4. Configure o OpenClaw

    {
      "msteams": {
        "enabled": true,
        "appId": "<APP_ID>",
        "appPassword": "<APP_PASSWORD>",
        "tenantId": "<TENANT_ID>",
        "webhook": { "port": 3978, "path": "/api/messages" }
      }
    }

    Você também pode usar variáveis de ambiente em vez de chaves de configuração:

    • MSTEAMS_APP_ID
    • MSTEAMS_APP_PASSWORD
    • MSTEAMS_TENANT_ID
  5. Endpoint do bot

    • Defina o Messaging Endpoint do Azure Bot como:
      • https://<host>:3978/api/messages (ou o caminho/porta escolhidos).
  6. Execute o gateway

    • O canal Teams inicia automaticamente quando o plugin está instalado e a configuração msteams existe com credenciais.

Contexto de histórico

  • channels.msteams.historyLimit controla quantas mensagens recentes de canal/grupo são incluídas no prompt.
  • Recorre a messages.groupChat.historyLimit. Defina 0 para desativar (padrão 50).
  • O histórico de DM pode ser limitado com channels.msteams.dmHistoryLimit (turnos do usuário). Overrides por usuário: channels.msteams.dms["<user_id>"].historyLimit.

Permissões RSC atuais do Teams (Manifesto)

Estas são as permissões resourceSpecific existentes no manifesto do app Teams. Elas se aplicam apenas dentro do team/chat onde o app está instalado.

Para canais (escopo de team):

  • ChannelMessage.Read.Group (Application) - receber todas as mensagens do canal sem @menção
  • ChannelMessage.Send.Group (Application)
  • Member.Read.Group (Application)
  • Owner.Read.Group (Application)
  • ChannelSettings.Read.Group (Application)
  • TeamMember.Read.Group (Application)
  • TeamSettings.Read.Group (Application)

Para chats de grupo:

  • ChatMessage.Read.Chat (Application) - receber todas as mensagens de chat de grupo sem @menção

Exemplo de manifesto Teams (reduzido)

Exemplo mínimo e válido com os campos obrigatórios. Substitua IDs e URLs.

{
  "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
  "manifestVersion": "1.23",
  "version": "1.0.0",
  "id": "00000000-0000-0000-0000-000000000000",
  "name": { "short": "OpenClaw" },
  "developer": {
    "name": "Your Org",
    "websiteUrl": "https://example.com",
    "privacyUrl": "https://example.com/privacy",
    "termsOfUseUrl": "https://example.com/terms"
  },
  "description": { "short": "OpenClaw in Teams", "full": "OpenClaw in Teams" },
  "icons": { "outline": "outline.png", "color": "color.png" },
  "accentColor": "#5B6DEF",
  "bots": [
    {
      "botId": "11111111-1111-1111-1111-111111111111",
      "scopes": ["personal", "team", "groupChat"],
      "isNotificationOnly": false,
      "supportsCalling": false,
      "supportsVideo": false,
      "supportsFiles": true
    }
  ],
  "webApplicationInfo": {
    "id": "11111111-1111-1111-1111-111111111111"
  },
  "authorization": {
    "permissions": {
      "resourceSpecific": [
        { "name": "ChannelMessage.Read.Group", "type": "Application" },
        { "name": "ChannelMessage.Send.Group", "type": "Application" },
        { "name": "Member.Read.Group", "type": "Application" },
        { "name": "Owner.Read.Group", "type": "Application" },
        { "name": "ChannelSettings.Read.Group", "type": "Application" },
        { "name": "TeamMember.Read.Group", "type": "Application" },
        { "name": "TeamSettings.Read.Group", "type": "Application" },
        { "name": "ChatMessage.Read.Chat", "type": "Application" }
      ]
    }
  }
}

Ressalvas do manifesto (campos obrigatórios)

  • bots[].botId deve corresponder ao App ID do Azure Bot.
  • webApplicationInfo.id deve corresponder ao App ID do Azure Bot.
  • bots[].scopes deve incluir as superfícies que você planeja usar (personal, team, groupChat).
  • bots[].supportsFiles: true é obrigatório para tratamento de arquivos no escopo pessoal.
  • authorization.permissions.resourceSpecific deve incluir leitura/envio de canal se quiser tráfego de canal.

Atualizando um app existente

Para atualizar um app Teams já instalado (ex.: para adicionar permissões RSC):

  1. Atualize o manifest.json com as novas configurações
  2. Incremente o campo version (ex.: 1.0.01.1.0)
  3. Re-compacte o manifesto com ícones (manifest.json, outline.png, color.png)
  4. Faça upload do novo zip:
    • Opção A (Teams Admin Center): Teams Admin Center → Teams apps → Manage apps → encontre seu app → Upload new version
    • Opção B (Sideload): No Teams → Apps → Manage your apps → Upload a custom app
  5. Para canais de team: reinstale o app em cada team para que as novas permissões entrem em vigor
  6. Feche completamente e reabra o Teams (não apenas feche a janela) para limpar metadados de app em cache

Capacidades: apenas RSC vs Graph

Com apenas RSC do Teams (app instalado, sem permissões de Graph API)

Funciona:

  • Ler conteúdo de texto de mensagens de canal.
  • Enviar conteúdo de texto em mensagens de canal.
  • Receber anexos de arquivo em DMs (pessoal).

Não funciona:

  • Conteúdo de imagens ou arquivos em canais/grupos (o payload inclui apenas um stub HTML).
  • Download de anexos armazenados no SharePoint/OneDrive.
  • Leitura de histórico de mensagens (além do evento de webhook ao vivo).

Com RSC do Teams + permissões de Application do Microsoft Graph

Adiciona:

  • Download de conteúdos hospedados (imagens coladas nas mensagens).
  • Download de anexos de arquivo armazenados no SharePoint/OneDrive.
  • Leitura de histórico de mensagens de canal/chat via Graph.

RSC vs Graph API

CapacidadePermissões RSCGraph API
Mensagens em tempo realSim (via webhook)Não (apenas polling)
Mensagens históricasNãoSim (pode consultar histórico)
Complexidade de setupApenas manifestoRequer consentimento de admin + fluxo de token
Funciona offlineNão (deve estar rodando)Sim (consulta a qualquer momento)

Conclusão: RSC é para escuta em tempo real; Graph API é para acesso histórico. Para recuperar mensagens perdidas enquanto offline, você precisa de Graph API com ChannelMessage.Read.All (requer consentimento de admin).

Mídia + histórico com Graph (obrigatório para canais)

Se precisar de imagens/arquivos em canais ou quiser buscar histórico de mensagens, você deve ativar permissões do Microsoft Graph e conceder consentimento de admin.

  1. No Entra ID (Azure AD) App Registration, adicione permissões de Application do Microsoft Graph:
    • ChannelMessage.Read.All (anexos de canal + histórico)
    • Chat.Read.All ou ChatMessage.Read.All (chats de grupo)
  2. Conceda consentimento de admin para o tenant.
  3. Incremente a versão do manifesto do app Teams, re-faça upload e reinstale o app no Teams.
  4. Feche completamente e reabra o Teams para limpar metadados de app em cache.

Permissão adicional para menções de usuário: menções @usuário funcionam imediatamente para usuários na conversa. No entanto, se quiser buscar e mencionar dinamicamente usuários que não estão na conversa atual, adicione a permissão User.Read.All (Application) e conceda consentimento de admin.

Limitações conhecidas

Timeouts de webhook

O Teams entrega mensagens via webhook HTTP. Se o processamento demorar muito (ex.: respostas lentas de LLM), você pode ver:

  • Timeouts do gateway
  • Teams reenviando a mensagem (causando duplicatas)
  • Respostas perdidas

O OpenClaw lida com isso retornando rapidamente e enviando respostas proativamente, mas respostas muito lentas ainda podem causar problemas.

Formatação

O markdown do Teams é mais limitado que o do Slack ou Discord:

  • Formatação básica funciona: negrito, itálico, código, links
  • Markdown complexo (tabelas, listas aninhadas) pode não renderizar corretamente
  • Adaptive Cards são suportados para enquetes e envio de cards arbitrários (veja abaixo)

Configuração

Configurações principais (veja /gateway/configuration para padrões compartilhados de canal):

  • channels.msteams.enabled: ativar/desativar o canal.
  • channels.msteams.appId, channels.msteams.appPassword, channels.msteams.tenantId: credenciais do bot.
  • channels.msteams.webhook.port (padrão 3978)
  • channels.msteams.webhook.path (padrão /api/messages)
  • channels.msteams.dmPolicy: pairing | allowlist | open | disabled (padrão: pairing)
  • channels.msteams.allowFrom: lista de permitidos de DM (IDs de objeto AAD recomendados). O assistente resolve nomes para IDs durante a configuração quando o acesso ao Graph está disponível.
  • channels.msteams.dangerouslyAllowNameMatching: toggle emergencial para reativar correspondência mutável por UPN/nome de exibição e roteamento direto por nome de team/canal.
  • channels.msteams.textChunkLimit: tamanho do chunk de texto de saída.
  • channels.msteams.chunkMode: length (padrão) ou newline para dividir em linhas em branco (limites de parágrafo) antes da fragmentação por tamanho.
  • channels.msteams.mediaAllowHosts: lista de permitidos para hosts de anexos de entrada (padrão: domínios Microsoft/Teams).
  • channels.msteams.mediaAuthAllowHosts: lista de permitidos para anexar headers de Authorization em retries de mídia (padrão: hosts Graph + Bot Framework).
  • channels.msteams.requireMention: exigir @menção em canais/grupos (padrão true).
  • channels.msteams.replyStyle: thread | top-level (veja Estilo de resposta).
  • channels.msteams.teams.<teamId>.replyStyle: override por team.
  • channels.msteams.teams.<teamId>.requireMention: override por team.
  • channels.msteams.teams.<teamId>.tools: overrides de política de ferramentas por team padrão (allow/deny/alsoAllow) usados quando um override de canal está ausente.
  • channels.msteams.teams.<teamId>.toolsBySender: overrides de política de ferramentas por team por remetente padrão (wildcard "*" suportado).
  • channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: override por canal.
  • channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: override por canal.
  • channels.msteams.teams.<teamId>.channels.<conversationId>.tools: overrides de política de ferramentas por canal (allow/deny/alsoAllow).
  • channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: overrides de política de ferramentas por canal por remetente (wildcard "*" suportado).
  • Chaves de toolsBySender devem usar prefixos explícitos: id:, e164:, username:, name: (chaves legadas sem prefixo ainda mapeiam apenas para id:).
  • channels.msteams.sharePointSiteId: ID do site SharePoint para uploads de arquivo em chats de grupo/canais (veja Envio de arquivos em chats de grupo).

Roteamento e sessões

  • As chaves de sessão seguem o formato padrão de agente (veja /concepts/session):
    • Mensagens diretas compartilham a sessão principal (agent:<agentId>:<mainKey>).
    • Mensagens de canal/grupo usam ID de conversa:
      • agent:<agentId>:msteams:channel:<conversationId>
      • agent:<agentId>:msteams:group:<conversationId>

Estilo de resposta: threads vs posts

O Teams introduziu recentemente dois estilos de UI de canal sobre o mesmo modelo de dados subjacente:

EstiloDescriçãoreplyStyle recomendado
Posts (clássico)Mensagens aparecem como cards com respostas em thread abaixothread (padrão)
Threads (tipo Slack)Mensagens fluem linearmente, mais parecido com o Slacktop-level

O problema: A API do Teams não expõe qual estilo de UI um canal usa. Se você usar o replyStyle errado:

  • thread em um canal estilo Threads → respostas aparecem aninhadas de forma estranha
  • top-level em um canal estilo Posts → respostas aparecem como posts separados de nível superior em vez de na thread

Solução: Configure replyStyle por canal com base em como o canal está configurado:

{
  "msteams": {
    "replyStyle": "thread",
    "teams": {
      "19:[email protected]": {
        "channels": {
          "19:[email protected]": {
            "replyStyle": "top-level"
          }
        }
      }
    }
  }
}

Anexos e imagens

Limitações atuais:

  • DMs: Imagens e anexos de arquivo funcionam via APIs de arquivo de bot do Teams.
  • Canais/grupos: Anexos ficam no armazenamento M365 (SharePoint/OneDrive). O payload do webhook inclui apenas um stub HTML, não os bytes reais do arquivo. Permissões de Graph API são obrigatórias para baixar anexos de canal.

Sem permissões do Graph, mensagens de canal com imagens são recebidas como apenas texto (o conteúdo da imagem não é acessível ao bot). Por padrão, o OpenClaw só baixa mídia de hostnames da Microsoft/Teams. Sobrescreva com channels.msteams.mediaAllowHosts (use ["*"] para permitir qualquer host). Headers de Authorization são anexados apenas para hosts em channels.msteams.mediaAuthAllowHosts (padrão: hosts Graph + Bot Framework). Mantenha essa lista restrita (evite sufixos multi-tenant).

Envio de arquivos em chats de grupo

Bots podem enviar arquivos em DMs usando o fluxo FileConsentCard (integrado). No entanto, enviar arquivos em chats de grupo/canais requer configuração adicional:

ContextoComo os arquivos são enviadosConfiguração necessária
DMsFileConsentCard → usuário aceita → bot enviaFunciona sem configuração adicional
Chats de grupo/canaisUpload para SharePoint → link de compartilhamentoRequer sharePointSiteId + permissões do Graph
Imagens (qualquer contexto)Base64 inlineFunciona sem configuração adicional

Por que chats de grupo precisam do SharePoint

Bots não têm um drive OneDrive pessoal (o endpoint /me/drive da Graph API não funciona para identidades de aplicativo). Para enviar arquivos em chats de grupo/canais, o bot faz upload para um site SharePoint e cria um link de compartilhamento.

Configuração

  1. Adicione permissões de Graph API no Entra ID (Azure AD) → App Registration:

    • Sites.ReadWrite.All (Application) - upload de arquivos para o SharePoint
    • Chat.Read.All (Application) - opcional, permite links de compartilhamento por usuário
  2. Conceda consentimento de admin para o tenant.

  3. Obtenha o ID do site SharePoint:

    # Via Graph Explorer ou curl com um token válido:
    curl -H "Authorization: Bearer $TOKEN" \
      "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
    
    # Exemplo: para um site em "contoso.sharepoint.com/sites/BotFiles"
    curl -H "Authorization: Bearer $TOKEN" \
      "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
    
    # A resposta inclui: "id": "contoso.sharepoint.com,guid1,guid2"
  4. Configure o OpenClaw:

    {
      channels: {
        msteams: {
          // ... other config ...
          sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",
        },
      },
    }

Comportamento de compartilhamento

PermissãoComportamento de compartilhamento
Apenas Sites.ReadWrite.AllLink de compartilhamento organizacional (qualquer pessoa na org pode acessar)
Sites.ReadWrite.All + Chat.Read.AllLink de compartilhamento por usuário (apenas membros do chat podem acessar)

O compartilhamento por usuário é mais seguro, pois apenas os participantes do chat podem acessar o arquivo. Se a permissão Chat.Read.All estiver ausente, o bot recorre ao compartilhamento organizacional.

Comportamento de fallback

CenárioResultado
Chat de grupo + arquivo + sharePointSiteId configuradoUpload para SharePoint, envia link de compartilhamento
Chat de grupo + arquivo + sem sharePointSiteIdTenta upload OneDrive (pode falhar), envia apenas texto
Chat pessoal + arquivoFluxo FileConsentCard (funciona sem SharePoint)
Qualquer contexto + imagemBase64 inline (funciona sem SharePoint)

Local de armazenamento dos arquivos

Arquivos enviados são armazenados em uma pasta /OpenClawShared/ na biblioteca de documentos padrão do site SharePoint configurado.

Enquetes (Adaptive Cards)

O OpenClaw envia enquetes do Teams como Adaptive Cards (não existe API nativa de enquete do Teams).

  • CLI: openclaw message poll --channel msteams --target conversation:<id> ...
  • Votos são registrados pelo gateway em ~/.openclaw/msteams-polls.json.
  • O gateway deve permanecer online para registrar votos.
  • As enquetes ainda não publicam resumos de resultados automaticamente (inspecione o arquivo de armazenamento se necessário).

Adaptive Cards (arbitrários)

Envie qualquer JSON de Adaptive Card para usuários ou conversas do Teams usando a ferramenta message ou CLI.

O parâmetro card aceita um objeto JSON de Adaptive Card. Quando card é fornecido, o texto da mensagem é opcional.

Ferramenta do agente:

{
  "action": "send",
  "channel": "msteams",
  "target": "user:<id>",
  "card": {
    "type": "AdaptiveCard",
    "version": "1.5",
    "body": [{ "type": "TextBlock", "text": "Hello!" }]
  }
}

CLI:

openclaw message send --channel msteams \
  --target "conversation:19:[email protected]" \
  --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'

Veja a documentação de Adaptive Cards para esquema e exemplos de cards. Para detalhes de formato de alvo, veja Formatos de alvo abaixo.

Formatos de alvo

Alvos do MSTeams usam prefixos para distinguir entre usuários e conversas:

Tipo de alvoFormatoExemplo
Usuário (por ID)user:<aad-object-id>user:40a1a0ed-4ff2-4164-a219-55518990c197
Usuário (por nome)user:<display-name>user:John Smith (requer Graph API)
Grupo/canalconversation:<conversation-id>conversation:19:[email protected]
Grupo/canal (bruto)<conversation-id>19:[email protected] (se contém @thread)

Exemplos CLI:

# Enviar para um usuário por ID
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"

# Enviar para um usuário por nome de exibição (aciona lookup na Graph API)
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"

# Enviar para um chat de grupo ou canal
openclaw message send --channel msteams --target "conversation:19:[email protected]" --message "Hello"

# Enviar um Adaptive Card para uma conversa
openclaw message send --channel msteams --target "conversation:19:[email protected]" \
  --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'

Exemplos da ferramenta do agente:

{
  "action": "send",
  "channel": "msteams",
  "target": "user:John Smith",
  "message": "Hello!"
}
{
  "action": "send",
  "channel": "msteams",
  "target": "conversation:19:[email protected]",
  "card": {
    "type": "AdaptiveCard",
    "version": "1.5",
    "body": [{ "type": "TextBlock", "text": "Hello" }]
  }
}

Observação: sem o prefixo user:, nomes são resolvidos como grupo/team. Sempre use user: ao direcionar pessoas pelo nome de exibição.

Mensagens proativas

  • Mensagens proativas só são possíveis após o usuário ter interagido, porque armazenamos referências de conversa nesse momento.
  • Veja /gateway/configuration para controles de dmPolicy e lista de permitidos.

IDs de team e canal (armadilha comum)

O parâmetro de query groupId nas URLs do Teams NÃO é o ID do team usado para configuração. Extraia os IDs do caminho da URL:

URL do team:

https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
                                    └────────────────────────────┘
                                    Team ID (decodifique a URL)

URL do canal:

https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
                                      └─────────────────────────┘
                                      Channel ID (decodifique a URL)

Para configuração:

  • Team ID = segmento do caminho após /team/ (decodificado, ex.: 19:[email protected])
  • Channel ID = segmento do caminho após /channel/ (decodificado)
  • Ignore o parâmetro de query groupId

Canais privados

Bots têm suporte limitado em canais privados:

RecursoCanais padrãoCanais privados
Instalação do botSimLimitado
Mensagens em tempo real (webhook)SimPode não funcionar
Permissões RSCSimPode se comportar diferente
@mençõesSimSe o bot estiver acessível
Histórico via Graph APISimSim (com permissões)

Soluções se canais privados não funcionarem:

  1. Use canais padrão para interações com o bot
  2. Use DMs - usuários sempre podem enviar mensagens diretamente ao bot
  3. Use Graph API para acesso histórico (requer ChannelMessage.Read.All)

Solução de problemas

Problemas comuns

  • Imagens não aparecem em canais: permissões do Graph ou consentimento de admin ausentes. Reinstale o app Teams e feche/reabra o Teams completamente.
  • Sem respostas em canal: menções são obrigatórias por padrão; defina channels.msteams.requireMention=false ou configure por team/canal.
  • Incompatibilidade de versão (Teams ainda mostra manifesto antigo): remova + re-adicione o app e feche o Teams completamente para atualizar.
  • 401 Unauthorized do webhook: Esperado ao testar manualmente sem JWT do Azure - significa que o endpoint é acessível mas a autenticação falhou. Use Azure Web Chat para testar corretamente.

Erros de upload do manifesto

  • “Icon file cannot be empty”: O manifesto referencia arquivos de ícone que têm 0 bytes. Crie ícones PNG válidos (32x32 para outline.png, 192x192 para color.png).
  • “webApplicationInfo.Id already in use”: O app ainda está instalado em outro team/chat. Encontre e desinstale primeiro, ou aguarde 5-10 minutos para propagação.
  • “Something went wrong” no upload: Faça upload via https://admin.teams.microsoft.com, abra o DevTools do navegador (F12) → aba Network e verifique o corpo da resposta para o erro real.
  • Sideload falhando: Tente “Upload an app to your org’s app catalog” em vez de “Upload a custom app” - isso frequentemente contorna restrições de sideload.

Permissões RSC não funcionando

  1. Verifique se webApplicationInfo.id corresponde exatamente ao App ID do bot
  2. Re-faça upload do app e reinstale no team/chat
  3. Verifique se o admin da organização bloqueou permissões RSC
  4. Confirme que está usando o escopo correto: ChannelMessage.Read.Group para teams, ChatMessage.Read.Chat para chats de grupo

Referências