Table of Contents
Graph-based Message Trace API to Retrieve Message Trace Records and Details

Being a Microsoft Graph kind of person, I was very interested in the January 22 announcement (also MC1221939, 24 January 2026) about the preview of the Graph-based Exchange Online message trace API, especially so because the Exchange development group has been rightly criticized for not embracing Graph APIs for administrative interfaces. It’s great to see a crack appear in the dam.
The exchangeMessageTrace resource type comes with two methods, to list message trace objects and to get message details by user. I’ve written about using the new Get-MessageTraceV2 and Get-MessageTraceDetailV2 cmdlets before, which are based on the APIs, so it was logical to check out the APIs.
No Microsoft Graph PowerShell SDK Cmdlets Yet
The APIs are very new, so Microsoft Graph PowerShell SDK cmdlets are not available in V2.34. Perhaps the cmdlets will show up in V2.35. In any case, the Invoke-MgGraphRequest cmdlet does everything needed to run the APIs in an interactive session. The prerequisites are that the signed in user must be an Exchange administrator and the session must have the delegated ExchangeMessageTrace.Read.All permission (scope).
Message Trace API Authorization Issues Because of a Missing Service Principal
The APIs didn’t work at first. A 401 “unauthorized” error greeted all attempts to access message trace data on January 22. A day later, Microsoft had updated the error text to include this very important information:
{"error":{"code":"Unauthorized","message":"Service principal-less Authentication failed: the service principal for App ID 8bd644d1-64a1-4d4b-ae52-2e0cbf64e373 was not found. Please create a service principal for this app in your tenant. Provisioning may take several hours to complete.
The lack of authorization is due to a missing service principal for the Microsoft “Transport Data Platform” multi-tenant application. The service principal acts as the instantiation for the application within the tenant. Without the service principal, the API cannot authenticate with the data store holding the message trace data, which is why the 401s happen.
To create the service principal, run the New-MgServicePrincipal cmdlet from the Microsoft Graph PowerShell SDK and pass the application identifier (shown in the error above). For example:
New-MgServicePrincipal -AppId 8bd644d1-64a1-4d4b-ae52-2e0cbf64e373 DisplayName Id AppId ----------- -- ----- Transport Data Platform e0902d65-4d64-4d9f-8843-41e67af735b6 8bd644d1-64a1-4d4b-ae52-2e0cbf64e373
Running Message Trace API Requests
With the service principal in place, you can run requests to fetch message trace data. By default, the page size for requests is 1,000, meaning that the Graph returns 1,000 message trace records per request. Even the smallest tenant generates thousands of message trace records during the 10-day sliding window that this data is available for, so pagination is required to fetch all the available trace records.
A fully-worked out example explains how to fetch and use message trace records best. A 2023 article explains how to analyze the domains that send and receive messages for a tenant with a PowerShell script that uses the Get-MessageTraceV2 cmdlet. I created a new version of the script that uses Graph API requests instead of the cmdlet. Here are the commands to fetch message trace data for the last ten days in pages of 2,000 records:
# Message trace date is kept for a maximum of 10 days
$StartDate = (Get-Date).AddDays(-10).toString("s") + "Z"
$EndDate = (Get-Date).ToString("s") + "Z"
Write-Host ("Message trace data will be analyzed between {0} and {1}" -f (Get-Date $StartDate -format 'dd-MMM-yyyy HH:mm'), (Get-Date $EndDate -format 'dd-MMM-yyyy HH:mm'))
[int]$BatchSizeForMessages = 2000
$Uri = "https://graph.microsoft.com/beta/admin/exchange/tracing/messageTraces?`$filter=receivedDateTime ge {0} and receivedDateTime le {1} and status eq 'Delivered'&`$top={2}" -f $StartDate, $EndDate, $BatchSizeForMessages
[array]$Messages = $Null
Try {
# The warning action is suppressed here because we don't want to see warnings when more data is available
[array]$MessagePage = Invoke-MgGraphRequest -Method GET -Uri $Uri -ErrorAction Stop
$Messages += $MessagePage.Value
Write-Host ("Fetched {0} messages in first batch" -f $Messages.count)
} Catch {
Write-Host ("An error occured fetching message trace data: {0}" -f $_.Exception.Message)
Break
}
If the Graph returns a nextLink pointer (to the next page of results), a DO loop fetches successive pages until all available data is retrieved. I had to incorporate a short pause before fetching each page to avoid 401 errors (charmingly, the same error about not having a service principal appears when the service principal is emphatically available). I’m sure Microsoft will eliminate the requirement to pause between pages before the API goes to general availability.
You can download a copy of the script from the Office 365 for IT Pros GitHub repository.
Fetching Message Details with the Message Trace API
The message details API fetches details of the individual message tracing steps for a message recipient. This example fetches details of the first message in the set of information retrieved for the last ten days:
$Message = $Messages[0]
$MessageId = $Message.Id
$RecipientAddress = $Message.recipientAddress
$Uri = ("https://graph.microsoft.com/beta/admin/exchange/tracing/messageTraces/{0}/getDetailsByRecipient(recipientAddress='{1}')" -f $MessageId, $RecipientAddress)
$MessageData = Invoke-MgGraphRequest -Uri $uri -Method Get
$MessageData.Value
Name Value
---- -----
action
dateTime 23/01/2026 14:22:31
event Receive
data <root><MEP Name="ConnectorId" String="VI0PR04MB11071\Default VI0PR04MB11071" /><MEP Name="ClientIP" String="2603:10a6:10:50f::15" /><MEP Name="Se…
messageId <CAKA4JMHWRbkh9gCg3Hpmi3x+diMx=2AHTYRLAD4tG_n_oS9Rww@mail.gmail.com>
description Message received by: VI0PR04MB11071.eurprd04.prod.outlook.com
id 88e0d825-c9fa-4b02-1b24-08de5a8ad808
action
dateTime 23/01/2026 14:22:38
event Deliver
data <root><MEP Name="SourceContext" String="08DE5A2012048DB2;2026-01-23T14:22:38.456Z;ClientSubmitTime:" /><MEP Name="MailboxServer" String="AM0PR04M…
messageId <CAKA4JMHWRbkh9gCg3Hpmi3x+diMx=2AHTYRLAD4tG_n_oS9Rww@mail.gmail.com>
description The message was successfully delivered.
id 88e0d825-c9fa-4b02-1b24-08de5a8ad808
The Choice Between Exchange Online PowerShell and the Graph APIs
Apart from dealing with topics like Graph pagination, none of what’s described above will seem strange to administrators accustomed to Exchange Online message tracing. The same information is available via the Graph as through the message tracing cmdlets. This raises the question of why these APIs exist. The answer is that not every process wants to load the Exchange Online management PowerShell module to fetch message trace information. Some processes need to do everything through simple HTTP requests, and that’s what Graph API requests are.
It’s good to have choice!
Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365. Only humans contribute to our work!
@Tony: Thanks for the article. $MessageData.Value provides a “data” attribute that shows a name field called “ConnectorID”, but no clear text output. How can I find out which connector was used to send/receive the email?