New API to Remove Chat Threads

Microsoft 365 notification MC673983 (6 Sept 2023) announces the availability of the Teams Delete Chat Thread Graph API. The API supports the removal of 1:1, meeting, and group chat threads, but not messages in channel conversations. A chat thread is a collection of messages between one or more participants (users or apps).

APIs are also available to retrieve information about deleted chat threads and to restore (undo) deleted chat threads. Deployment of the new APIs has started and should be completed worldwide by the end of September 2023.

Running the Delete Chat APIs

Microsoft says that admins can use the APIs via PowerShell scripts. Out of the box, Teams Graph APIs that interact with user data are limited to delegate permissions. In other words, the only data that’s available is that belonging to the signed-in account. In this article, I focus on using the APIs through an interactive session with the Microsoft Graph PowerShell SDK. I used version 2.5 of the SDK for my testing. The cmdlets used here come from both the V1.0 and Beta Graph modules, so be sure to install both.

Despite my account’s status as a Teams administrator, the least-permission model used by the Graph and restriction to delegate permissions limits me to chats that my account can access.

The permissions needed to delete chat threads are:

  • Chat.ReadWrite: To read chat information.
  • Chat.ManageDeletion.All: To delete a chat thread, list deleted chats, or undo (restore) deleted chats.

For an interactive SDK session, make sure to specify these permissions (Scopes) when you run the Connect-MgGraph cmdlet. To access chat data for other users, you’ll need a registered Entra ID app with consent to use these permissions. The app will likely need other permissions, like User.Read.All to read information about user accounts.

Signing in and Getting Chats

Before you can delete chat threads, you need to know some information about the threads. I therefore wrote a script to find all the chat threads accessible to the account that signed into the Graph SDK. The script then builds an array of chat threads of three types by running the Get-MgBetaUserChat cmdlet:

  • Group: A chat thread involving more than two participants. The participants can be user accounts (members or guests) or apps.
  • OneOnOne: A chat thread between two users in a 1:1 conversation. The users can be in the same or different tenants.
  • Meeting: A chat thread associated with a Teams online meeting.

The filter used to fetch chat threads ignores threads of the unknownFutureValue type. Most of these threads appear to belong to future events and aren’t of great interest.

Connect-MgGraph -Scopes Chat.ReadWrite, Chat.ManageDeletion.All
 -NoWelcomeMessage
$Account = (Get-MgContext).Account
$UserId = (Get-MgUser -UserId $Account).Id
# Get chats for the user
[array]$Chats = Get-MgBetaUserChat -Userid $UserId -All -Filter "chattype eq 'group' or chattype eq 'oneonone' or chattype eq 'Meeting'" | Sort-Object LastUpdatedDateTime -Descending

Here’s what I found about chat threads in my account:

$Chats | Group-Object ChatType -NoElement

Count Name
----- ----
   59 group
  371 meeting
  114 oneOnOne

Chat threads can originate in other tenants, most often when people attend meetings organized outside their home tenant. When examining the distribution of chat threads across tenants, it should come as no surprise that the majority of my threads originated in my tenant:

$Chats | Group-Object TenantId -NoElement | Sort-Object Count -Descending | Format-Table Name, Count

Name                                 Count
----                                 -----
b662313f-14fc-43a2-9a7a-d2e27f4f3478   434
72f988bf-86f1-41af-91ab-2d7cd011db47    71
c0a3a43f-9257-4949-be1b-f83cfb83f65f    14
91c369b5-1c9e-439c-989c-1867ec606603    12
1786fc65-d8f0-40d0-b749-2f75d9c349a7     4
22e90715-3da6-4a78-9ec6-b3282389492b     4
2cf6ec6e-2282-4005-9973-344bcba75c54     1

People don’t usually remember tenant identifiers, so my script builds a hash table of identifiers and tenant display names so that the report can output the name of the owning tenant for each chat thread.

To create a report listing information about the chat threads, it’s a matter of looping through each thread and extracting information like the chat participants and the tenant name. Information for some chat participants is missing because their accounts are no longer available. Fetching chat participants takes the longest in terms of how long it takes for the script to run. Figure 1 shows the output of the report.

Reporting Teams chat threads
Figure 1: Reporting Teams chat threads

You can download the complete script from GitHub.

Deleting Chat Threads

Now that we know the details about a user’s chat threads, we can use the delete API to remove threads that we don’t want to keep. The essential requirement is to have the identifier for the chat thread. The report data includes the identifier for each thread, but you can also populate a variable by searching for a chat thread with a specific topic:

$ChatId = $Chats | Where-Object {$_.topic -eq 'External chat with Teams consumer'} | Select-Object -ExpandProperty Id

After finding the chat identifier, we can remove the chat thread using the Remove-MgBetaChat cmdlet and check what we did with the Get-MgBetaTeamWorkDeletedChat cmdlet (which really doesn’t tell us much except confirm the deletion status for a chat thread). Note that the Remove-MgBetaChat cmdlet doesn’t prompt for confirmation:

