Migração de webhooks do O365 Connector para Workflows (Power Automate) no Microsoft Teams: guia completo, exemplos e melhores práticas

Migre com segurança seus webhooks do O365 Connector para Workflows (Power Automate) no Microsoft Teams. Este guia prático explica as diferenças de URL, como reaproveitar o payload e três modelos de integração para evitar criar um endpoint por fluxo, com exemplos em JSON, Node.js, C# e PowerShell.

Índice

Panorama da mudança e impacto para plugins

O conector “Office 365 Connector” (O365) foi descontinuado em 1.º de outubro de 2024. Se o seu plugin ou integração ainda publica mensagens em canais do Microsoft Teams usando URLs do tipo https://outlook.office.com/webhook/…, você precisa migrar para webhooks via Workflow usando Power Automate (gatilho When an HTTP request is received) ou outra abordagem moderna (por exemplo, Graph API). Este artigo foca no caminho com Power Automate, por exigir baixo esforço e preservar a lógica atual do seu produto.

Diagnóstico rápido: como saber se você ainda usa O365 Connector

IndícioComo reconhecerAção recomendada
URL do webhookComeça com https://outlook.office.com/webhook/ ou possui /IncomingWebhook/.Planejar migração para URL gerado por Power Automate.
PayloadUsa "@type": "MessageCard" (cartões legados) ou um texto simples.Converter para Adaptive Card ou enviar texto/HTML via ação do Teams no fluxo.
Composição da URLSeu código “monta” a URL a partir de partes (tenant, grupo, webhook).Trocar para armazenar a URL completa e única por fluxo.

O que efetivamente muda

TemaO que mudaRecomendação prática
URL do webhookO365: base fixa + IDs de tenant, grupo e webhook.
Workflow (Power Automate): URL completa gerada automaticamente, ex.:
https://prod-23.westeurope.logic.azure.com/workflows/{GUID}/triggers/Manual/paths/invoke?...&sig={hash}
Não existe “base URL” pública nem formato reconstruível só com o tenant.
Armazene a URL completa em configuração segura (cofre de segredos, variável de ambiente, Key Vault). Não tente compor a URL.
PayloadWorkflows aceitam JSON livre. Se o fluxo usar a ação “Post message in Teams” ou “Post adaptive card…”, o corpo compatível continua sendo texto/HTML ou Adaptive Cards.Testar o payload atual. Se você usa MessageCard (legado), migre para Adaptive Cards v1.6+.
Modelo de integraçãoDeixa de existir o padrão “GroupId + WebhookId → URL”. Cada fluxo tem sua própria URL.Ofereça gestão centralizada de URLs de fluxo no seu produto; em fase de transição, aceite que o usuário cole a URL completa.
SegurançaO parâmetro sig= é um token de acesso vinculado ao fluxo.Trate a URL como segredo (criptografia em repouso e em trânsito). Não exponha em logs, prints ou telas de suporte.

Entendendo a estrutura do novo webhook (Power Automate)

A URL do gatilho HTTP em Power Automate é similar às de Logic Apps e inclui:

  • Região/ambiente (ex.: prod-23.westeurope)
  • ID do workflow ({GUID})
  • Rota do gatilho (/triggers/Manual/paths/invoke)
  • Querystring com api-version, sp, sv, sig etc.

Você não consegue deduzir essa URL a partir do tenant, teamId ou channelId. Ela precisa ser armazenada integralmente, como faria com uma string de conexão.

Reaproveitando o payload: texto, HTML, MessageCard e Adaptive Card

O que você envia hoje define o esforço de migração:

Formato atualCompatibilidadeEstratégia
Texto simples/HTMLDiretamente aceito pela ação “Post message in Teams”.Enviar o mesmo corpo; opcionalmente enriquecer com menções e formatação.
MessageCard (legado)Não é aceito nativamente pela ação de Adaptive Card.Converter para Adaptive Card equivalente no plugin ou no fluxo.
Adaptive CardSuportado pelas ações “Post adaptive card (vX)”.Manter o JSON e evoluir para v1.6+ conforme necessidade.

Exemplo de conversão: MessageCard → Adaptive Card

Mensagem simples em MessageCard:

{
  "@type": "MessageCard",
  "@context": "http://schema.org/extensions",
  "summary": "Build",
  "title": "Build concluído",
  "text": "Pipeline #8421 terminou com sucesso."
}

Versão equivalente em Adaptive Card (1.6):

{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.6",
  "body": [
    { "type": "TextBlock", "size": "Large", "weight": "Bolder", "text": "Build concluído" },
    { "type": "TextBlock", "wrap": true, "text": "Pipeline #8421 terminou com sucesso." }
  ]
}

