Microsoft Teams Microsoft Graph

Change Notifications for Microsoft Teams Messages now Generally Available

Back in February we announced the public preview of change notifications for Teams messages and we are excited to share we have now moved the change notification for Teams to general availability. This was one of the most common requests made in UserVoice, and we heard it from developers who wanted to listen to Teams messages in near-real time, without polling, to enable scenarios such as data loss prevention, enterprise information archiving, and bots that listen to messages that they aren’t @mentioned on. We’re excited to tell you change notifications for Teams messages are now generally available, and you can now subscribe to change notifications on v1.0 endpoint of Microsoft Graph API.

Using the Microsoft Graph POST /subscriptions API, developers can subscribe to messages in a particular channel (/teams/{id}/channels/{id}/messages) or in a particular 1:1 or group chat thread (/chats/{id}/messages). You can hear about new messages, replies, edits, reactions, and deletes.

Developers can also subscribe to all messages in a tenant. This requires the creation of two subscriptions: one for the /teams/getAllMessages resource and one for /chats/getAllMessages. With the general availability of change notifications for Teams messages, /teams/getAllMessages and /chats/getAllMessages become part of the E5 value for customers, see our list of

As with all Microsoft Graph webhooks, creating a subscription starts with passing in the URL of the webhook you wish Graph to call back to, and validating the subscription creation within your web service. When a new message arrives, Graph will send that message to your webhook. Teams messages are the first Graph resource to support webhooks with resource data, so you don’t need to make a second API call to retrieve the message text.

Example — setting up a Teams message subscription:

POST https://graph.microsoft.com/beta/subscriptions
{
    "resource": "teams/97edf8ce-b0f5-4bc8-91e8-7c73185f18c0/channels/19:945129d1eede4536a6a9811e71d7b2a6@thread.skype/messages",
    "notificationUrl": "https://apps.contoso.com/messageswebhookhandler",
    "changeType": "created,updated,deleted",
    "clientState": "6b5acd8b-98d1-48be-a8be-e12842fadcd5",
    "expirationDateTime": "2020-02-13T01:33:09.1416264Z",
    "encryptionCertificate": "MIIDNDCCAhygAwIBAgIQLR3AKZYNS42E....",
    "encryptionCertificateId": "70ffad80b880496a968e83091174a979",
    "includeResourceData": "True"
}

Example response

HTTP/1.1 201 Created
{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
    "id": "da94613a-d11a-41e2-b635-295a59b29f3b",
    "resource": "teams/97edf8ce-b0f5-4bc8-91e8-7c73185f18c0/channels/19:945129d1eede4536a6a9811e71d7b2a6@thread.skype/messages",
    "notificationUrl": "https://apps.contoso.com/messageswebhookhandler",
    "applicationId": "7ad2eb86-67bc-42a1-bb7b-04659985e51e",
    "changeType": "created,updated",
    "clientState": "ClientSecret",
    "lifecycleNotificationUrl": null,
    "expirationDateTime": "2020-02-13T01:33:09.1416264Z",
    "creatorId": "dfe1df92-51cf-4f68-be9c-87be7c1b447d",
    "includeResourceData": true,
    "encryptionCertificate": "MIIDNDCCAhygAwIBAgIQLR3AKZYNS42E....",
    "encryptionCertificateId": "70ffad80b880496a968e83091174a979"
}

After you request the subscription, Microsoft Graph will ping your URL to confirm that wants notifications. As subsequent messages are updated, you will see requests to your web services that look as follows:

POST https://apps.contoso.com/messageswebhookhandler
{
    "body": {
        "contentType": "text",
        "content": "Hello world!"
    },
    "id": "1581554024315",
    "replyToId": "1581553690121",
    "etag": "1581554024315",
    "messageType": "message",
    "createdDateTime": "2020-02-13T00:33:44Z",
    "lastModifiedDateTime": null,
    "deletedDateTime": null,
    "subject": null,
    "summary": null,
    "importance": "normal",
    "locale": "en-us",
    "webUrl": "",
    "from": {
        "application": null,
        "device": null,
        "user": {
            "id": "7ed20f7a-09c6-4a47-8ebf-a6a1f2588624",
            "displayName": "Megan Bowen",
            "userIdentityType": "aadUser"
        },
        "conversation": null
    },
    "attachments": [],
    "mentions": [],
    "policyViolation": null,
    "reactions": [],
    "replies": [],
    "hostedContents": []
}

Decrypting the encryptedContent gives the full message details:

{
    "body": {
        "contentType": "text",
        "content": "Hello world!"
    },
    "id": "1581554024315",
    "replyToId": "1581553690121",
    "etag": "1581554024315",
    "messageType": "message",
    "createdDateTime": "2020-02-13T00:33:44Z",
    "lastModifiedDateTime": null,
    "deletedDateTime": null,
    "subject": null,
    "summary": null,
    "importance": "normal",
    "locale": "en-us",
    "webUrl": "",
    "from": {
        "application": null,
        "device": null,
        "user": {
            "id": "7ed20f7a-09c6-4a47-8ebf-a6a1f2588624",
            "displayName": "Megan Bowen",
            "userIdentityType": "aadUser"
        },
        "conversation": null
    },
    "attachments": [],
    "mentions": [],
    "policyViolation": null,
    "reactions": [],
    "replies": [],
    "hostedContents": []
}

Data loss prevention PATCH

After examining a message, apps can mark a message as noncompliant with organizational policy and hide that message from view.

PATCH /teams/{id}/channels/{id}/messages/{id}
{
    "policyViolation": {
        "dlpAction": "BlockAccess",
        "verdictDetails": "AllowFalsePositiveOverride",
        "policyTip": {
            "generalText": "Don't share credit card numbers!",
            "complianceUrl": "https://contoso.com/dlp-policy-page",
            "matchedConditionDescriptions": [
                "Credit Card Number"
            ]
        }
    }
}

Next steps

You can get started by trying out the sample app.  See the documentation for more information about Microsoft Teams messaging webhooks.

If you have any feedback about or suggestions for these APIs, please let us know via User Voice (under Teamwork).

Happy coding!

filter-icon