Get change notifications for messages in Teams channels and chats using Microsoft Graph

Change notifications enable you to subscribe to changes (create, update, and delete) to messages in a channel or chat. Change notifications provide a low latency model by allowing you to maintain a subscription. You can also get the resource data in the notifications and therefore avoid calling the API to get the payload.

Continue with this article about scenarios for the chatMessage resource in the channel or chat context. Or, find out about change notifications for other Microsoft Teams resources.

Note

If you request a subscription expirationDateTime that is more than 1 hour in the future, you must subscribe to lifecycle notifications by including a lifecycleNotificationUrl property in your subscription request. Otherwise your subscription request will fail with the following error message: lifecycleNotificationUrl is a required property for subscription creation on this resource when the expirationDateTime value is set to greater than 1 hour.

Subscribe to changes at the tenant level

To track all changes related to messages in a tenant, you can use subscriptions at a tenant level for channel and chat messages, creating two subscriptions: one to track all messages across channels, and one to track all messages across chats.

Subscribe to messages across all channels

To get change notifications for all messages and replies across channels in a tenant, subscribe to /teams/getAllMessages. This resource supports including resource data in the notification.

Note: This API has licensing and payment requirements. It supports both model=A and model=B query parameters. If no model is specified, evaluation mode will be used.

Permissions

Permission type Permissions (from least to most privileged)
Delegated (work or school account) Not supported.
Delegated (personal Microsoft account) Not supported.
Application ChannelMessage.Read.All

Example

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/teams/getAllMessages",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Subscribe to messages across all chats

To get change notifications for all messages across chats in a tenant, subscribe to /chats/getAllMessages. This resource supports including resource data in the notification.

Note: This API has licensing and payment requirements. It supports both model=A and model=B query parameters. If no model is specified, evaluation mode will be used.

Permissions

Permission type Permissions (from least to most privileged)
Delegated (work or school account) Not supported.
Delegated (personal Microsoft account) Not supported.
Application Chat.Read.All

Example

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated,deleted",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/chats/getAllMessages",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Subscribe to messages in a channel

To track messages and replies in a channel, you can create a change notification subscription at a channel level by subscribing to /teams/{team-id}/channels/{channel-id}/messages. This resource supports including resource data in the notification in both delegated and application-only mode.

Channel-level subscriptions also support keyword-based search via the $search query parameter.

Permissions

Permission type Permissions (from least to most privileged)
Delegated (work or school account) ChannelMessage.Read.All
Delegated (personal Microsoft account) Not supported.
Application ChannelMessage.Read.Group*, ChannelMessage.Read.All

Note: Permissions marked with * are supported as part of resource-specific consent.

Example 1: Subscribe to all messages (and replies) in a channel

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/teams/{team-id}/channels/{channel-id}/messages",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Example 2: Subscribe to messages (and replies) in a channel that contain certain text

The following request sends messages that contain Hello to the subscriber.

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/teams/{team-id}/channels/{channel-id}/messages?$search=Hello",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Example 3: Subscribe to messages (and replies) in a channel without resource data

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/teams/{team-id}/channels/{channel-id}/messages",
  "includeResourceData": false,
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Example 4: Subscribe to messages (and replies) in a channel that mention a specific user

To get notifications only for messages where a specific user has been mentioned, you can specify the user's ID (9a6eb4d1-826b-48b1-9627-b50836c8fee9 in this example) in the query.

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/teams/{team-id}/channels/{channel-id}/messages?$filter=mentions/any(u: u/mentioned/user/id eq '9a6eb4d1-826b-48b1-9627-b50836c8fee9')",
  "includeResourceData": false,
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Subscribe to messages in a chat

To track messages in a chat, you can create a change notification subscription at a chat level by subscribing to /chats/{chat-id}/messages. This resource supports including resource data in the notification in both delegated and application-only mode.

Chat-level subscriptions also support keyword-based search via the $search query parameter.

Permissions

Permission type Permissions (from least to most privileged)
Delegated (work or school account) Chat.Read
Delegated (personal Microsoft account) Not supported.
Application ChatMessage.Read.Chat*, Chat.Read.All

Note: Permissions marked with * are supported as part of resource-specific consent for the beta version only currently.

Example 1: Subscribe to messages in a chat

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/chats/{chat-id}/messages",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Example 2: Subscribe to messages in a chat that contain certain text