Três modelos de integração (sem multiplicar endpoints)

Modelo A — Direto por canal (simples, porém vários endpoints)

Um fluxo por canal de destino. O plugin armazena uma URL por canal. Indicado para poucas equipes/canais e migração rápida.

Modelo B — Fluxo “dispatcher” (um endpoint para tudo)

Crie um único fluxo com o gatilho HTTP. O plugin envia um channelKey (no caminho, query ou cabeçalho). O fluxo faz lookup do canal de destino e publica a mensagem via ação do Teams.

  • Entrada sugerida: { "channelKey": "financeiro-pagamentos", "type": "adaptive", "payload": {…} }
  • Resolução de canais: Tabela (Dataverse, SharePoint, JSON em variável de ambiente) mapeando channelKey → teamId, channelId.
  • Vantagens: um único webhook, configuração gerenciável e rotação simples.

Modelo C — Orquestração por “child flows”

O gatilho HTTP chama child flows (ou ações condicionais) por área/rota. Útil quando a lógica por canal é significativamente diferente (aprovações, anexos, menções, etc.).

Segurança e governança

  • Trate a URL como segredo: o parâmetro sig= é um token de acesso. Use cofres/segredos e nunca registre a URL em logs.
  • Rotação: ao suspeitar de vazamento, gere um novo gatilho/URL e substitua no cofre e no plugin.
  • Validação de origem (opcional): além da URL, adicione um cabeçalho X-Signature com hash do corpo (p.ex., HMAC computado no seu backend) e valide no fluxo (com função auxiliar ou serviço intermediário).
  • Política de dados: evite enviar PII desnecessária. Remova segredos do payload.
  • Governança no Power Automate: publique em Solution, use Environment Variables e DLP para controlar conectores.

Passo a passo de migração

  1. Criar o fluxo no Power Automate com o gatilho When an HTTP request is received. Copie a URL gerada.
  2. Definir o esquema do JSON de entrada (use um sample com seu payload existente).
  3. Adicionar ação “Post message in Teams” (texto/HTML) ou “Post adaptive card” (para cartões).
  4. Opcional: resolver teamId e channelId por channelKey (tabela/mapping) no fluxo.
  5. Encerrar com “Response” informando 200/202 e um corpo padrão ({"ok":true}).
  6. Atualizar o plugin para ler o novo webhook de configuração segura e publicar o mesmo payload.
  7. Testar cenários, monitorar falhas e reduzir gradualmente o suporte ao O365 Connector.

Exemplos práticos

Schema de entrada para fluxo “dispatcher”

Use um sample como este para gerar o schema automaticamente:

{
  "channelKey": "financeiro-pagamentos",
  "type": "adaptive",
  "payload": {
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.6",
    "body": [
      { "type": "TextBlock", "text": "Pagamento aprovado", "size": "Large", "weight": "Bolder" },
      { "type": "TextBlock", "text": "NF 123456", "isSubtle": true }
    ]
  },
  "meta": {
    "eventId": "7e8f6a84-3a7b-4ed0-9d2f-1d2d9f0b1a22",
    "source": "erp-xpto"
  }
}

Node.js (axios) publicando no novo webhook

import axios from "axios";

async function postToTeamsWebhook(url, body) {
// url = armazenada em cofre/variável de ambiente
try {
const res = await axios.post(url, body, {
headers: {
"Content-Type": "application/json",
"X-Channel-Key": body.channelKey || ""
},
timeout: 10000
});
return res.status;
} catch (err) {
// Evite logar a URL completa
console.error("Falha ao enviar para Teams:", err.response?.status, err.message);
throw err;
}
}

// Exemplo de uso
const workflowUrl = process.env.TEAMSWORKFLOWURL;
const payload = {
channelKey: "financeiro-pagamentos",
type: "adaptive",
payload: {
"$schema": "[http://adaptivecards.io/schemas/adaptive-card.json](http://adaptivecards.io/schemas/adaptive-card.json)",
"type": "AdaptiveCard",
"version": "1.6",
"body": [
{ "type": "TextBlock", "text": "Pagamento aprovado", "size": "Large", "weight": "Bolder" },
{ "type": "TextBlock", "text": "NF 123456", "isSubtle": true }
]
}
};

postToTeamsWebhook(workflowUrl, payload).then((code) => {
console.log("Status:", code);
}); 

C# (.NET) com HttpClient

using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class TeamsClient
{
private readonly HttpClient _http;
```
public TeamsClient(HttpClient http) => _http = http;

public async Task<int> PostAsync(string workflowUrl, object body)
{
    var json = JsonSerializer.Serialize(body);
    using var content = new StringContent(json, Encoding.UTF8, "application/json");
    var res = await _http.PostAsync(workflowUrl, content);
    return (int)res.StatusCode;
}
```
} 

