Message trace API
As an Exchange Online administrator, you may notice that the Exchange Web Services (EWS) API is set to be retired in October 2026. As a result, Microsoft Graph API is now the recommended approach for accessing Exchange Online data and new features are gradually being added to Microsoft Graph.
One of the new features is the message trace API to track the flow of email messages. The Message Trace API replaces the Office 365 Reporting Web Service.
You can track:
- delivery status of the message
- date and time the message was received
- sender and recipient email addresses
- subject of the email
- source and destination IP addresses
- message-id header
- size of the message
The status of the message can be:
- gettingStatus
- pending
- failed
- delivered
- expanded
- quarantined
- filteredAsSpam
To list message traces, you can use the following endpoint:
GET /beta/admin/exchange/tracing/messageTraces
The app must be granted the ExchangeMessageTrace.Read.All delegated or application permission.
When calling the GET /beta/admin/exchange/tracing/messageTrace endpoint for the first time, you might encounter the following error:
{
"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. For details, see: https://learn.microsoft.com/entra/identity-platform/retire-service-principal-less-authentication?branch=main#create-a-service-principal",
...
}
}
Due to the service principal-less authentication mitigation effort, you need to create a service principal for the application with id 8bd644d1-64a1-4d4b-ae52-2e0cbf64e373.
POST /v1.0/servicePrincipals
{
"appId": "8bd644d1-64a1-4d4b-ae52-2e0cbf64e373"
}
After creating the service principal, you should be able to call the message trace API successfully. It can take a while, so give it few minutes or hours.
Tracing will only work if the service principal is created.
The app with ID 8bd644d1-64a1-4d4b-ae52-2e0cbf64e373 is a built-in Microsoft application called Transport Data Platform
The response will look like this:
{
"value": [
{
"id": "339e40d1-9a34-4bc5-adbe-08de5bf70906",
"messageId": "<a5cf7e42-ad2f-4f81-89c5-aaaaaaab86f@VIAPPFE7A2FBFA8.EURP250.PROD.OUTLOOK.COM>",
"status": "delivered",
"receivedDateTime": "2026-01-25T09:49:29.967Z",
"recipientAddress": "MartinMachacek@4wrvkx.onmicrosoft.com",
"senderAddress": "MicrosoftExchange123abcec88ae4615bbc36ab6ce41109e@4wrvkx.onmicrosoft.com",
"subject": "Undeliverable: Meeting",
"size": 69376,
"fromIP": "255.255.255.255",
"toIP": ""
},
{
"id": "eaaafa9a-788c-47f0-357c-08de5bf702e0",
"messageId": "<AMBP250MB14920887FE274BC92A@AMBP250MB9875.EURP250.PROD.OUTLOOK.COM>",
"status": "failed",
"receivedDateTime": "2026-01-25T09:49:19.648Z",
"recipientAddress": "aaa@bbb.ccccccc",
"senderAddress": "MartinMachacek@4wrvkx.onmicrosoft.com",
"subject": "Meeting",
"size": 25249,
"fromIP": "89.177.230.109",
"toIP": ""
},
{
"id": "eaaafa9a-788c-47f0-357c-08de5bf702e0",
"messageId": "<AMBP250MB14920887FE272A7C4BC92A@AMBP250MB9875.EURP250.PROD.OUTLOOK.COM>",
"status": "delivered",
"receivedDateTime": "2026-01-25T09:49:19.648Z",
"recipientAddress": "AlexW@4wrvkx.onmicrosoft.com",
"senderAddress": "MartinMachacek@4wrvkx.onmicrosoft.com",
"subject": "Meeting",
"size": 39686,
"fromIP": "89.177.230.109",
"toIP": ""
}
]
}
I sent a test email from MartinMachacek@4wrvkx.onmicrosoft.com to AlexW@4wrvkx.onmicrosoft.com and aaa@bbb.ccccccc.
The email to AlexW@4wrvkx.onmicrosoft.com was delivered successfully, while the email to aaa@bbb.ccccccc failed.
You might noticed that two message traces have the same message trace ID. This is because one message trace is created for each recipient.
You can use the message trace ID and recipient address to get more details about a specific message trace:
GET /beta/admin/exchange/tracing/messageTraces/{exchangeMessageTraceId}/getDetailsByRecipient(recipientAddress='parameterValue')
For example, let's get the details for the failed email:
GET /beta/admin/exchange/tracing/messageTraces/eaaafa9a-788c-47f0-357c-08de5bf702e0/getDetailsByRecipient(recipientAddress='aaa@bbb.ccccccc')
The response:
{
"value": [
{
"id": "eaaafa9a-788c-47f0-357c-08de5bf702e0",
"messageId": "<AMBP250MB1492088EBC4BC92A@AMBP250MB9875.EURP250.PROD.OUTLOOK.COM>",
"dateTime": "2026-01-25T09:49:20.187Z",
"event": "Submit",
"action": "",
"description": "The message was submitted.",
"data": "<root><MEP Name=\"RcptCount\" Integer=\"2\" /><MEP Name=\"ServerHostName\" String=\"VI6PPFE7A2F83A8.EURP250.PROD.OUTLOOK.COM\" /><MEP Name=\"ClientName\" String=\"AMBP250MB1492\" /><MEP Name=\"SequenceNumber\" Long=\"0\" /></root>"
},
{
"id": "eaaafa9a-788c-47f0-357c-08de5bf702e0",
"messageId": "<AMBP250MB14920887F4BC92A@AMBP250MB9875.EURP250.PROD.OUTLOOK.COM>",
"dateTime": "2026-01-25T09:49:26.421Z",
"event": "Receive",
"action": "",
"description": "Message received by: VI6P2F83A8.EURP250.PROD.OUTLOOK.COM",
"data": "<root><MEP Name=\"ConnectorId\" String=\"VI6P2F83A8\\Default VI6F83A8\" /><MEP Name=\"ClientIP\" String=\"2603:10a6:20b:764::22\" /><MEP Name=\"ServerHostName\" String=\"VI6PP83A8.EURP250.PROD.OUTLOOK.COM\" /><MEP Name=\"FirstForestHop\" String=\"VI6PP83A8.EURP250.PROD.OUTLOOK.COM\" /><MEP Name=\"DeliveryPriority\" String=\"Normal\" /><MEP Name=\"ReturnPath\" String=\"MartinMachacek@4wrvkx.onmicrosoft.com\" /><MEP Name=\"CustomData\" Blob=\"S:tlsversion=NONE\" /><MEP Name=\"SequenceNumber\" Long=\"0\" /></root>"
},
{
"id": "eaaafa9a-788c-47f0-357c-08de5bf702e0",
"messageId": "<AMBP250MB1EBC4BC92A@AMBP250MB9875.EURP250.PROD.OUTLOOK.COM>",
"dateTime": "2026-01-25T09:49:28.442Z",
"event": "TRANSFER",
"action": "",
"description": null,
"data": "<root><MEP Name=\"DeliveryPriority\" String=\"Normal\" /><MEP Name=\"ReturnPath\" String=\"MartinMachacek@4wrvkx.onmicrosoft.com\" /><MEP Name=\"SequenceNumber\" Long=\"0\" /><MEP Name=\"RecipientReference\" String=\"46729082\" /></root>"
},
{
"id": "eaaafa9a-788c-47f0-357c-08de5bf702e0",
"messageId": "<AMBP250MB14922A@AMBP250MB9875.EURP250.PROD.OUTLOOK.COM>",
"dateTime": "2026-01-25T09:49:29.981Z",
"event": "Fail",
"action": "",
"description": "Reason: [{LED=550 5.4.310 DNS domain bbb.ccccccc does not exist [Message=InfoDomainNonexistent] [LastAttemptedServerName=bbb.ccccccc] [AM4PEPF0002BAA6.EURPRD83.prod.outlook.com 2026-01-25T09:49:29.944Z 08DE5AB03F707551]};{MSG=InfoDomainNonexistent};{FQDN=bbb.ccccccc};{IP=0.0.0.0};{LRT=1/25/2026 9:49:29 AM}]. OutboundProxyTargetHostName: bbb.ccccccc",
"data": "<root><MEP Name=\"RcptCount\" Integer=\"1\" /><MEP Name=\"ConnectorId\" String=\"Outbound Proxy External Send Connector\" /><MEP Name=\"ServerHostName\" String=\"bbb.ccccccc\" /><MEP Name=\"DeliveryPriority\" String=\"Normal\" /><MEP Name=\"TotalLatency\" Integer=\"10\" /><MEP Name=\"OutboundProxyTargetHostName\" String=\"bbb.ccccccc\" /><MEP Name=\"ReturnPath\" String=\"MartinMachacek@4wrvkx.onmicrosoft.com\" /><MEP Name=\"ClientName\" String=\"VI6PA8.EURP250.PROD.OUTLOOK.COM\" /><MEP Name=\"CustomData\" Blob=\"S:OutboundProxyFrontEndName=eur.internal.map.protection.eop.outlook.com;S:IsSmtpResponseFromExternalServer=False;S:Microsoft.Exchange.Transport.MailRecipient.RequiredTlsAuthLevel=DomainValidation;S:ExternalSendLatency=10.297;S:OutboundIpPool=1102;S:OutboundIpPoolName=RegularOutboundPool\" /><MEP Name=\"SequenceNumber\" Long=\"0\" /><MEP Name=\"RecipientStatus\" String=\"[{LED=550 5.4.310 DNS domain bbb.ccccccc does not exist [Message=InfoDomainNonexistent] [LastAttemptedServerName=bbb.ccccccc] [AM4PEPF0002BAA6.EURPRD83.prod.outlook.com 2026-01-25T09:49:29.944Z 08DE5AB03F707551]};{MSG=InfoDomainNonexistent};{FQDN=bbb.ccccccc};{IP=0.0.0.0};{LRT=1/25/2026 9:49:29 AM}]\" /><MEP Name=\"RecipientReference\" String=\"<a5cf7e42-ad2f-4f81-89c5-97dda6f@VI6PP3A8.EURP250.PROD.OUTLOOK.COM>\" /></root>"
}
]
}
The detailed events are provided in chronological order and we can see that the message was submitted, received by SMTP server, transferred to the recipient's domain, and finally failed due to non-existent domain.
The data property associated with the event, contains supplementary information specific to the event.
<root>
<MEP Name="RcptCount" Integer="1" />
<MEP Name="ConnectorId" String="Outbound Proxy External Send Connector" />
<MEP Name="ServerHostName" String="bbb.ccccccc" />
<MEP Name="DeliveryPriority" String="Normal" />
<MEP Name="TotalLatency" Integer="10" />
<MEP Name="OutboundProxyTargetHostName" String="bbb.ccccccc" />
<MEP Name="ReturnPath" String="MartinMachacek@4wrvkx.onmicrosoft.com" />
<MEP Name="ClientName" String="VI6A8.EURP250.PROD.OUTLOOK.COM" />
<MEP
Name="CustomData"
Blob="S:OutboundProxyFrontEndName=eur.internal.map.protection.eop.outlook.com;S:IsSmtpResponseFromExternalServer=False;S:Microsoft.Exchange.Transport.MailRecipient.RequiredTlsAuthLevel=DomainValidation;S:ExternalSendLatency=10.297;S:OutboundIpPool=1102;S:OutboundIpPoolName=RegularOutboundPool" />
<MEP Name="SequenceNumber" Long="0" />
<MEP
Name="RecipientStatus"
String="[{LED=550 5.4.310 DNS domain bbb.ccccccc does not exist [Message=InfoDomainNonexistent] [LastAttemptedServerName=bbb.ccccccc] [AM4PEPF0002BAA6.EURPRD83.prod.outlook.com 2026-01-25T09:49:29.944Z 08DE5AB03F707551]};{MSG=InfoDomainNonexistent};{FQDN=bbb.ccccccc};{IP=0.0.0.0};{LRT=1/25/2026 9:49:29 AM}]" />
<MEP
Name="RecipientReference"
String="<a5cf7e42-ad2f-4f81-89c5-abc1236ab86f@VI6PPA8.EURP250.PROD.OUTLOOK.COM>" />
</root>
Supported ODATA query parameters
The message trace API supports the following ODATA query parameters to filter and sort the results:
$filter- to filter message traces$top- to specify the number of results to return. Default value is 1000. Range is 1 to 5000.
| Property | Supported operators |
|---|---|
| id | eq |
| messageId | eq |
| status | eq |
| receivedDateTime | range must be specified using the ge and le operators |
| recipientAddress | eq |
| senderAddress | eq |
| subject | contains, startsWith |
| size | - |
| fromIP | eq |
| toIP | eq |
As you can see, only a limited set of query operators are supported for filtering.
Conclusion
Now, we can obtain Exchange Online message trace results via the Microsoft Graph API. Application permissions are supported, which is a significant advantage for automated processes. On the other hand, the API is still in beta, and its capabilities are quite limited compared to what one might expect from the Microsoft Graph API.