Message

trigger.teams_event
TriggerChat & messagingconnection requiredruns in-engine

Creates a Microsoft Graph change-notification subscription at publish, renews it before its three-day expiry, and starts one run per notification.

Start a run when a message is posted, edited or deleted in a Teams channel or chat.

Connection

Required
Yes — the node will not save without one
Auth type
OAuth 2.0
Providers
Microsoft Teams
Secret shape
{"client_id", "client_secret", "refresh_token"}
Config it needs
tenant
Resolved by
config.connection_id, else node.connection_id
Note
The refresh token needs ChannelMessage.Read.All to watch a channel, or Chat.Read to watch a chat — Graph refuses a subscription whose credential cannot read the resource. Connect-with-Microsoft does not mint Teams connections yet, so the refresh token has to be supplied on the connection directly. Publishing also needs MANTLE_APP_PUBLIC_BASE_URL set to an https URL Graph can reach: it probes that URL before it will create the subscription.

Configuration

5 fields, none required. A dotted path is a nested object and name[] an array of objects — the editor renders the same tree.

FieldTypeRequiredDefaultConstraintsExample
watch
Whether to watch a team channel or a 1:1/group chat. Graph needs a different resource path for each, and different ids.
stringNochannel_messageone of "channel_message", "chat_message"—
team_id
The team whose channel to watch, as its GUID — teams.list_teams returns it. Only used when watching a channel.
stringConditional—≤ 64 characters; only with watchfbe2bf47-16c8-47cf-b4a5-4b9b187c508b
channel_id
The channel to watch (19:…@thread.tacv2) — teams.list_channels returns it. Only used when watching a channel.
stringConditional—≤ 256 characters; only with watch19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2
chat_id
The chat to watch (19:…@unq.gbl.spaces) — teams.list_chats returns it. Only used when watching a chat.
stringConditional—≤ 256 characters; only with watch19:8ea0e38b-efb3-4757-924a-5f94061cf8c2@unq.gbl.spaces
change_types
Which changes fire a run. 'updated' is chattier than it looks — Teams counts an edit, a reaction and a policy check as updates.
arrayNo["created"]1–3 characters; array of string—

Example configuration

Every value below is the contract’s own example or default — nothing invented.

{
  "change_types": [
    "created"
  ],
  "channel_id": "19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2",
  "chat_id": "19:8ea0e38b-efb3-4757-924a-5f94061cf8c2@unq.gbl.spaces",
  "team_id": "fbe2bf47-16c8-47cf-b4a5-4b9b187c508b",
  "watch": "channel_message"
}

Output

One shape.

teams notification

The flattened Graph change notification. It carries the message's id and path, not its text — fetch the message to read it.

Extra keys beyond the ones listed can appear.

FieldTypeExample
change_type
What happened to the message: created, updated or deleted. Branch on this when the trigger subscribes to more than one.
stringcreated
subscription_id
Graph's id for the subscription that delivered this. Part of the idempotency key, so a retry cannot start a second run.
string9f9d1ed0-c9cc-42e7-8d80-a7fc4b0cda3c
resource
Graph's path to the message that changed — pass it to a Graph read to fetch the message itself, which the notification does not carry.
stringteams('fbe2bf47-16c8-47cf-b4a5-4b9b187c508b')/channels('19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2')/messages('1612293113399')
message_id
The chatMessage's id. Teams message ids are millisecond timestamps as strings, so keep them as strings.
string1612293113399
team_id
The team the message is in, lifted out of the resource path. Empty for a chat message.
stringfbe2bf47-16c8-47cf-b4a5-4b9b187c508b
channel_id
The channel the message is in. Empty for a chat message.
string19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2
chat_id
The chat the message is in. Empty for a channel message.
string—
tenant_id
The Microsoft 365 tenant the notification came from — Microsoft's tenant, not Mantle's.
string—
subscription_expires_at
When Graph will stop delivering unless the subscription is renewed. Mantle renews it automatically; this is here for diagnosing a trigger that stopped.
string—
payload
The change notification exactly as Graph sent it, minus clientState (the shared secret), for anything the fields above do not lift out.
object—
{
  "change_type": "created",
  "channel_id": "19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2",
  "chat_id": "",
  "message_id": "1612293113399",
  "payload": {
    "changeType": "created",
    "resourceData": {
      "id": "1612293113399"
    }
  },
  "resource": "teams('fbe2bf47-16c8-47cf-b4a5-4b9b187c508b')/channels('19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2')/messages('1612293113399')",
  "subscription_expires_at": "2026-09-13T11:26:41.0537895Z",
  "subscription_id": "9f9d1ed0-c9cc-42e7-8d80-a7fc4b0cda3c",
  "team_id": "fbe2bf47-16c8-47cf-b4a5-4b9b187c508b",
  "tenant_id": "84bd8158-6d4d-4958-8b9f-9d6445542f95"
}

Notes

  • Publishing creates the Graph subscription; unpublishing, disabling or archiving deletes it. A workflow that is off is off at Microsoft too.
  • Graph subscriptions expire — three days is the ceiling for Teams messages — so Mantle renews this one automatically well before it lapses.
  • The notification carries the message's id and path, not its text. Rich notifications need an encryption certificate; read the message with a Teams node instead.
  • Deliveries are checked against the clientState Graph echoes back. That is a shared secret in the body, not a signature: it proves who sent the notification, not that the body is unmodified in transit.
  • One run per (subscription, change type, message id) — Graph retries an undelivered notification for up to four hours and a retry cannot start a second run.
  • Microsoft counts every Teams subscription in the tenant against one 10,000-subscription quota, and allows one per app-and-channel (or chat) combination; a second workflow watching the same channel is refused.

See Message run in a workflow.

In a technical demo we add the Microsoft Teams connection, drop the node on the canvas and let Workflow Health pre-flight it before the first real run.