Documentation Index
Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt
Use this file to discover all available pages before exploring further.
Status: anexos de texto + DM são compatíveis; o envio de arquivos em canais/grupos exige sharePointSiteId + permissões do Graph (consulte Envio de arquivos em chats de grupo). Enquetes são enviadas por Adaptive Cards. As ações de mensagem expõem upload-file explícito para envios com arquivo em primeiro lugar.
Plugin incluído
O Microsoft Teams é distribuído como um plugin incluído nas versões atuais do OpenClaw, então nenhuma
instalação separada é necessária na build empacotada normal.
Se você estiver em uma build mais antiga ou em uma instalação personalizada que exclui o Teams incluído,
instale o pacote npm diretamente:
openclaw plugins install @openclaw/msteams
Use o pacote simples para acompanhar a tag de lançamento oficial atual. Fixe uma versão exata
somente quando precisar de uma instalação reproduzível.
Checkout local (ao executar a partir de um repositório git):
openclaw plugins install ./path/to/local/msteams-plugin
Detalhes: Plugins
Configuração rápida
O @microsoft/teams.cli lida com o registro do bot, a criação do manifesto e a geração de credenciais em um único comando.
1. Instale e faça login
npm install -g @microsoft/teams.cli@preview
teams login
teams status # verify you're logged in and see your tenant info
A Teams CLI está atualmente em preview. Comandos e flags podem mudar entre versões.
2. Inicie um túnel (o Teams não consegue acessar localhost)
Instale e autentique a CLI devtunnel se ainda não tiver feito isso (guia de introdução).
# One-time setup (persistent URL across sessions):
devtunnel create my-openclaw-bot --allow-anonymous
devtunnel port create my-openclaw-bot -p 3978 --protocol auto
# Each dev session:
devtunnel host my-openclaw-bot
# Your endpoint: https://<tunnel-id>.devtunnels.ms/api/messages
--allow-anonymous é obrigatório porque o Teams não consegue autenticar com devtunnels. Cada solicitação de bot recebida ainda é validada automaticamente pelo Teams SDK.
Alternativas: ngrok http 3978 ou tailscale funnel 3978 (mas elas podem alterar URLs a cada sessão).
3. Crie o app
teams app create \
--name "OpenClaw" \
--endpoint "https://<your-tunnel-url>/api/messages"
Este único comando:
- Cria uma aplicação Entra ID (Azure AD)
- Gera um segredo de cliente
- Cria e envia um manifesto de app do Teams (com ícones)
- Registra o bot (gerenciado pelo Teams por padrão - nenhuma assinatura do Azure necessária)
A saída mostrará CLIENT_ID, CLIENT_SECRET, TENANT_ID e um ID do App do Teams - anote-os para as próximas etapas. Ela também oferece instalar o app diretamente no Teams.
4. Configure o OpenClaw usando as credenciais da saída:
{
channels: {
msteams: {
enabled: true,
appId: "<CLIENT_ID>",
appPassword: "<CLIENT_SECRET>",
tenantId: "<TENANT_ID>",
webhook: { port: 3978, path: "/api/messages" },
},
},
}
Ou use variáveis de ambiente diretamente: MSTEAMS_APP_ID, MSTEAMS_APP_PASSWORD, MSTEAMS_TENANT_ID.
5. Instale o app no Teams
teams app create solicitará que você instale o app - selecione “Instalar no Teams”. Se você pulou essa etapa, pode obter o link depois:
teams app get <teamsAppId> --install-link
6. Verifique se tudo funciona
teams app doctor <teamsAppId>
Isso executa diagnósticos no registro do bot, na configuração do app AAD, na validade do manifesto e na configuração de SSO.
Para implantações de produção, considere usar autenticação federada (certificado ou identidade gerenciada) em vez de segredos de cliente.
Chats de grupo são bloqueados por padrão (channels.msteams.groupPolicy: "allowlist"). Para permitir respostas em grupos, defina channels.msteams.groupAllowFrom ou use groupPolicy: "open" para permitir qualquer membro (controlado por menção).
Objetivos
- Falar com o OpenClaw via DMs, chats de grupo ou canais do Teams.
- Manter o roteamento determinístico: as respostas sempre retornam ao canal em que chegaram.
- Usar por padrão um comportamento seguro de canal (menções obrigatórias, a menos que configurado de outra forma).
Gravações de configuração
Por padrão, o Microsoft Teams tem permissão para gravar atualizações de configuração acionadas por /config set|unset (requer commands.config: true).
Desative com:
{
channels: { msteams: { configWrites: false } },
}
Controle de acesso (DMs + grupos)
Acesso por DM
- 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 ou grupos estáticos de acesso de remetentes, como accessGroup:core-team.
- Não dependa de correspondência por UPN/nome de exibição para allowlists - eles podem mudar. O OpenClaw desativa a correspondência direta por nome por padrão; habilite explicitamente com
channels.msteams.dangerouslyAllowNameMatching: true.
- O assistente pode resolver nomes para IDs via Microsoft Graph quando as credenciais permitirem.
Acesso de grupo
- Padrão:
channels.msteams.groupPolicy = "allowlist" (bloqueado, a menos que você adicione groupAllowFrom). Use channels.defaults.groupPolicy para substituir o padrão quando não definido.
channels.msteams.groupAllowFrom controla quais remetentes ou grupos estáticos de acesso de remetentes podem acionar em chats/canais de grupo (recorre a channels.msteams.allowFrom).
- Defina
groupPolicy: "open" para permitir qualquer membro (ainda controlado por menção por padrão).
- Para permitir nenhum canal, defina
channels.msteams.groupPolicy: "disabled".
Exemplo:
{
channels: {
msteams: {
groupPolicy: "allowlist",
groupAllowFrom: ["00000000-0000-0000-0000-000000000000", "accessGroup:core-team"],
},
},
}
Teams + allowlist de canais
- Restrinja respostas de grupo/canal listando equipes e canais em
channels.msteams.teams.
- As chaves devem usar IDs de conversa estáveis do Teams a partir de links do Teams, não nomes de exibição mutáveis.
- Quando
groupPolicy="allowlist" e uma allowlist de equipes estiver presente, somente equipes/canais listados são aceitos (controlado 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 equipes/canais e de allowlist de usuários para IDs (quando as permissões do Graph permitirem)
e registra o mapeamento; nomes de equipes/canais não resolvidos são mantidos como digitados, mas ignorados para roteamento por padrão, a menos que
channels.msteams.dangerouslyAllowNameMatching: true esteja habilitado.
Exemplo:
{
channels: {
msteams: {
groupPolicy: "allowlist",
teams: {
"My Team": {
channels: {
General: { requireMention: true },
},
},
},
},
},
}
Autenticação federada (certificado mais identidade gerenciada)
Adicionado em 2026.4.11
Para implantações de produção, o OpenClaw oferece suporte à autenticação federada como uma alternativa mais segura aos segredos de cliente. Dois métodos estão disponíveis:
Opção A: Autenticação baseada em certificado
Use um certificado PEM registrado com o registro do seu app Entra ID.
Configuração:
- Gere ou obtenha um certificado (formato PEM com chave privada).
- Em Entra ID → Registro de App → Certificados e segredos → Certificados → carregue o certificado público.
Configuração:
{
channels: {
msteams: {
enabled: true,
appId: "<APP_ID>",
tenantId: "<TENANT_ID>",
authType: "federated",
certificatePath: "/path/to/cert.pem",
webhook: { port: 3978, path: "/api/messages" },
},
},
}
Variáveis de ambiente:
MSTEAMS_AUTH_TYPE=federated
MSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem
Opção B: Identidade Gerenciada do Azure
Use a Identidade Gerenciada do Azure para autenticação sem senha. Isso é ideal para implantações em infraestrutura do Azure (AKS, App Service, VMs do Azure) onde uma identidade gerenciada está disponível.
Como funciona:
- O pod/VM do bot tem uma identidade gerenciada (atribuída pelo sistema ou atribuída pelo usuário).
- Uma credencial de identidade federada vincula a identidade gerenciada ao registro do app Entra ID.
- Em tempo de execução, o OpenClaw usa
@azure/identity para adquirir tokens do endpoint IMDS do Azure (169.254.169.254).
- O token é passado ao Teams SDK para autenticação do bot.
Pré-requisitos:
- Infraestrutura do Azure com identidade gerenciada habilitada (identidade de workload do AKS, App Service, VM)
- Credencial de identidade federada criada no registro do app Entra ID
- Acesso de rede ao IMDS (
169.254.169.254:80) a partir do pod/VM
Configuração (identidade gerenciada atribuída pelo sistema):
{
channels: {
msteams: {
enabled: true,
appId: "<APP_ID>",
tenantId: "<TENANT_ID>",
authType: "federated",
useManagedIdentity: true,
webhook: { port: 3978, path: "/api/messages" },
},
},
}
Configuração (identidade gerenciada atribuída pelo usuário):
{
channels: {
msteams: {
enabled: true,
appId: "<APP_ID>",
tenantId: "<TENANT_ID>",
authType: "federated",
useManagedIdentity: true,
managedIdentityClientId: "<MI_CLIENT_ID>",
webhook: { port: 3978, path: "/api/messages" },
},
},
}
Variáveis de ambiente:
MSTEAMS_AUTH_TYPE=federated
MSTEAMS_USE_MANAGED_IDENTITY=true
MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id> (somente para atribuída pelo usuário)
Configuração de identidade de carga de trabalho do AKS
Para implantações do AKS usando identidade de carga de trabalho:
-
Habilite a identidade de carga de trabalho no cluster AKS.
-
Crie uma credencial de identidade federada no registro do aplicativo do Entra ID:
az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{
"name": "my-bot-workload-identity",
"issuer": "<AKS_OIDC_ISSUER_URL>",
"subject": "system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT>",
"audiences": ["api://AzureADTokenExchange"]
}'
-
Anote a conta de serviço do Kubernetes com o ID do cliente do aplicativo:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-bot-sa
annotations:
azure.workload.identity/client-id: "<APP_CLIENT_ID>"
-
Rotule o pod para injeção de identidade de carga de trabalho:
metadata:
labels:
azure.workload.identity/use: "true"
-
Garanta acesso de rede ao IMDS (
169.254.169.254): se estiver usando NetworkPolicy, adicione uma regra de egresso permitindo tráfego para 169.254.169.254/32 na porta 80.
Comparação de tipos de autenticação
| Método | Configuração | Vantagens | Desvantagens |
|---|
| Segredo do cliente | appPassword | Configuração simples | Exige rotação de segredo, menos seguro |
| Certificado | authType: "federated" + certificatePath | Nenhum segredo compartilhado pela rede | Sobrecarga de gerenciamento de certificados |
| Identidade gerenciada | authType: "federated" + useManagedIdentity | Sem senha, sem segredos para gerenciar | Infraestrutura do Azure necessária |
Comportamento padrão: Quando authType não está definido, o OpenClaw usa autenticação por segredo do cliente por padrão. Configurações existentes continuam funcionando sem alterações.
Desenvolvimento local (tunelamento)
O Teams não consegue acessar localhost. Use um túnel de desenvolvimento persistente para que a URL permaneça a mesma entre sessões:
# One-time setup:
devtunnel create my-openclaw-bot --allow-anonymous
devtunnel port create my-openclaw-bot -p 3978 --protocol auto
# Each dev session:
devtunnel host my-openclaw-bot
Alternativas: ngrok http 3978 ou tailscale funnel 3978 (as URLs podem mudar a cada sessão).
Se a URL do túnel mudar, atualize o endpoint:
teams app update <teamsAppId> --endpoint "https://<new-url>/api/messages"
Testando o bot
Executar diagnósticos:
teams app doctor <teamsAppId>
Verifica o registro do bot, o aplicativo AAD, o manifesto e a configuração de SSO em uma única passagem.
Enviar uma mensagem de teste:
- Instale o aplicativo do Teams (use o link de instalação de
teams app get <id> --install-link)
- Encontre o bot no Teams e envie uma DM
- Verifique os logs do Gateway para atividade recebida
Variáveis de ambiente
Todas as chaves de configuração também podem ser definidas por variáveis de ambiente:
MSTEAMS_APP_ID
MSTEAMS_APP_PASSWORD
MSTEAMS_TENANT_ID
MSTEAMS_AUTH_TYPE (opcional: "secret" ou "federated")
MSTEAMS_CERTIFICATE_PATH (federado + certificado)
MSTEAMS_CERTIFICATE_THUMBPRINT (opcional, não necessário para autenticação)
MSTEAMS_USE_MANAGED_IDENTITY (federado + identidade gerenciada)
MSTEAMS_MANAGED_IDENTITY_CLIENT_ID (somente MI atribuída pelo usuário)
O OpenClaw expõe uma ação member-info baseada no Graph para Microsoft Teams para que agentes e automações possam resolver detalhes de membros do canal (nome de exibição, email, função) diretamente pelo Microsoft Graph.
Requisitos:
- Permissão RSC
Member.Read.Group (já no manifesto recomendado)
- Para consultas entre equipes: permissão de Aplicativo Graph
User.Read.All com consentimento de administrador
A ação é controlada por channels.msteams.actions.memberInfo (padrão: habilitada quando credenciais do Graph estão disponíveis).
Contexto de histórico
channels.msteams.historyLimit controla quantas mensagens recentes de canal/grupo são encapsuladas no prompt.
- Recorre a
messages.groupChat.historyLimit. Defina 0 para desabilitar (padrão 50).
- O histórico de conversa buscado é filtrado por listas de permissão de remetentes (
allowFrom / groupAllowFrom), portanto a semeadura de contexto da conversa inclui apenas mensagens de remetentes permitidos.
- O contexto de anexos citados (
ReplyTo* derivado do HTML de resposta do Teams) atualmente é passado como recebido.
- Em outras palavras, listas de permissão controlam quem pode acionar o agente; hoje, somente caminhos específicos de contexto suplementar são filtrados.
- O histórico de DM pode ser limitado com
channels.msteams.dmHistoryLimit (turnos do usuário). Substituições por usuário: channels.msteams.dms["<user_id>"].historyLimit.
Permissões RSC atuais do Teams (manifesto)
Estas são as resourceSpecific permissions existentes no manifesto do nosso aplicativo do Teams. Elas se aplicam apenas dentro da equipe/chat em que o aplicativo está instalado.
Para canais (escopo de equipe):
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 em grupo:
ChatMessage.Read.Chat (Application) - receber todas as mensagens de chat em grupo sem @menção
Para adicionar permissões RSC via Teams CLI:
teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type Application
Exemplo de manifesto do Teams (redigido)
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 ID do Aplicativo Azure Bot.
webApplicationInfo.id deve corresponder ao ID do Aplicativo Azure Bot.
bots[].scopes deve incluir as superfícies que você planeja usar (personal, team, groupChat).
bots[].supportsFiles: true é obrigatório para manipulação de arquivos no escopo pessoal.
authorization.permissions.resourceSpecific deve incluir leitura/envio de canal se você quiser tráfego de canal.
Atualizando um aplicativo existente
Para atualizar um aplicativo do Teams já instalado (por exemplo, para adicionar permissões RSC):
# Download, edit, and re-upload the manifest
teams app manifest download <teamsAppId> manifest.json
# Edit manifest.json locally...
teams app manifest upload manifest.json <teamsAppId>
# Version is auto-bumped if content changed
Após atualizar, reinstale o aplicativo em cada equipe para que as novas permissões entrem em vigor e encerre completamente e reinicie o Teams (não apenas feche a janela) para limpar metadados do aplicativo em cache.
Capacidades: somente RSC vs Graph
Funciona:
- Ler conteúdo de texto de mensagens do canal.
- Enviar conteúdo de texto de mensagens do canal.
- Receber anexos de arquivos pessoais (DM).
Não funciona:
- Conteúdo de imagens ou arquivos de canal/grupo (a carga útil inclui apenas um stub HTML).
- Baixar anexos armazenados no SharePoint/OneDrive.
- Ler histórico de mensagens (além do evento Webhook ao vivo).
Adiciona:
- Baixar conteúdos hospedados (imagens coladas em mensagens).
- Baixar anexos de arquivos armazenados no SharePoint/OneDrive.
- Ler histórico de mensagens de canal/chat via Graph.
RSC vs API Graph
| Capacidade | Permissões RSC | API Graph |
|---|
| Mensagens em tempo real | Sim (via Webhook) | Não (somente polling) |
| Mensagens históricas | Não | Sim (pode consultar histórico) |
| Complexidade de configuração | Somente manifesto do aplicativo | Exige consentimento de administrador + fluxo de token |
| Funciona offline | Não (precisa estar em execução) | Sim (consulte a qualquer momento) |
Resumo: RSC é para escuta em tempo real; a API Graph é para acesso histórico. Para recuperar mensagens perdidas enquanto estava offline, você precisa da API Graph com ChannelMessage.Read.All (exige consentimento de administrador).
Mídia + histórico habilitados pelo Graph (obrigatório para canais)
Se você precisa de imagens/arquivos em canais ou quer buscar histórico de mensagens, deve habilitar permissões do Microsoft Graph e conceder consentimento de administrador.
- No Registro de Aplicativo do Entra ID (Azure AD), adicione permissões de Aplicativo do Microsoft Graph:
ChannelMessage.Read.All (anexos de canal + histórico)
Chat.Read.All ou ChatMessage.Read.All (chats em grupo)
- Conceda consentimento de administrador para o tenant.
- Incremente a versão do manifesto do aplicativo do Teams, reenvie e reinstale o aplicativo no Teams.
- Encerre completamente e reinicie o Teams para limpar metadados do aplicativo em cache.
Permissão adicional para menções de usuários: Menções @ a usuários funcionam imediatamente para usuários na conversa. No entanto, se você quiser pesquisar 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 administrador.
Limitações conhecidas
Timeouts de Webhook
O Teams entrega mensagens via Webhook HTTP. Se o processamento demorar demais (por exemplo, respostas lentas de LLM), você pode ver:
- Timeouts do Gateway
- O Teams tentando enviar a mensagem novamente (causando duplicatas)
- Respostas descartadas
OpenClaw lida com isso retornando rapidamente e enviando respostas proativamente, mas respostas muito lentas ainda podem causar problemas.
O markdown do Teams é mais limitado que o do Slack ou Discord:
- A formatação básica funciona: negrito, itálico,
code, links
- Markdown complexo (tabelas, listas aninhadas) pode não ser renderizado corretamente
- Adaptive Cards são compatíveis com enquetes e envios de apresentação semântica (veja abaixo)
Configuração
Principais configurações (veja /gateway/configuration para padrões de canais compartilhados):
channels.msteams.enabled: habilita/desabilita 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 permissões de DMs (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: alternância de emergência para reabilitar correspondência mutável de UPN/nome de exibição e roteamento direto por nome de equipe/canal.
channels.msteams.textChunkLimit: tamanho do bloco 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 divisão por tamanho.
channels.msteams.mediaAllowHosts: lista de permissões para hosts de anexos de entrada (o padrão são domínios Microsoft/Teams).
channels.msteams.mediaAuthAllowHosts: lista de permissões para anexar cabeçalhos Authorization em novas tentativas de mídia (o padrão são hosts Graph + Bot Framework).
channels.msteams.requireMention: exige @mention em canais/grupos (padrão true).
channels.msteams.replyStyle: thread | top-level (veja Estilo de resposta).
channels.msteams.teams.<teamId>.replyStyle: substituição por equipe.
channels.msteams.teams.<teamId>.requireMention: substituição por equipe.
channels.msteams.teams.<teamId>.tools: substituições padrão de política de ferramentas por equipe (allow/deny/alsoAllow) usadas quando falta uma substituição de canal.
channels.msteams.teams.<teamId>.toolsBySender: substituições padrão de política de ferramentas por remetente e por equipe (curinga "*" compatível).
channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: substituição por canal.
channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: substituição por canal.
channels.msteams.teams.<teamId>.channels.<conversationId>.tools: substituições de política de ferramentas por canal (allow/deny/alsoAllow).
channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: substituições de política de ferramentas por remetente e por canal (curinga "*" compatível).
- As chaves de
toolsBySender devem usar prefixos explícitos:
channel:, id:, e164:, username:, name: (chaves legadas sem prefixo ainda são mapeadas apenas para id:).
channels.msteams.actions.memberInfo: habilita ou desabilita a ação de informações de membro apoiada pelo Graph (padrão: habilitada quando credenciais do Graph estão disponíveis).
channels.msteams.authType: tipo de autenticação - "secret" (padrão) ou "federated".
channels.msteams.certificatePath: caminho para o arquivo de certificado PEM (autenticação federada + certificado).
channels.msteams.certificateThumbprint: impressão digital do certificado (opcional, não necessária para autenticação).
channels.msteams.useManagedIdentity: habilita autenticação de identidade gerenciada (modo federado).
channels.msteams.managedIdentityClientId: ID do cliente para identidade gerenciada atribuída pelo usuário.
channels.msteams.sharePointSiteId: ID do site do SharePoint para uploads de arquivos em chats de grupo/canais (veja Enviando 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 o ID da conversa:
agent:<agentId>:msteams:channel:<conversationId>
agent:<agentId>:msteams:group:<conversationId>
Estilo de resposta: tópicos vs postagens
O Teams introduziu recentemente dois estilos de interface de canal sobre o mesmo modelo de dados subjacente:
| Estilo | Descrição | replyStyle recomendado |
|---|
| Postagens (clássico) | As mensagens aparecem como cartões com respostas encadeadas abaixo | thread (padrão) |
| Tópicos (semelhante ao Slack) | As mensagens fluem linearmente, mais como no Slack | top-level |
O problema: A API do Teams não expõe qual estilo de interface um canal usa. Se você usar o replyStyle errado:
thread em um canal no estilo Tópicos → as respostas aparecem aninhadas de forma estranha
top-level em um canal no estilo Postagens → as respostas aparecem como postagens separadas de nível superior em vez de dentro do tópico
Solução: Configure replyStyle por canal com base em como o canal está configurado:
{
channels: {
msteams: {
replyStyle: "thread",
teams: {
"19:abc...@thread.tacv2": {
channels: {
"19:xyz...@thread.tacv2": {
replyStyle: "top-level",
},
},
},
},
},
},
}
Precedência de resolução
Quando o bot envia uma resposta para um canal, replyStyle é resolvido da substituição mais específica até o padrão. O primeiro valor que não seja undefined vence:
- Por canal —
channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle
- Por equipe —
channels.msteams.teams.<teamId>.replyStyle
- Global —
channels.msteams.replyStyle
- Padrão implícito — derivado de
requireMention:
requireMention: true → thread
requireMention: false → top-level
Se você definir requireMention: false globalmente sem um replyStyle explícito, menções em canais no estilo Postagens aparecerão como postagens de nível superior mesmo quando a entrada foi uma resposta em tópico. Fixe replyStyle: "thread" no nível global, de equipe ou de canal para evitar surpresas.
Preservação do contexto do tópico
Quando replyStyle: "thread" está em vigor e o bot foi @mentioned dentro de um tópico de canal, o OpenClaw reanexa a raiz do tópico original à referência de conversa de saída (19:…@thread.tacv2;messageid=<root>) para que a resposta chegue ao mesmo tópico. Isso vale tanto para envios ao vivo (durante o turno) quanto para envios proativos feitos depois que o contexto de turno do Bot Framework expirou (por exemplo, agentes de longa duração, respostas de chamadas de ferramenta enfileiradas via mcp__openclaw__message).
A raiz do tópico é obtida do threadId armazenado na referência de conversa. Referências armazenadas mais antigas que são anteriores ao threadId recorrem a activityId (qualquer atividade de entrada que tenha inicializado a conversa por último), portanto implantações existentes continuam funcionando sem uma nova inicialização.
Quando replyStyle: "top-level" está em vigor, entradas de tópicos de canal são respondidas intencionalmente como novas postagens de nível superior — nenhum sufixo de tópico é anexado. Esse é o comportamento correto para canais no estilo Tópicos; se você vir postagens de nível superior onde esperava respostas encadeadas, seu replyStyle está configurado incorretamente para esse canal.
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 da Graph API são necessárias para baixar anexos de canal.
- Para envios explícitos priorizando arquivo, use
action=upload-file com media / filePath / path; message opcional vira o texto/comentário acompanhante, e filename substitui o nome enviado.
Sem permissões do Graph, mensagens de canal com imagens serão recebidas apenas como texto (o conteúdo da imagem não é acessível ao bot).
Por padrão, o OpenClaw só baixa mídia de hostnames Microsoft/Teams. Substitua com channels.msteams.mediaAllowHosts (use ["*"] para permitir qualquer host).
Cabeçalhos Authorization só são anexados para hosts em channels.msteams.mediaAuthAllowHosts (o padrão são hosts Graph + Bot Framework). Mantenha essa lista estrita (evite sufixos multi-tenant).
Enviando 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 exige configuração adicional:
| Contexto | Como os arquivos são enviados | Configuração necessária |
|---|
| DMs | FileConsentCard → usuário aceita → bot faz upload | Funciona imediatamente |
| Chats de grupo/canais | Upload para SharePoint → link de compartilhamento | Exige sharePointSiteId + permissões do Graph |
| Imagens (qualquer contexto) | Inline codificado em Base64 | Funciona imediatamente |
Por que chats de grupo precisam do SharePoint
Bots não têm uma unidade pessoal do OneDrive (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 do SharePoint e cria um link de compartilhamento.
Configuração
-
Adicione permissões da Graph API no Entra ID (Azure AD) → Registro de aplicativo:
Sites.ReadWrite.All (Application) - enviar arquivos para o SharePoint
Chat.Read.All (Application) - opcional, habilita links de compartilhamento por usuário
-
Conceda consentimento de administrador para o tenant.
-
Obtenha o ID do seu site do SharePoint:
# Via Graph Explorer or curl with a valid token:
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
# Example: for a site at "contoso.sharepoint.com/sites/BotFiles"
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
# Response includes: "id": "contoso.sharepoint.com,guid1,guid2"
-
Configure o OpenClaw:
{
channels: {
msteams: {
// ... other config ...
sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",
},
},
}
Comportamento de compartilhamento
| Permissão | Comportamento de compartilhamento |
|---|
Apenas Sites.ReadWrite.All | Link de compartilhamento para toda a organização (qualquer pessoa na organização pode acessar) |
Sites.ReadWrite.All + Chat.Read.All | Link 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 para toda a organização.
Comportamento de fallback
| Cenário | Resultado |
|---|
Chat de grupo + arquivo + sharePointSiteId configurado | Upload para SharePoint, envia link de compartilhamento |
Chat de grupo + arquivo + sem sharePointSiteId | Tenta upload para OneDrive (pode falhar), envia apenas texto |
| Chat pessoal + arquivo | Fluxo FileConsentCard (funciona sem SharePoint) |
| Qualquer contexto + imagem | Inline codificado em Base64 (funciona sem SharePoint) |
Local de armazenamento dos arquivos
Os arquivos enviados são armazenados em uma pasta /OpenClawShared/ na biblioteca de documentos padrão do site do SharePoint configurado.
Enquetes (Adaptive Cards)
O OpenClaw envia enquetes do Teams como Adaptive Cards (não há API nativa de enquetes do Teams).
- CLI:
openclaw message poll --channel msteams --target conversation:<id> ...
- Os 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 automaticamente resumos de resultados (inspecione o arquivo de armazenamento se necessário).
Cartões de apresentação
Envie cargas úteis de apresentação semântica para usuários ou conversas do Teams usando a ferramenta message ou a CLI. O OpenClaw as renderiza como Teams Adaptive Cards a partir do contrato genérico de apresentação.
O parâmetro presentation aceita blocos semânticos. Quando presentation é fornecido, o texto da mensagem é opcional.
Ferramenta de agente:
{
action: "send",
channel: "msteams",
target: "user:<id>",
presentation: {
title: "Hello",
blocks: [{ type: "text", text: "Hello!" }],
},
}
CLI:
openclaw message send --channel msteams \
--target "conversation:19:abc...@thread.tacv2" \
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'
Para detalhes sobre o formato de destino, veja Formatos de destino abaixo.
Os destinos MSTeams usam prefixos para distinguir entre usuários e conversas:
| Tipo de destino | Formato | Exemplo |
|---|
| 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/canal | conversation:<conversation-id> | conversation:19:abc123...@thread.tacv2 |
| Grupo/canal (bruto) | <conversation-id> | 19:abc123...@thread.tacv2 (se contiver @thread) |
Exemplos de CLI:
# Send to a user by ID
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"
# Send to a user by display name (triggers Graph API lookup)
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"
# Send to a group chat or channel
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
# Send a presentation card to a conversation
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello"}]}'
Exemplos da ferramenta de agente:
{
action: "send",
channel: "msteams",
target: "user:John Smith",
message: "Hello!",
}
{
action: "send",
channel: "msteams",
target: "conversation:19:abc...@thread.tacv2",
presentation: {
title: "Hello",
blocks: [{ type: "text", text: "Hello" }],
},
}
Sem o prefixo user:, os nomes usam por padrão a resolução de grupo ou equipe. Sempre use user: ao direcionar para pessoas pelo nome de exibição.
Mensagens proativas
- Mensagens proativas só são possíveis depois que um usuário interage, porque armazenamos referências de conversa nesse momento.
- Veja
/gateway/configuration para dmPolicy e controle por lista de permissões.
IDs de equipe e canal (pegadinha comum)
O parâmetro de consulta groupId nas URLs do Teams NÃO é o ID da equipe usado para configuração. Extraia os IDs do caminho da URL em vez disso:
URL da equipe:
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
└────────────────────────────┘
Team conversation ID (URL-decode this)
URL do canal:
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
└─────────────────────────┘
Channel ID (URL-decode this)
Para configuração:
- Chave de equipe = segmento do caminho após
/team/ (decodificado da URL, por exemplo, 19:Bk4j...@thread.tacv2; tenants mais antigos podem mostrar @thread.skype, que também é válido)
- Chave de canal = segmento do caminho após
/channel/ (decodificado da URL)
- Ignore o parâmetro de consulta
groupId para roteamento do OpenClaw. Ele é o ID de grupo do Microsoft Entra, não o ID de conversa do Bot Framework usado em atividades recebidas do Teams.
Canais privados
Bots têm suporte limitado em canais privados:
| Recurso | Canais padrão | Canais privados |
|---|
| Instalação do bot | Sim | Limitada |
| Mensagens em tempo real (webhook) | Sim | Pode não funcionar |
| Permissões RSC | Sim | Podem se comportar de forma diferente |
| @mentions | Sim | Se o bot estiver acessível |
| Histórico do Graph API | Sim | Sim (com permissões) |
Soluções alternativas se canais privados não funcionarem:
- Use canais padrão para interações com o bot
- Use DMs - os usuários sempre podem enviar mensagens diretamente para o bot
- Use Graph API para acesso histórico (requer
ChannelMessage.Read.All)
Solução de problemas
Problemas comuns
- Imagens não aparecem nos canais: permissões do Graph ou consentimento de administrador ausentes. Reinstale o app do Teams e feche totalmente/reabra o Teams.
- Sem respostas no canal: mentions são obrigatórias por padrão; defina
channels.msteams.requireMention=false ou configure por equipe/canal.
- Incompatibilidade de versão (o Teams ainda mostra o manifesto antigo): remova e adicione novamente o app, depois feche totalmente o Teams para atualizar.
- 401 Unauthorized do webhook: esperado ao testar manualmente sem Azure JWT - significa que o endpoint está acessível, mas a autenticação falhou. Use o Azure Web Chat para testar corretamente.
Erros de upload do manifesto
- “Icon file cannot be empty”: o manifesto referencia arquivos de ícone com 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 outra equipe/chat. Encontre-o e desinstale-o primeiro, ou aguarde 5 a 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 ver o erro real.
- Falha no sideload: tente “Upload an app to your org’s app catalog” em vez de “Upload a custom app” - isso muitas vezes contorna restrições de sideload.
Permissões RSC não funcionam
- Verifique se
webApplicationInfo.id corresponde exatamente ao App ID do seu bot
- Faça upload novamente do app e reinstale-o na equipe/chat
- Verifique se o administrador da sua organização bloqueou permissões RSC
- Confirme que você está usando o escopo correto:
ChannelMessage.Read.Group para equipes, ChatMessage.Read.Chat para chats em grupo
Referências
Relacionado