PowerShell (Invoke-RestMethod)

$url = $env:TEAMSWORKFLOWURL
$body = @{
  channelKey = "financeiro-pagamentos"
  type = "text"
  payload = @{
    message = "Fechamento diário concluído ✅"
  }
} | ConvertTo-Json -Depth 10

Invoke-RestMethod -Method Post -Uri $url -Body $body -ContentType "application/json" 

Transformando MessageCard para Adaptive Card no servidor

Se seu plugin gera MessageCard, inclua uma etapa de transformação antes de publicar:

function messageCardToAdaptive(messageCard) {
  const title = messageCard.title || messageCard.summary || "Notificação";
  const text = messageCard.text || "";
  const actions = (messageCard.potentialAction || [])
    .filter(a => a['@type'] === 'OpenUri')
    .map(a => ({ type: "Action.OpenUrl", title: a.name, url: a.targets?.[0]?.uri || a.url }));

return {
"$schema": "[http://adaptivecards.io/schemas/adaptive-card.json](http://adaptivecards.io/schemas/adaptive-card.json)",
"type": "AdaptiveCard",
"version": "1.6",
"body": [
{ "type": "TextBlock", "size": "Large", "weight": "Bolder", "text": title },
{ "type": "TextBlock", "wrap": true, "text": text }
],
"actions": actions
};
} 

Fluxo “dispatcher”: desenho de alto nível

  1. Trigger: When an HTTP request is received (schema com channelKey, type, payload e meta).
  2. Validação: checa X-Signature (se aplicável) e tamanho do corpo.
  3. Lookup: obtém teamId/channelId por channelKey.
  4. Switch: por type:
    • text → “Post message in Teams”.
    • adaptive → “Post adaptive card”.
    • html → “Post message in Teams” com corpo HTML.
  5. Response: retorna 200 e {"ok":true,"id":...}.

Boas práticas de resiliência

  • Idempotência: envie meta.eventId único; no fluxo, descarte duplicados (por exemplo, verificando uma lista/tabela simples).
  • Backoff: em 429/5xx, use exponential backoff no plugin.
  • Tempo de resposta: mantenha a lógica do fluxo enxuta; para rotinas pesadas, queue e responda 202.
  • Observabilidade: logue apenas metadados (nunca a URL). Use Run history e alertas por falha.

Comparativo detalhado: O365 Connector × Workflow (Power Automate)

AspectoO365 ConnectorWorkflow (Power Automate)
ProvisãoWebhook criado no canal do TeamsURL criada ao adicionar o gatilho HTTP no fluxo
Formato de URLBase fixa em outlook.office.com + IDsURL única por fluxo/ambiente com sig embutido
AutenticaçãoToken implícito na URL do conectorAssinatura (sig) + validações adicionais que você implementar
PayloadTexto/HTML e MessageCard (legado)Texto/HTML e Adaptive Cards (recomendado)
EndereçamentoURL já “aponta” para um canal específicoVocê decide o canal dentro do fluxo (flexibilidade para dispatcher)
ManutençãoTroca no canalGovernança via Solutions, variáveis e versionamento

Checklist de migração

Antes

  • Levantar todos os webhooks O365 em uso e respectivos canais.
  • Inventariar formatos de payload (texto, MessageCard, Adaptive Card).
  • Definir o modelo (A/B/C) e o repositório de URLs (cofre/variáveis).

Durante

  • Criar o(s) fluxo(s) em Power Automate e coletar as novas URLs.
  • Implementar transformação de MessageCard → Adaptive (se necessário).
  • Atualizar o plugin para ler a nova URL e publicar o payload.
  • Habilitar logs seguros no plugin (sem expor a URL).

Depois

  • Monitorar falhas e latência; ajustar retry e timeouts.
  • Desativar/remover os webhooks O365 legados.
  • Documentar o processo interno e treinar o time de operação.

Exemplo de fluxo minimalista (texto)

Entrada esperada:

{
  "channelKey": "suporte-geral",
  "type": "text",
  "payload": { "message": "Ticket #4321 atribuído a @joao" }
}

Etapas:

  1. Trigger HTTP (gera URL pública do fluxo).
  2. Obter teamId/channelId por channelKey (tabela simples).
  3. Post message in Teams (mensagem = payload.message).
  4. Response 200 com {"ok":true}.

Exemplo de fluxo (Adaptive Card)

Entrada esperada:

