Send a Microsoft Teams Notification from a Workflow
The problem
Some workflow results need a human to act on them: an order that couldn't be exported, a stock level below its minimum, a nightly job that found unexpected data. Email works, but many teams coordinate in Microsoft Teams, and a message in the channel where they already work gets seen faster.
The solution
Teams can accept messages from outside through a webhook: a special URL that posts whatever is sent to it into a channel or chat. A Vince Platform workflow can call that URL with a REST API step:
The Transform step builds the message as an Adaptive Card, Microsoft's JSON format for rich messages with headings, lists of facts and buttons. The REST API step sends it.
Older guides use an "Incoming Webhook" connector with URLs on webhook.office.com. Microsoft has retired those connectors. Use the Workflows app in Teams instead, as described below. It gives you a URL on a Power Automate host.
Prerequisites
- A Teams channel or chat where you're allowed to add workflows. Your Teams administrator may need to allow the Workflows app.
- A Vince Platform workflow that produces the information you want to post. This guide uses an order export as the example.
- Familiarity with the REST API and Transform components.
Step 1 — Create the webhook in Teams
In Teams, create a workflow from the template Send webhook alerts to a channel (for a channel) or Send webhook alerts to a chat (for a chat). Depending on your Teams version, it can also appear as Post to a channel when a webhook request is received. Pick the team and channel, then finish the wizard. Teams then shows the webhook URL. Copy it.
Microsoft changes the names of these menus and templates from time to time. For the current steps, see Microsoft's guide Create incoming webhooks with Workflows for Microsoft Teams.
Anyone who has the URL can post into your channel, with no login needed. The URL carries its own signature (the sig= part). Don't paste it into tickets, chat messages or screenshots. If it leaks, delete the workflow in Teams and create a new one; that gives you a new URL.
Step 2 — Build the message with Transform
In this example, a REST API step (rest_api_1) has sent orders to another system, and its body holds the result of that export:
{
"orders": [
{ "orderNumber": "SO-50211", "status": "SENT", "message": "OK" },
{ "orderNumber": "SO-50212", "status": "FAILED", "message": "Unknown customer C-0042" },
{ "orderNumber": "SO-50213", "status": "FAILED", "message": "Warehouse WH9 is closed" }
]
}
Add a Transform step that builds an Adaptive Card listing the orders that failed:
(
$orders := $context.data.all.rest_api_1.body.orders;
$failed := $orders[status != "SENT"];
{
"failedCount": $count($failed),
"request": {
"type": "message",
"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": "Order export: " & $string($count($failed)) & " of " & $string($count($orders)) & " orders failed",
"weight": "Bolder",
"size": "Medium",
"wrap": true
},
{
"type": "FactSet",
"facts": $failed.{ "title": orderNumber, "value": message }[]
}
]
}
}
]
}
}
)
The expression returns two things:
failedCount: used in Step 3 to skip the notification when there's nothing to report.request: the message itself. Its outer wrapper ("type": "message"plus anattachmentslist containing the card) is the format the Teams webhook expects. Inside it:- a TextBlock, the heading
- a FactSet, the list of order number and reason pairs
The [] after the facts mapping keeps facts a list even when only one order failed. Adaptive Cards require a list there.
With the example input, request comes out as:
{
"type": "message",
"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": "Order export: 2 of 3 orders failed", "weight": "Bolder", "size": "Medium", "wrap": true },
{
"type": "FactSet",
"facts": [
{ "title": "SO-50212", "value": "Unknown customer C-0042" },
{ "title": "SO-50213", "value": "Warehouse WH9 is closed" }
]
}
]
}
}
]
}
Design the card in Microsoft's Adaptive Cards Designer first. Paste the card JSON (the part under content) and see a live preview, then move it into your Transform and replace the fixed values with JSONata expressions.
Step 3 — Post to Teams
Add a REST API step after the Transform:
| Setting | Value |
|---|---|
| Use connection | Off: the webhook URL carries its own signature |
| Endpoint URL | The webhook URL you copied in Step 1 |
| HTTP Method | post |
Under Headers, select Add header:
| Key | Value |
|---|---|
content-type | application/json |
Under Request body, use the Body path tab and set it to request. This sends only the message, not failedCount.
Under Advanced:
| Setting | Value |
|---|---|
| Version | 3 |
| Skip if expression | body.failedCount = 0, so nobody gets a message when all orders went through |
When Teams accepts the message, the step returns statusCode 202 (Accepted). The message appears in the channel shortly after, posted by the Workflows app.
Troubleshooting
| Symptom | Likely cause |
|---|---|
statusCode 400 | The body isn't valid for the webhook, often because the type/attachments wrapper is missing or contentType is misspelled. Check the request in Execution Logs. |
statusCode 401 or 404 | The URL is incomplete (the sig= part is missing) or the Teams workflow was deleted. Copy the URL again from Teams. |
202, but nothing appears in the channel | Teams accepted the request, but its own workflow failed. Open the workflow's run history in Teams or Power Automate to see why, for example a card that doesn't render. |
| The card shows without the list | facts wasn't a list. Check the [] after the mapping. |
Variations
- Post to several channels: create one Teams workflow per channel, then add one REST API step per URL, or one REST API step with For each over a list of URLs built in the Transform. See REST API in Practice.
- Notify about a single record: in a workflow started by an Event trigger, build the card from the record that changed. For example: "Order SO-50212 is ready to ship".
- Email instead of, or as well as, Teams: see Email a Run Summary.
Further reading
- REST API component: reference for headers, Body path, Skip if expression and For each
- Transform component: reference for building JSON with JSONata
- JSONata Masterclass: structure larger message-building expressions
- Create incoming webhooks with Workflows for Microsoft Teams: Microsoft's guide to creating the webhook
- Adaptive Cards documentation: the full card format