"I never got that email" is a classic helpdesk ticket, and guessing wastes everyone's time. Message trace shows whether Exchange Online received a message, what it did with it and where it ended up. In this post I'll walk through tracing in the Exchange admin center and with the V2 PowerShell cmdlets, how to read the results, and what to do next for each outcome.
Prerequisites#
- Permissions: the Exchange Administrator role in Microsoft Entra ID, or membership of the Organization Management role group in Exchange Online.
- Facts from the user: sender, recipient, roughly when it was sent (and in which time zone) and the subject. If anyone has a copy, the
Message-IDfrom the headers is the most precise filter there is. - For PowerShell: the ExchangeOnlineManagement module, version 3.7.0 or later.
Traces covering 10 days or less return results almost immediately. You can look back up to 90 days, but each near real-time query covers at most 10 days; anything wider becomes a downloadable report.
Step 1: Run a trace in the Exchange admin center#
- In the Exchange admin center (
admin.exchange.microsoft.com), go to Mail flow › Message trace. The Exchange message trace link in the Microsoft Defender portal opens the same page. - Select Start a trace. The defaults search all senders and recipients for the last two days.
- Enter the Senders and/or Recipients. External addresses work, and so does one wildcard per value, such as
*@fabrikam.com. - Set the Time range. Switch to Custom time range to pick a time zone and exact start and end times.
- Under Detailed search options, narrow it with a subject filter (starts with, ends with or contains), a delivery status, the Message ID (including the angle brackets) or the direction.
- Keep the report type as Summary report and select Search.
Step 2: Read the delivery status#
| Status | What it means | Next step |
|---|---|---|
| Delivered | Handed to the mailbox or the recipient's mail server | Mail flow worked. Check Junk Email, Inbox rules and the Other tab. |
| Failed | Not delivered | Open the details; the Fail event carries the reason and NDR code. |
| Pending | Delivery is being attempted or retried | Look at the Defer events and the To IP; usually a receiving-side problem. |
| Quarantined | Held as spam, bulk or phishing | Review it on the Quarantine page of the Microsoft Defender portal. |
| Filtered as spam | Identified as spam and rejected or blocked, not quarantined | Check the verdict in the details and which anti-spam policy applied. |
| Expanded | A group was expanded to its members | Trace the individual members. |
| Getting status | Received moments ago, no data yet | Wait a few minutes and refresh. |
You may also see Recalled when the sender used Message Recall. Two caveats: you can only filter on Pending, Quarantined and Filtered as spam in searches shorter than 10 days, and the reported status can trail reality by five to ten minutes.
Step 3: Open the message trace details#
Click a row (not its round check box) to open the details. Expand Message events and read them in order: Receive first, then any processing, ending in Deliver, Send, Fail or Defer. Other events worth recognizing are Expand (group expansion), Transfer (recipients moved to a split copy of the message), Resolved (redirected to another address) and DLP rule. Even a healthy message logs several events.
More information adds the Message ID, size, From IP and To IP. To pull every record that shares the same Message ID (forwarding, group expansion, mail flow rules), tick the row's check box and choose Find related.
Step 4: Trace from PowerShell with the V2 cmdlets#
Get-MessageTraceV2 and Get-MessageTraceDetailV2 replace Get-MessageTrace and Get-MessageTraceDetail, which Microsoft is retiring, so update any old scripts. The rules have changed:
- Data goes back 90 days, but one query can span at most 10 days. Without dates you get the last 48 hours.
- Results default to 1,000 rows and
-ResultSizegoes up to 5,000.-Pageand-PageSizeare gone. - Timestamps are in UTC, and a tenant can run 100 queries in any five-minute window.
Connect-ExchangeOnline
# Failed messages to one recipient in the last 24 hours
$trace = Get-MessageTraceV2 -RecipientAddress user@contoso.com -Status Failed `
-StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date)
$trace | Format-Table Received, SenderAddress, Subject, Status
# Event-by-event detail for the same messages
$trace | Get-MessageTraceDetailV2 | Format-ListValid -Status values are Delivered, Expanded, Failed, FilteredAsSpam, GettingStatus, Pending and Quarantined. When a result set is larger than one batch, run the query again with -EndDate set to the last row's Received value and -StartingRecipientAddress set to its RecipientAddress.
Step 5: Go further back with downloadable reports#
For ranges longer than 10 days, or when you need more detail, the admin center builds two CSV reports from archived trace data: the Enhanced summary report (adds direction, original client IP and connector) and the Extended report (full routing and event detail). Both need at least a sender, recipient or Message ID. Choose Next, confirm the title and notification address, select Prepare report, then download it from the Downloadable reports tab. Allow several hours, and expect the most recent 24 hours to be missing from the archive.
In PowerShell, the equivalent is a historical search, which covers messages up to 90 days old:
Start-HistoricalSearch -ReportTitle "Partner invoices" -ReportType MessageTraceDetail `
-StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date).AddDays(-12) `
-SenderAddress billing@fabrikam.com -NotifyAddress admin@contoso.com
Get-HistoricalSearch | Format-Table ReportTitle, Status, RowsVerify: what the results usually tell you#
- No record at all: the message never reached Exchange Online. Recheck the address spelling, time range and time zone, confirm your MX record and connectors, then ask the sender's admin for their outbound logs.
- Delivered but not visible: look at Junk Email, Inbox rules, Focused Inbox and any auto-delete or retention settings. Message trace can't see what happens after delivery.
- Failed: the NDR code in the Fail event is your starting point.
- Quarantined or Filtered as spam: decide whether it's a false positive, release it if appropriate and submit it to Microsoft for review.
- Pending for hours: the receiving server is deferring. If retries eventually run out, the sender gets an NDR.
Tips & gotchas#
- The admin center shows times in the time zone from your Exchange account settings, while PowerShell returns UTC. Mixing the two up is an easy way to search the wrong window.
- For a message sent to more than 1,000 recipients, filter by
-MessageTraceIdto get complete results. - Prefer
-SubjectFilterType StartsWithorEndsWithoverContains; Microsoft recommends them for performance. - Historical searches are capped at 250 per tenant in 24 hours, and cancelled ones still count.