Remove-MgBetaChat -ChatId $ChatId

Get-MgBetaTeamworkDeletedChat -DeletedChatId $ChatId | fl

Id                   : 19:631b8356677448d2af176dea4178f760@thread.v2
AdditionalProperties : {[@odata.context, https://graph.microsoft.com/beta/$metadata#teamwork/deletedChats/$entity]}

Deleting a chat thread is a soft-delete action, meaning the deletion can be reversed. The effect of the soft deletion is that Teams hides the thread to make it invisible to all chat participants. A chat thread is a shared object, so deletion only has to occur once to render the thread inaccessible to all participants. Deletion removes all the messages in a thread (you can’t remove a single message). However, deleting a thread only deletes the messages. It does not remove content shared in a thread. This information remains in the Microsoft Teams Chat Files folder in the OneDrive for Business account of the person who shared the file.

The exact delay before a deleted chat thread becomes inaccessible to participants depends on the client that they use and how often clients refresh their cached chats. It’s a bit like the way that message deletions performed by a Teams retention policy can take several days before clients remove the messages from user view.

However, once Remove-MgBetaChat removes a chat thread, all participants lose access to the thread even if they can still see the chat messages in a client. If a chat participant attempts to send a message to a deleted chat thread that’s still visible in their client, the send action fails because the user is no longer a member of the chat.

The Undo-MgBetaTeamWorkDeletedChatDelete cmdlet restores a deleted chat thread. Restores must happen within seven days of deletion. If left any longer, it might not be possible to recover a deleted chat.

Undo-MgBetaTeamworkDeletedChatDelete -DeletedChatId $ChatId

Chat participants should be able to interact with the thread very soon after reversal of the deletion. The client must discover that the chat is no longer deleted. Once that happens, the thread returns to normal.

Delete Chat API Throttling

Microsoft throttles Delete Chat API requests to one request per second within a tenant. This is probably to discourage tenants from viewing the new API as a replacement for retention policies. A retention policy targeted at Teams chat messages can remove chats faster across the entire tenant. However, retention policies are age-based and remove chats older than a specified period (for instance, after seven days). They do not have the flexibility to target chats for removal based on other attributes, such as the topic, a chat participant, or the type of chat.

Throttling means that the Delete Chat API must be considered as a surgical tool to find and remove individual chat threads rather than a broad-brush method to clean up the chats for user accounts.

Can’t Remove Chat Threads from Other Tenant

In the case of federated chats (where a user from one tenant chats with a user in another tenant using Teams external access), chats “belong” to the tenant where the chat originated. In other words, the tenant of the user who started the federated chat.

The same restriction exists for group and meeting chats originating in other tenants. Any attempt to remove a chat that doesn’t belong to a tenant results in a forbidden error:

Remove-MgBetaChat -ChatId $ChatId

Remove-MgBetaChat_Delete: InsufficientPrivileges
Status: 403 (Forbidden)

Only administrators from the originating tenant can remove chat threads.

Slow But Steady Chat Thread Deletion

There’s nothing particularly complicated about using the Teams Delete Chat API, once you find the identifier for the chat thread to delete. The things to remember are throttling and that you can’t delete a thread originating from another tenant. Apart from that, administrators can (slowly) clean up chat threads with PowerShell as required. I’ll tackle the topic of how best to clean up unwanted chat threads in a future article.

Microsoft Platform Migration Planning and Consolidation

Simplify migration planning, overcome migration challenges, and finish projects faster while minimizing the costs, risks and disruptions to users.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.

Comments

  1. Steve

    Hey Tony,

    We tested this on some accounts and it definitely works to have supervised chat kick in for student-student. One major problem – the chats between a student-teacher were deleted as well and when the student or teacher go to chat each other again, the message gets stuck sending and never goes through. If you refresh the browser you get a “You can’t send messages because you are not a member of the chat” error. Tried deleting local cache on both ends as well as testing from a fresh computer/InPrivate browsing session. I’m thinking this will work itself out after 7 days when the chat goes into hard deletion. Thoughts?

    1. Avatar photo
      Tony Redmond

      When you delete a chat thread, the deletion action occurs against the Teams message store. Clients need to refresh their cache to respect the deletion. The reason why your users are told that they’re not members of the chat is because the thread is now gone (soft deleted) even if the client thinks otherwise. Things do sort themselves out over time, but as I think this kind of operation should be occasional rather than regular, the client time lag is acceptable.

  2. Hank

    Thank you for posting this!!!!

    As an educational tenant, we have been looking for a way to get supervised chat to kick in for existing chats. Retention policies don’t work for this, they do delete the messages but students are still able to open a new chat. I created an app registration and used the new API to clear out all chat threads for two test students which worked to get supervised chat policy working correctly.

Leave a Reply