Architecture

Replace Teams Incoming Webhooks with Workflows after connector retirement

Office 365 Connectors in Teams stopped working in May 2026. Rebuild channel alerts with a Workflows webhook, send Adaptive Cards and secure who can post.

13 min read
On this page

To replace a Teams incoming webhook, create a workflow in the Teams Workflows app from a template such as Send webhook alerts to a channel, copy the new webhook URL it generates, and point your monitoring tool or script at that URL with a JSON body in the {"type":"message","attachments":[...]} Adaptive Card format. Office 365 Connectors in Teams stopped working when Microsoft completed the retirement rollout in May 2026, so every old webhook.office.com connector URL has to be replaced this way (or with a bot or Microsoft Graph). The new webhook runs as a Power Automate flow, which means it has an owner, a trigger security setting and Power Automate limits that the old connector never had.

Who this is for and what you will have at the end

This guide is for administrators and developers whose Teams channel alerts went silent after the connector retirement: build pipelines, monitoring systems, Azure alerts, ticketing tools or PowerShell scripts that used to post to an Incoming Webhook connector. At the end you will have:

  • A working Workflows webhook for each channel or chat that receives alerts.
  • Sender code (PowerShell and curl) that posts a valid Adaptive Card payload.
  • A deliberate choice of who can call the webhook, with token-based calls where needed.
  • Co-owners on each workflow so alerts survive staff changes.
  • A troubleshooting list for the errors people actually hit during the migration.

What changed and what the replacement looks like

Microsoft first announced the retirement of Office 365 Connectors within Teams in July 2024 and extended the deadline several times while Workflows gained missing features. The final update scheduled the shutdown rollout to begin on May 18, 2026 and complete on May 22, 2026, after which Office 365 Connectors no longer function. Microsoft's stated replacement is Power Automate workflows, created through the Workflows app in Teams; the earlier updates also name a Teams app (bot) or Microsoft Graph as alternatives.

The replacement trigger is When a Teams webhook request is received (operation ID TeamsIncomingWebhookTrigger) in the Microsoft Teams connector. It exposes a URL that accepts POST requests only. The templates pair it with an action that posts the card from the request into the chat or channel you choose.

AreaOld Incoming Webhook connectorWorkflows webhook
Where it livesConfigured on the channelA Power Automate flow owned by a user
Who can call itAnyone with the URLAnyone, any user in your tenant, or specific users
Card formatsMessageCard, actionable messagesAdaptive Cards and Message Cards (no Message Card buttons, no actionable messages)
Sender identity in TeamsConnector name and iconFlow bot (no custom name or icon) or the owner as a user
LicensingIncludedTemplates and trigger don't require a premium licence
LimitsConnector limitsPower Automate request limits and Teams connector throttling

The ownership difference is the one that causes the most surprises later. A workflow is linked to its owner, not to the team or channel. If that owner leaves and nobody else co-owns the flow, it can become orphaned and stop posting.

Prerequisites

Before you start, check the following:

  • The Workflows app is allowed. The Teams connector reference states that the posting actions require the Workflows (formerly Power Automate) app to be available and set to allow in the Teams admin center.
  • A sensible owner account. The person or service account that creates the workflow becomes its owner. Plan to add at least one co-owner.
  • An inventory of old webhook senders. List every system that posted to a webhook.office.com URL, the channel it targeted and whether its cards used buttons.
  • Government cloud check. The Flow bot poster option is available only in commercial tenants. In GCC, GCC High and DoD tenants, use the User poster option instead.
  • A test tool. PowerShell 7, curl or Postman is enough to send test requests.

Step 1: Create the webhook workflow from a template

Microsoft's support article lists six webhook templates, three for chats and three for channels:

  • Send webhook alerts to a chat, Send webhook alerts from specific people to a chat, Send webhook alerts from people in an org to a chat
  • Send webhook alerts to a channel, Send webhook alerts from specific people to a channel, Send webhook alerts from people in an org to a channel

