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.
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ício | Como reconhecer | Ação recomendada |
---|---|---|
URL do webhook | Começa com https://outlook.office.com/webhook/ ou possui /IncomingWebhook/ . | Planejar migração para URL gerado por Power Automate. |
Payload | Usa "@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 URL | Seu 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
Tema | O que muda | Recomendação prática |
---|---|---|
URL do webhook | O365: 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. |
Payload | Workflows 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ção | Deixa 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ça | O 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 atual | Compatibilidade | Estratégia |
---|---|---|
Texto simples/HTML | Diretamente 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 Card | Suportado 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
- Criar o fluxo no Power Automate com o gatilho When an HTTP request is received. Copie a URL gerada.
- Definir o esquema do JSON de entrada (use um sample com seu payload existente).
- Adicionar ação “Post message in Teams” (texto/HTML) ou “Post adaptive card” (para cartões).
- Opcional: resolver
teamId
echannelId
porchannelKey
(tabela/mapping) no fluxo. - Encerrar com “Response” informando
200
/202
e um corpo padrão ({"ok":true}
). - Atualizar o plugin para ler o novo webhook de configuração segura e publicar o mesmo payload.
- 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
- Trigger: When an HTTP request is received (schema com
channelKey
,type
,payload
emeta
). - Validação: checa
X-Signature
(se aplicável) e tamanho do corpo. - Lookup: obtém
teamId
/channelId
porchannelKey
. - Switch: por
type
:- text → “Post message in Teams”.
- adaptive → “Post adaptive card”.
- html → “Post message in Teams” com corpo HTML.
- 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)
Aspecto | O365 Connector | Workflow (Power Automate) |
---|---|---|
Provisão | Webhook criado no canal do Teams | URL criada ao adicionar o gatilho HTTP no fluxo |
Formato de URL | Base fixa em outlook.office.com + IDs | URL única por fluxo/ambiente com sig embutido |
Autenticação | Token implícito na URL do conector | Assinatura (sig ) + validações adicionais que você implementar |
Payload | Texto/HTML e MessageCard (legado) | Texto/HTML e Adaptive Cards (recomendado) |
Endereçamento | URL já “aponta” para um canal específico | Você decide o canal dentro do fluxo (flexibilidade para dispatcher) |
Manutenção | Troca no canal | Governanç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:
- Trigger HTTP (gera URL pública do fluxo).
- Obter
teamId
/channelId
porchannelKey
(tabela simples). - Post message in Teams (mensagem =
payload.message
). - 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:
- Trigger HTTP (schema com
payload
). - “Post adaptive card in a chat or channel” → selecionar Post in a channel.
- 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 peloid
. - 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
- Criar o fluxo com o gatilho HTTP e copiar a URL.
- Adicionar a ação do Teams (mensagem ou Adaptive Card).
- Configurar mapeamento
channelKey → teamId/channelId
(se for dispatcher). - Guardar a URL completa em cofre/variável de ambiente.
- Adaptar o plugin para ler a nova URL e publicar o payload existente.
- Testar unitário, integração e carga; monitorar.
- 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).