Webhook Post To Microsoft Teams

In the not too distant past, sending a webhook to a Microsoft Teams channel was pretty simple. You could click the 3 dots next to a channel name, select the webhook option, grab the URL, and you were basically good to go. That method has, unfortunately, gone by the wayside, as the official documentation will tell you (at least at the time of this writing… I imagine this page will eventually go away, and the deprecation notice with it.) The new method to set up a webhook to post to a channel is through “Workflows“, which in turn tap into Power Automate. The aptly named Power Automate certainly does have a lot of power, but it also is a lot more complicated, something that means simple tasks, like setting up a webhook, can become much more difficult when compared to the handful of button clicks required previously.

While following the official documentation above, I ran into all sorts of issues where things just weren’t clear. Given that this process has changed over time, there’s also a ton of bad information online. Humorously enough, nowhere did I get worse information than from Microsoft’s own Copilot chat bot within our Office 365 instance… go figure. Documenting it here will hopefully help someone else from banging there head against the process in the future when you just need to have HTTP POSTs to an endpoint end up as messages within a Teams channel.

One important thing to note is that I was trying to do this without requiring any further rights than what I already have (I’m just a normal user in our Office 365 instance, not an admin.) It’s also worth mentioning, since there are several different formats available, that I was trying to post content into the channel as an Adaptive Card. That’ll be discussed in more detail later, but the process would be slightly different for other schema options. The Adaptive Card seems to offer the advantage of the best customization and formatting options, hence why I chose it.

The first step is to create the Workflow itself in Power Automate. While Microsoft will instruct you on how to do this within Teams, I found it much easier to just go to the Power Automate portal directly. From there I created a new Flow and selected the “Send webhook alerts to a channel” option for the template.

This will create a flow with two pieces.

  1. The URL where the webhook is received.
  2. The mechanism that actually creates the post in teams.

For the first piece, there’s nothing else required beyond copying the URL down for testing later. Just be careful with it because the code passed in as a URL parameter controls access to the webhook. You don’t need to set up any further authentication within Entra, but this also means that if anyone gets the URL they’ll be able to send data to Teams.

Tons of stuff online will outline the need for a middle step between these two for parsing the JSON. Since the Adaptive Card is a known format to Microsoft, though, that step isn’t necessary.

Clicking on the second stage within the Flow editor will show the various options for what to do with the webhook content. This is where you configure which Team and channel should receive the messages. Note that for this to work for me, I had to set the Post as value to User. If I set it to be the generic Flowbot, it would not work for me. Content online suggested that the channel simply needed to have the Power Automate and/or Workflows apps added to it, but it already did. Other content suggested the bot needed to be added as a member of the team. None of the methods for adding the bot were available to me, so I’m not completely sure if the information was bad or if it was a limitation of my rights (or lack thereof) in the system. Regardless, going with User works fine as long as you’re okay with the messages looking like they’re being sent by your own account. In my case, I was fine with this.

The one curious piece was what should go into the Adaptive Card field. After some trial and error with the pieces available, I eventually struck on success by inserting the value of “Attachments Adaptive Card”. I was able to select this by typing a / in the field and seeing all of the available options. This is what lets Teams know an Adaptive Card is expected and alleviates the need for any type of parsing. It’ll also change the overall Flow slightly in that the second step will become a For each loop. This allows for multiple cards to be sent at one time, and it’ll make more sense when we check out the payload next.

That was all the work needed from the Power Automate side, so next I just needed to test it out with something like Postman. I wasn’t sure the format of the payload for an Adaptive Card, though. Fortunately, it’s pretty well documented, and I actually found Copilot to do a decent job of highlighting the various options supported for formatting the text with different font colors, sizes, and weights. A sample would be:

{
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.card.adaptive",
      "content": {
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "type": "AdaptiveCard",
        "version": "1.4",
        "body": [
          {
            "type": "TextBlock",
            "text": "Alert Heading!",
            "wrap": true,
            "horizontalAlignment": "Center",
            "size": "Large",
            "weight": "Bolder"
          },
          {
            "type": "TextBlock",
            "text": "This is the alert content.",
            "wrap": true
          }
        ]
    }
}

Note how the entire payload exists within a single top-level key of attachments. That’s how you could include multiple cards at once. In my use case, that wasn’t necessary. But it’s worth keeping in mind that the option is available. Testing with this showed me the expected message in Teams:

Should the message fail to appear, leaving the Flow editor and going back to the main page of the Flow will show the run history:

This list updates in near real time and offers good insight into what’s going on. If a run fails, clicking on that instance in the history will provide more details like any error messages and where in the flow the problem occurred. The only thing which drove me crazy is that the UI for this looks extremely similar to the UI for editing a Flow… but making edits in it isn’t possible. You just have to remember where the problem was, go back to the main page for the Flow, and then select to edit it from there.