Each template uses a different authentication type, so pick the template that matches who will call the webhook (see Step 3). To create the workflow from the channel itself:

  1. In Teams, go to the team and channel that should receive the alerts.
  2. Select More options (...) next to the channel, then select Workflows.
  3. Search for Send webhook alerts to a channel (or one of the variants) and select it.
  4. Configure the parameters, including the team and channel to post to.
  5. Select Save.
  6. On the workflow's details page, copy the webhook URL.

You can also open the Workflows app from any entry point and choose the template there. If you need to find the URL again later, open the flow in the Power Automate designer, in either Power Automate or Teams, and look at the trigger card. Treat the URL as a secret, because with the Anyone option it is the only thing protecting the channel.

Create one workflow per destination channel or chat. Reusing a single workflow for many unrelated alert sources makes ownership, throttling and troubleshooting harder.

Step 2: Convert the payload to the Workflows format

The trigger accepts two request body schemas: a collection of Adaptive Cards, or a single Message Card. For anything new, use the Adaptive Card format. The rules from the connector reference are:

  • type must be message.
  • attachments is an array of card objects.
  • Each attachment's contentType must be application/vnd.microsoft.card.adaptive, and contentUrl should be null.
  • content holds the Adaptive Card JSON itself.

A complete alert payload looks like this:

{
  "type": "message",
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.card.adaptive",
      "contentUrl": null,
      "content": {
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "type": "AdaptiveCard",
        "version": "1.4",
        "body": [
          {
            "type": "TextBlock",
            "text": "Backup job failed on SQL01",
            "weight": "bolder",
            "size": "medium",
            "wrap": true
          },
          {
            "type": "FactSet",
            "facts": [
              { "title": "Server", "value": "SQL01.contoso.com" },
              { "title": "Severity", "value": "High" },
              { "title": "Time (UTC)", "value": "2026-10-11T06:15:00Z" }
            ]
          }
        ],
        "actions": [
          {
            "type": "Action.OpenUrl",
            "title": "Open runbook",
            "url": "https://wiki.contoso.com/runbooks/sql-backup"
          }
        ]
      }
    }
  ]
}

A few constraints are worth designing for from the start:

  • Size. When the action posts a message, the limit is approximately 28 KB including text, images, links, tables and mentions. Larger messages fail with Request Entity too large.
  • Card version. Teams supports Adaptive Card features up to version 1.6 for bot-sent cards, and the Teams mobile app supports cards up to 1.6. Staying at or below 1.6 avoids rendering differences on phones.
  • Buttons. Message Card buttons, including HttpPost actions, are not rendered by Workflows. Use Adaptive Card actions such as Action.OpenUrl instead. The trigger also does not support actionable messages.
  • Mentions. A single message can @mention up to 20 users and 20 tags.

If a third-party product can only emit the old MessageCard JSON, it can still post through Workflows, but plan to replace any buttons with links in the card text.

Step 3: Decide who can trigger the workflow

The trigger has a triggerAuthenticationType setting with three options, and the template you chose sets it for you:

OptionWhen to use itWhat the caller sends
AnyoneThird-party SaaS tools that can only POST to a URLNo authentication header at all
Any user in my tenantYour own scripts and services that can get an Entra ID tokenA bearer token from your tenant
Specific users in my tenantA known set of users or service principalsA bearer token whose object ID is on the allowed list

With Anyone, Microsoft is explicit that you must not pass an authentication token header, or the POST fails. With the other two options, requests must carry a token with these claims: aud (the Power Automate audience for your cloud), iss, tid, and for the specific-users option, oid. The public cloud audience is https://service.flow.microsoft.com/ and it must match exactly, including the trailing slash. For a daemon or script, add the object ID of its service principal to the allowed users; Power Automate's documentation states that service principal object IDs are accepted there. If you leave the allowed users list empty, the scope falls back to the whole tenant.

