Microsoft Teams
Trigger workflows from @-mentions in Microsoft Teams channels via Outgoing Webhooks.
The Microsoft Teams trigger fires when the connected bot is @-mentioned in a Teams channel. Teams delivers an Activity envelope (Bot Framework shape) with the message text, sender, channel, and tenant IDs; the trigger surfaces these as flat outputs.
How to configure
- In Microsoft Teams open the channel you want to listen to → Manage channel → Connectors → search for Outgoing Webhook (you may need a workspace-level admin to enable Outgoing Webhooks first via the Teams admin center). Click Configure.
- Set Name to anything ("MyBotBox") and paste the Webhook URL as the Callback URL. Teams generates a Security token (HMAC secret) and shows it once — copy it immediately and store it as the trigger's signing secret. Teams signs the raw body with HMAC-SHA256 and sends the base64 digest in
Authorization: HMAC <digest>. The trigger default-denies when a secret is configured — bad signatures get401. - Outgoing webhooks fire only when the bot is @-mentioned in the channel — you'll receive an
Activityenvelope withtype: 'message', the messagetext(HTML, including the<at>tag for the mention), and fullchannelData(team / channel / tenant IDs). Read e.g.<teams1.text>,<teams1.from.name>,<teams1.channelData.team.id>. - Verify by
@-mentioning the bot in the connected channel. Teams expects a response within 5 seconds — the trigger ACKs immediately, so latency on your workflow does not block the user. Recreate the connector if you rotate the security token; Teams will not show it again after first display.
Signature format
| Header | Value |
|---|---|
Authorization | HMAC <base64> — base64 digest of HMAC-SHA256(base64-decoded-secret, raw_body) |
The Teams security token is itself base64-encoded; the platform base64-decodes it before keying the HMAC. The Activity id is used as the webhook_event_dedup key.
Sample payload
{
"type": "message",
"id": "1745700000000",
"timestamp": "2026-04-26T21:00:00.000Z",
"localTimestamp": "2026-04-26T16:00:00.000-05:00",
"localTimezone": "America/Chicago",
"serviceUrl": "https://smba.trafficmanager.net/amer/",
"channelId": "msteams",
"from": {
"id": "29:1abcDEF_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "Alex Chen",
"aadObjectId": "11111111-2222-3333-4444-555555555555"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"tenantId": "00000000-0000-0000-0000-000000000000",
"id": "19:abc123@thread.tacv2;messageid=1745700000000"
},
"recipient": {
"id": "28:9876fedc-aaaa-bbbb-cccc-ddddeeeeffff",
"name": "MyBotBox"
},
"textFormat": "plain",
"locale": "en-US",
"text": "<at>MyBotBox</at> summarize the last 10 messages",
"attachments": [
{
"contentType": "text/html",
"content": "<div><at id=\"0\">MyBotBox</at> summarize the last 10 messages</div>"
}
],
"entities": [
{
"type": "mention",
"mentioned": { "id": "28:9876fedc-aaaa-bbbb-cccc-ddddeeeeffff", "name": "MyBotBox" },
"text": "<at>MyBotBox</at>"
},
{ "type": "clientInfo", "locale": "en-US", "country": "US", "platform": "Web" }
],
"channelData": {
"teamsChannelId": "19:abc123@thread.tacv2",
"teamsTeamId": "19:def456@thread.tacv2",
"channel": { "id": "19:abc123@thread.tacv2" },
"team": { "id": "19:def456@thread.tacv2" },
"tenant": { "id": "00000000-0000-0000-0000-000000000000" }
}
}Verify with the bundled scripts
bun apps/sat/tests/staging/triggers/microsoftteams/verify.ts \
https://mybotbox.com/api/webhooks/trigger/<path> \
--secret=<base64-encoded-teams-token>python3 apps/sat/tests/staging/triggers/microsoftteams/verify.py \
https://mybotbox.com/api/webhooks/trigger/<path> \
--secret=<base64-encoded-teams-token>Pass the security token Teams gave you as base64 — same string the trigger stores. Source: apps/sat/tests/staging/triggers/microsoftteams/.
Troubleshooting
401mismatch — the security token from Teams was lost (Teams shows it only once). Edit the connector, click Reset key, copy the new value into the trigger's signing secret.- Bot is mentioned but no fire — Outgoing Webhooks must be enabled at the Teams admin center level; channel connectors stay greyed out otherwise. Confirm with a workspace admin.
- HTML entities in
<teams1.text>— yes, Teams sends the text including the<at>tag verbatim. Strip it in a Function block:text.replace(/<at[^>]*>.*?<\/at>\s*/g, '').trim(). - Duplicates — Teams may retry on non-2xx; trigger dedupes on the Activity
idviawebhook_event_dedup.