{
  "channelKey": "engenharia-alertas",
  "type": "adaptive",
  "payload": {
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.6",
    "body": [
      { "type": "TextBlock", "text": "Alerta de deploy", "size": "Large", "weight": "Bolder" },
      { "type": "FactSet", "facts": [
        { "title": "Serviço", "value": "api-pedidos" },
        { "title": "Ambiente", "value": "prod" },
        { "title": "Status", "value": "sucesso" }
      ]}
    ]
  }
}

Etapas:

  1. Trigger HTTP (schema com payload).
  2. “Post adaptive card in a chat or channel” → selecionar Post in a channel.
  3. Response 200.

Estratégias de armazenamento da URL no plugin

  • Cofre/segredo por ambiente: variável TEAMSWORKFLOWURL por env (dev/homolog/prod). Rotacione via pipeline de configuração.
  • Catálogo centralizado: tabela WebhookEndpoints com id, name, url, owner, lastRotatedUtc. O plugin carrega o endpoint pelo id.
  • Interface de admin: permita colar/substituir a URL completa; mascarar na UI e esconder em logs.

Erros comuns e como evitar

  • Montar a URL a partir do tenant: não funciona com Workflows. Sempre armazene a URL completa.
  • Enviar MessageCard “puro”: converta para Adaptive Card ou publique como texto/HTML.
  • Esquecer o “Response”: alguns clientes aguardam status final; inclua a ação “Response”.
  • Vazar a URL em logs: o parâmetro sig dá acesso. Redija ou oculte.

FAQ

Posso ter um único webhook para vários canais?
Sim. Use o dispatcher (Modelo B) e encaminhe para canais com base em uma chave de rota (channelKey) ou cabeçalho.

Consigo gerar a URL a partir de um código curto?
Não. A URL é gerada pelo Power Automate e não há “base” pública para recompor o endereço.

Meu payload atual vai “funcionar” sem mudanças?
Se for texto/HTML, funciona com a ação de mensagem. Se for MessageCard, converta para Adaptive Card. Se já for Adaptive Card, normalmente é compatível.

Como rotacionar a URL?
Crie um novo gatilho/fluxo (ou regenere a URL se aplicável), atualize o cofre e troque no plugin. Desative a URL antiga.

Roteiro de implementação recomendado

  1. Criar o fluxo com o gatilho HTTP e copiar a URL.
  2. Adicionar a ação do Teams (mensagem ou Adaptive Card).
  3. Configurar mapeamento channelKey → teamId/channelId (se for dispatcher).
  4. Guardar a URL completa em cofre/variável de ambiente.
  5. Adaptar o plugin para ler a nova URL e publicar o payload existente.
  6. Testar unitário, integração e carga; monitorar.
  7. Desligar gradualmente os webhooks O365 anteriores.

Conclusão

Migrar do O365 Connector para Workflows com Power Automate no Microsoft Teams é principalmente uma mudança de endereço (URL) e de modelo (de “URL por canal” para “URL por fluxo”). Ao armazenar a URL completa com segurança, reaproveitar o payload (preferindo Adaptive Cards) e adotar um padrão como o dispatcher, você reduz retrabalho, simplifica governança e garante compatibilidade com a plataforma atual. Siga o checklist e os exemplos acima para uma transição suave e sustentável.


Modelos de payload prontos para copiar

Texto/HTML

{
  "channelKey": "marketing",
  "type": "html",
  "payload": { "message": "<b>Campanha enviada</b> para 12.540 clientes." }
}

Adaptive Card – Alerta

{
  "channelKey": "observabilidade",
  "type": "adaptive",
  "payload": {
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.6",
    "body": [
      { "type": "TextBlock", "text": "CPU alta no cluster k8s", "size": "Large", "weight": "Bolder" },
      { "type": "TextBlock", "text": "Threshold: > 80% por 5 min", "wrap": true }
    ],
    "actions": [
      { "type": "Action.OpenUrl", "title": "Ver painel", "url": "https://exemplo.local/dashboard" }
    ]
  }
}

Adaptive Card – Aprovação simples

{
  "channelKey": "compras",
  "type": "adaptive",
  "payload": {
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.6",
    "body": [
      { "type": "TextBlock", "text": "Aprovar pedido #7789", "size": "Large", "weight": "Bolder" },
      { "type": "FactSet", "facts": [
        { "title": "Fornecedor", "value": "Alpha Ltda" },
        { "title": "Valor", "value": "R$ 12.300,00" }
      ]}
    ]
  }
}

Resumo acionável

  • Troque outlook.office.com/webhook pela URL completa do fluxo (Power Automate).
  • Prefira Adaptive Cards para mensagens ricas no Teams.
  • Adote um dispatcher para manter um único endpoint.
  • Guarde a URL como segredo e implemente rotação.
  • Use Response e códigos HTTP claros (200/202/4xx/5xx).
Índice