Step 4: Update the senders

For a sender that uses the Anyone option, a PowerShell call is a straight POST:

$webhookUrl = '<paste the URL from the workflow details page>'
 
$card = @{
    type        = 'message'
    attachments = @(
        @{
            contentType = 'application/vnd.microsoft.card.adaptive'
            contentUrl  = $null
            content     = @{
                '$schema' = 'http://adaptivecards.io/schemas/adaptive-card.json'
                type      = 'AdaptiveCard'
                version   = '1.4'
                body      = @(
                    @{ type = 'TextBlock'; text = 'Disk space below 10% on FS01'; wrap = $true }
                )
            }
        }
    )
}
 
$json = $card | ConvertTo-Json -Depth 20
Invoke-RestMethod -Method Post -Uri $webhookUrl -ContentType 'application/json' -Body $json

Always pass -Depth: ConvertTo-Json defaults to 2 levels, and an Adaptive Card payload is nested deeper than that. The same request with curl:

curl -X POST "$WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d @alert-card.json

For Any user in my tenant or Specific users in my tenant, the sender first gets an access token. With an app registration and the client credentials flow, request a token for the Power Automate resource. Because that resource identifier ends in a slash, Microsoft's scope guidance says the .default scope needs a double slash:

$tenantId = '<tenant ID>'
$body = @{
    client_id     = '<app (client) ID>'
    client_secret = '<client secret>'
    scope         = 'https://service.flow.microsoft.com//.default'
    grant_type    = 'client_credentials'
}
$token = Invoke-RestMethod -Method Post `
    -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" `
    -ContentType 'application/x-www-form-urlencoded' -Body $body
 
$secureToken = ConvertTo-SecureString $token.access_token -AsPlainText -Force
Invoke-RestMethod -Method Post -Uri $webhookUrl -ContentType 'application/json' `
    -Authentication Bearer -Token $secureToken -Body $json

Decode the token once (Microsoft suggests pasting it into jwt.io for inspection) and confirm that aud is exactly https://service.flow.microsoft.com/ and tid is your tenant before you blame the workflow. Prefer a certificate over a client secret for long-running services.

If you are also chasing failures in other flows that use HTTP triggers, the URL change covered in Fixing Power Automate HTTP trigger URLs after the logic.azure.com retirement applies to Teams webhook triggers too.

Step 5: Add co-owners and plan for throttling

Because a workflow belongs to its owner, add at least one co-owner straight after creation. Admins can do this for flows that are already orphaned from the Power Platform admin center (select the environment, then Resources > Flows, select the flow and select Share) or with the admin cmdlets in the Microsoft.PowerApps.Administration.PowerShell module:

Get-AdminFlowOwnerRole -EnvironmentName '<environment ID>' -FlowName '<flow ID>'
 
Set-AdminFlowOwnerRole -EnvironmentName '<environment ID>' -FlowName '<flow ID>' `
    -PrincipalType User -RoleName CanEdit -PrincipalObjectId '<new owner object ID>'

Then check volume against the documented limits. The webhook trigger follows the Power Automate runtime endpoint limits for the owner's performance profile: about 1,000 concurrent inbound calls and 45,000 invoke calls per 5 minutes (4,500 on the Low profile). The tighter constraint is often the Teams connector: Flow bot operations are limited to 25 non-GET requests per connection per 300 seconds. A noisy monitoring system that sends hundreds of alerts during an incident will hit that limit. Aggregate alerts at the source, or batch several findings into one card.

Verify the migration

For each migrated alert source:

  1. Send a test request and confirm that the card appears in the right channel or chat, posted by the Flow bot or the owner.
  2. Open the workflow's run history in Power Automate and confirm the run succeeded.
  3. View the card on Teams desktop and mobile to catch layout or version problems.
  4. Confirm the workflow has a co-owner.
  5. Remove the old connector URL from the sender's configuration so nobody mistakes a dead URL for a working one.

