How to mark all messages in the mail folder as read or unread via Microsoft Graph API

Mark message as read or unread

When working with messages through the Microsoft Graph API, one common task is to mark messages as read or unread. This can be particularly useful for managing email workflows and ensuring that important messages are appropriately flagged.

To mark a message as read or unread, you can use the PATCH method on the message resource and update the isRead property. Here's an example of how to do this using the Microsoft Graph API:

Mark a message as read

PATCH /v1.0/me/messages/{message_id}
Content-Type: application/json

{
  "isRead": true
}

Mark a message as unread

PATCH /v1.0/me/messages/{message_id}
Content-Type: application/json

{
  "isRead": false
}

Mark all messages in a folder as read or unread

What if you want to mark all messages in a specific mail folder as read or unread? Until now, the Microsoft Graph API didn't provide a direct way to do this in a single request. Only solution was to retrieve all unread messages in the folder and then iterate through them to update their isRead property individually.

Even if the batch request could be used to update up to 20 messages at a time, it would still require multiple requests if there are more than 20 messages to mark as read on unread.

Good news is that Microsoft Graph API introduces new beta endpoint that allows you to mark all messages in a mail folder as read or unread.

All you need to do is to send a POST request to the updateAllMessagesReadState action on the mail folder resource. In the request body, specify the isRead property to indicate whether you want to mark the messages as read (true) or unread (false).

If you want to suppress read receipts, you can also include the suppressReadReceipts property set to true.

POST /beta/me/mailFolders/{mail_folder_id}/updateAllMessagesReadState

{
  "isRead": true,
  "suppressReadReceipts": true
}

The response will be 202 Accepted. The response also contains a Location header, which contains the location of the mail folder operation that was created to handle the update of all messages states.

Check the status of the operation by making a GET request to this location.

GET /beta/me/mailFolders/{mail_folder_id}/operations/{operation_id}

The response will contain the operationStatus property, which can have the following values:

  • notStarted: The operation has not started yet.
  • running: The operation is currently running.
  • succeeded: The operation completed successfully.
  • failed: The operation failed to complete.

If you prefer using PowerShell, you can prepare a script that will execute the operation and periodically check the status until the operation is completed.

<#
.SYNOPSIS
    Marks messages in a mail folder as read/unread and waits for the async operation to complete.

.PARAMETER UserId
    The user id (or user principal name) for the mailbox.

.PARAMETER MailFolderId
    The mail folder id.

.PARAMETER MarkAsRead
    $true to mark messages as read, $false to mark as unread.

.PARAMETER PollIntervalSeconds
    How many seconds to wait between polling attempts. Default 5.

.PARAMETER TimeoutSeconds
    How many seconds to wait before timing out. Default 300.

.EXAMPLE
    Set-Location 'Path\To\Folder\With\Scripts'
    . .\Set-MailFolderMessagesReadState.ps1
    Set-MailFolderMessagesReadState -UserId 'user@contoso.com' -MailFolderId 'AAMk...' -MarkAsRead $true -Verbose
#>
function Set-MailFolderMessagesReadState {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string] $UserId,

        [Parameter(Mandatory = $true)]
        [string] $MailFolderId,

        [Parameter(Mandatory = $true)]
        [bool] $MarkAsRead,

        [int] $PollIntervalSeconds = 5,

        [int] $TimeoutSeconds = 300
    )

    begin {
        Write-Host "Setting messages in folder '$MailFolderId' for user '$UserId' to read state: $MarkAsRead" -ForegroundColor Cyan
        try {
            Import-Module Microsoft.Graph.Beta.Mail -ErrorAction Stop
        } catch {
            Write-Error "Failed to import Microsoft.Graph.Beta.Mail. Install it with 'Install-Module Microsoft.Graph.Beta' and authenticate using Connect-MgGraph."
            throw
        }
    }

    process {
        try {
            $allMessagesCount = Get-MgBetaUserMailFolderMessageCount -UserId $userId -MailFolderId $mailFolderId -ErrorAction Stop
            Write-Host "Total messages in folder: $allMessagesCount"
            $tobeProcessedCount = if ($MarkAsRead) {
                Get-MgBetaUserMailFolderMessageCount -UserId $userId -MailFolderId $mailFolderId -Filter "isRead eq false" -ErrorAction Stop
            } else {
                Get-MgBetaUserMailFolderMessageCount -UserId $userId -MailFolderId $mailFolderId -Filter "isRead eq true" -ErrorAction Stop
            }
            Write-Host "Messages to be processed: $tobeProcessedCount"

            $params = @{
	            isRead = $MarkAsRead
	            suppressReadReceipts = $true
            }
            Update-MgBetaUserMailFolderMessageReadState -UserId $UserId -MailFolderId $MailFolderId -BodyParameter $params -ResponseHeadersVariable respHeaders -ErrorAction Stop

            $location = $respHeaders['Location']
            if (-not $location) {
                throw "Response did not include a Location header; cannot track operation."
            }

            $operationsSegment = $location.Split('/')[-1]
            Write-Verbose "Operations segment from Location header: $operationsSegment"

            $operationId = $null
            if ($operationsSegment -match '[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}') {
                $operationId = ($matches[0])
            } elseif ($operationsSegment.Length -ge 47) {
                $operationId = $operationsSegment.Substring(11,36)
            }

            if (-not $operationId) {
                throw "Could not parse operation id from Location header: $operationsSegment"
            }

            Write-Verbose "Tracking operation with id: $operationId"

            $start = [DateTime]::UtcNow
            $operation = Get-MgBetaUserMailFolderOperation -UserId $UserId -MailFolderId $MailFolderId -MailFolderOperationId $operationId -ErrorAction Stop

            $operationStatus = $operation.AdditionalProperties['operationStatus']
            while ($operationStatus -eq 'notStarted' -or $operationStatus -eq 'running') {
                if (([DateTime]::UtcNow - $start).TotalSeconds -gt $TimeoutSeconds) {
                    throw "Timed out waiting for operation $operationId (waited $TimeoutSeconds seconds)."
                }
                Start-Sleep -Seconds $PollIntervalSeconds
                $tobeProcessedCount = if ($MarkAsRead) {
                    Get-MgBetaUserMailFolderMessageCount -UserId $userId -MailFolderId $mailFolderId -Filter "isRead eq false" -ErrorAction Stop
                } else {
                    Get-MgBetaUserMailFolderMessageCount -UserId $userId -MailFolderId $mailFolderId -Filter "isRead eq true" -ErrorAction Stop
                }
                Write-Host "Messages remaining to be processed: $tobeProcessedCount"
                $operation = Get-MgBetaUserMailFolderOperation -UserId $UserId -MailFolderId $MailFolderId -MailFolderOperationId $operationId -ErrorAction Stop
                $operationStatus = $operation.AdditionalProperties['operationStatus']
            }

            Write-Host "Operation status: $($operationStatus)" -ForegroundColor Green
            if ($operationStatus -eq 'failed') {
                Write-Error "Operation failed: $($operation.Error.Message)"
                return $operationStatus
            } else {
                Write-Host "Operation completed successfully."
                return $operationStatus
            }
        } catch {
            Write-Error $_.Exception.Message
            throw
        }
    }
}

Conclusion

Marking all messages in a mail folder as read or unread is now much easier with the new updateAllMessagesReadState action in the Microsoft Graph API. This allows for efficient management of email states without the need for complex iterations or multiple requests. Make sure to check the operation status to confirm that the changes have been applied successfully.

What's also great, the endpoint supports both delegated and application permissions.

0
Buy Me a Coffee at ko-fi.com
An error has occurred. This application may no longer respond until reloaded. Reload x