The following request sends messages that contain Hello to the subscriber.

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/chats/{chat-id}/messages?$search=Hello",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Example 3: Subscribe to messages (and replies) in a chat without resource data

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json
{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/chats/{chat-id}/messages",
  "includeResourceData": false,
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Example 4: Subscribe to message in a chat in which a specific user is mentioned

To get notifications only for messages in which a specific user has been mentioned, you can specify the user's ID (9a6eb4d1-826b-48b1-9627-b50836c8fee9 in this example) in the query.

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/chats/{chat-id}/messages?$filter=mentions/any(u: u/mentioned/user/id eq '9a6eb4d1-826b-48b1-9627-b50836c8fee9')",
  "includeResourceData": false,
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Subscribe to changes at the user level

To track messages across all chats a particular user is part of, you can create a change notification subscription at a user level by subscribing to /users/{user-id}/chats/getAllMessages. This resource supports including resource data in the notification in both delegated and application-only modes.

User-level chat messaging subscriptions also support keyword-based search via the $search query parameter.

Note: This API has licensing and payment requirements. It supports the model=B query parameter. If no model is specified, evaluation mode will be used.

Permissions

Permission type Permissions (from least to most privileged)
Delegated (work or school account) Chat.Read, Chat.ReadWrite
Delegated (personal Microsoft account) Not supported.
Application Chat.Read.All, Chat.ReadWrite.All

Example: Subscribe to messages across all chats a particular user is part of

POST https://graph.microsoft.com/beta/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated,deleted",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/users/{user-id}/chats/getAllMessages",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Subscribe to messages of any chat in a tenant where a specific Teams app is installed

To get change notifications for all messages across chats in a tenant where a specific Teams app is installed, subscribe to /appCatalogs/teamsApps/{teams-app-id}/installedToChats/getAllMessages. This resource supports including resource data in the notification.

Note: This API has licensing and payment requirements. It supports the model=B query parameter. If no model is specified, evaluation mode will be used.

Permissions

Permission type Permissions (from least to most privileged)
Delegated (work or school account) Not supported.
Delegated (personal Microsoft account) Not supported.
Application Chat.Read.WhereInstalled, Chat.ReadWrite.WhereInstalled

Example

POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json

{
  "changeType": "created,updated",
  "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
  "resource": "/appCatalogs/teamsApps/386bbcdb-1e1c-4f3f-b7d0-ad7b9ea6cf7c/installedToChats/getAllMessages",
  "includeResourceData": true,
  "encryptionCertificate": "{base64encodedCertificate}",
  "encryptionCertificateId": "{customId}",
  "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
  "clientState": "{secretClientState}"
}

Notification payloads

Depending on your subscription, you can either get the notification with resource data, or without it. Subscribing with resource data allows you to get the message payload along with the notification, removing the need to call back and get the content.

Notifications with resource data

For notifications with resource data, the payload looks like the following. This payload is for a message sent in a chat.

{
    "value": [{
        "subscriptionId": "10493aa0-4d29-4df5-bc0c-ef742cc6cd7f",
        "changeType": "created",
        "clientState": "<<--SpecifiedClientState-->>",
        "subscriptionExpirationDateTime": "2021-02-02T10:30:34.9097561-08:00",
        "resource": "chats('19:8ea0e38b-efb3-4757-924a-5f94061cf8c2_97f62344-57dc-409c-88ad-c4af14158ff5@unq.gbl.spaces')/messages('1612289765949')",
        "resourceData": {
            "id": "1612289765949",
            "@odata.type": "#Microsoft.Graph.chatMessage",
            "@odata.id": "chats('19:8ea0e38b-efb3-4757-924a-5f94061cf8c2_97f62344-57dc-409c-88ad-c4af14158ff5@unq.gbl.spaces')/messages('1612289765949')"
        },
        "encryptedContent": {
            "data": "<<--EncryptedContent-->",
            "dataKey": "<<--EnryptedDataKeyUsedForEncryptingContent-->>",
            "encryptionCertificateId": "<<--IdOfTheCertificateUsedForEncryptingDataKey-->>",
            "encryptionCertificateThumbprint": "<<--ThumbprintOfTheCertificateUsedForEncryptingDataKey-->>"
        },
        "tenantId": "<<--TenantForWhichNotificationWasSent-->>"
    }],
    "validationTokens": ["<<--ValidationTokens-->>"]
}

For details about how to validate tokens and decrypt the payload, see Set up change notifications that include resource data.

The decrypted notification payload looks like the following. The payload conforms to the chatMessage schema. The payload is similar to that returned by GET operations.

{
  "id": "1612289992105",
  "replyToId": null,
  "etag": "1612289992105",
  "messageType": "message",
  "createdDateTime": "2021-02-02T18:19:52Z",
  "lastModifiedDateTime": "2021-02-02T18:19:52.105Z",
  "lastEditedDateTime": null,
  "deletedDateTime": null,
  "subject": null,
  "summary": null,
  "chatId": "19:8ea0e38b-efb3-4757-924a-5f94061cf8c2_97f62344-57dc-409c-88ad-c4af14158ff5@unq.gbl.spaces",
  "importance": "normal",
  "locale": "en-us",
  "webUrl": null,
  "from": {
    "application": null,
    "device": null,
    "user": {
      "id": "8ea0e38b-efb3-4757-924a-5f94061cf8c2",
      "displayName": "Ramjot Singh",
      "userIdentityType": "aadUser"
    },
    "conversation": null
  },
  "body": {
    "contentType": "text",
    "content": "test"
  },
  "channelIdentity": null,
  "attachments": [],
  "mentions": [],
  "policyViolation": null,
  "reactions": [],
  "replies": [],
  "hostedContents": []
}

Notifications without resource data

Notifications without resource data give you enough information to make GET calls to get the message content. Subscriptions for notifications without resource data don't require an encryption certificate (because actual resource data isn't sent over).

The payload looks like the following. This payload is for a message sent in a channel.

 {
  "subscriptionId": "9f9d1ed0-c9cc-42e7-8d80-a7fc4b0cda3c",
  "changeType": "created",
  "tenantId": "<<--TenantForWhichNotificationWasSent-->>",
  "clientState": "<<--SpecifiedClientState-->>",
  "subscriptionExpirationDateTime": "2021-02-02T11:26:41.0537895-08:00",
  "resource": "teams('fbe2bf47-16c8-47cf-b4a5-4b9b187c508b')/channels('19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2')/messages('1612293113399')",
  "resourceData": {
    "id": "1612293113399",
    "@odata.type": "#Microsoft.Graph.chatMessage",
    "@odata.id": "teams('fbe2bf47-16c8-47cf-b4a5-4b9b187c508b')/channels('19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2')/messages('1612293113399')"
  }
}

The resource and @odata.id properties can be used to make calls to Microsoft Graph to get the payload for the message. GET calls always return the current state of the message. If the message is changed between when the notification is sent and when the message is retrieved, the operation returns the updated message.