Troubleshooting

Request Entity too large. The message exceeded the roughly 28 KB limit. Trim long text, remove embedded base64 images and link to details instead.

BotNotInConversationRoster - The bot is not part of the conversation roster. This appears when the Flow bot poster option is used in a GCC, GCC High or DoD tenant. Switch the post action to the User poster option.

POST fails as soon as you add an Authorization header. The trigger is set to Anyone, which rejects requests that include a token. Remove the header, or switch the trigger to a tenant option and send a valid token.

Calls fail with a token present. Check the aud claim first; it must match the audience for your cloud exactly, including the trailing slash. Then check tid, and for the specific-users option, that the caller's oid is in the allowed list.

Buttons from the old card are missing. Message Card buttons are not rendered by Workflows. Rebuild the card as an Adaptive Card with Action.OpenUrl links.

Posting to a private channel fails. Microsoft's retirement post says private channels are supported through the Power Automate portal and were rolling out to the Teams Workflows app, while the connector reference still lists posting to a private channel as a known issue. Test with your target channel before cutover; if it fails, post to a standard or shared channel.

"Something went wrong, please try again" when submitting a card. This is a documented issue when the post action is combined with the When someone responds to an adaptive card trigger. Use Post adaptive card and wait for a response instead.

Alerts stop weeks after migration. Check whether the owner left or their account was disabled, then reassign ownership as shown in Step 5.

Checklist

  • Workflows app allowed in the Teams admin center.
  • One workflow per destination, created from the template that matches the caller.
  • Payloads converted to the type: message plus Adaptive Card attachments format, under 28 KB.
  • Trigger option chosen deliberately; token-based callers send the correct audience.
  • Co-owners added to every workflow.
  • Alert volume checked against the Flow bot limit of 25 requests per 300 seconds.
  • Old connector URLs removed from every sender.

References

Questions people ask

Do Office 365 Connector incoming webhooks still work in Teams?

No. Microsoft's final retirement update scheduled the shutdown rollout for May 18 to May 22, 2026, after which Office 365 Connectors no longer function. Alerts must be sent to a workflow built on the When a Teams webhook request is received trigger, or to a bot or Microsoft Graph instead.

Does the Teams webhook workflow need a Power Automate Premium licence?

Microsoft's support article states that the webhook templates and the Teams webhook trigger don't require a premium licence. Normal Power Automate request limits for the owner's performance profile still apply, so high-volume senders should check the throttling limits.

Can a Workflows webhook post MessageCard payloads?

Yes, Workflows accepts both Adaptive Card and Message Card formats, but buttons in Message Cards, such as HttpPost actions, are not rendered. If your old connector cards had interactive buttons, convert them to Adaptive Cards.

Why does my webhook workflow return an error when I send a bearer token?

If the trigger is set to Anyone, requests must not include an authentication token header or they fail. Only the Any user in my tenant and Specific users in my tenant options expect a token, and that token must carry the Power Automate audience for your cloud.

Microsoft TeamsPower AutomateWorkflowsAdaptive Cards
  1. Build a multi-stage approval workflow with Power Automate and SharePoint

    Automate manager and finance approvals on SharePoint list items with the Power Automate Approvals connector, write decisions back to the list, and handle Teams, timeouts and the 30-day limit.

    Architecture13 min read
  2. Power Automate error handling: try-catch scopes, run after and alerts

    Make Power Automate cloud flows fail gracefully: retry policies, Try, Catch and Finally scopes, result() to find the failed action, Teams alerts, error logging and a correct final run status.

    Architecture12 min read
  3. Connect Business Central to Outlook, Teams and SharePoint in Microsoft 365

    Set up Business Central email, the Outlook add-in, the Teams app, read-only access with Microsoft 365 licenses, OneDrive, and SharePoint storage for document attachments.

    Architecture13 min read