MyBotBoxMyBotBox

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

  1. In Microsoft Teams open the channel you want to listen to → Manage channelConnectors → search for Outgoing Webhook (you may need a workspace-level admin to enable Outgoing Webhooks first via the Teams admin center). Click Configure.
  2. 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 get 401.
  3. Outgoing webhooks fire only when the bot is @-mentioned in the channel — you'll receive an Activity envelope with type: 'message', the message text (HTML, including the <at> tag for the mention), and full channelData (team / channel / tenant IDs). Read e.g. <teams1.text>, <teams1.from.name>, <teams1.channelData.team.id>.
  4. 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

HeaderValue
AuthorizationHMAC <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

  • 401 mismatch — 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 id via webhook_event_dedup.