Once your DMARC record includes an rua= tag, participating mail receivers can start sending aggregate reports to that address. They usually arrive once a day as XML attachments, commonly compressed as .xml.gz. Open one and it's not exactly self-explanatory. Here's what's actually in one, and how to read it without giving up and deciding never to open one again.
The shape of the file
Decompress the attachment and you get an XML document. RFC 9990 defines the current aggregate report format, which looks roughly like this:
<feedback xmlns="urn:ietf:params:xml:ns:dmarc-2.0">
<version>1.0</version>
<report_metadata>
<org_name>google.com</org_name>
<email>[email protected]</email>
<report_id>example.com-1731628800</report_id>
<date_range>
<begin>1731628800</begin>
<end>1731715199</end>
</date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<p>quarantine</p>
<sp>quarantine</sp>
<np>reject</np>
<testing>n</testing>
<discovery_method>treewalk</discovery_method>
</policy_published>
<record>
<row>
<source_ip>198.51.100.7</source_ip>
<count>42</count>
<policy_evaluated>
<disposition>quarantine</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
<envelope_from>bounce.example.net</envelope_from>
</identifiers>
<auth_results>
<dkim>
<domain>vendor.example.net</domain>
<selector>outbound</selector>
<result>pass</result>
</dkim>
<spf>
<domain>bounce.example.net</domain>
<scope>mfrom</scope>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>
The version element identifies the report format and currently has a value of 1.0. Five other sections do most of the useful work:
report_metadata: who generated the report, how to contact them, the report's unique ID, and the time window it covers. Thebeginandendvalues are Unix timestamps representing the reporting period, not necessarily the first and last messages observed.policy_published: the DMARC policy configuration the receiver observed for this report. It may not match what's live now. If your policy changed during the reporting period, the receiver may send separate reports for each configuration; otherwise, it should report the final configuration it observed.record→row: each record represents a tuple of source IP, policy evaluation, and authentication identifiers. The same IP can appear in multiple records when the other values differ.counttells you how many messages shared that combination.identifiers: the domains taken from the message, including the domain visible in the From header and, when available, the envelope sender used for SPF.auth_results: the raw SPF and DKIM authentication results, separate from whether those authenticated domains aligned with the From domain for DMARC.
Inside policy_evaluated, disposition is the DMARC-related action the receiver selected for those messages: none, pass, quarantine, or reject. It isn't necessarily the message's final delivery outcome. A receiver can override the published policy, and unrelated spam or security filters can still block a message that passes DMARC.
The distinction that trips everyone up
auth_results tells you whether SPF or DKIM passed at the protocol level. policy_evaluated tells you whether each mechanism passed the additional alignment test required by DMARC.
DMARC passes when at least one of these is true:
- SPF passes and its authenticated domain aligns with the domain in the From header.
- DKIM passes and the signing domain aligns with the domain in the From header.
In the example above, SPF passed for bounce.example.net, and DKIM passed for vendor.example.net. Neither domain aligns with the From domain, example.com, so policy_evaluated reports a failure for both mechanisms. Because the published policy is p=quarantine, the reported disposition is quarantine.
Under relaxed alignment, the authenticated domain and From domain must share the same Organizational Domain—typically the main domain under which the subdomains are registered. For example, bounce.example.com and mail.example.com both belong to the Organizational Domain example.com, so they can align in relaxed mode. Under strict alignment, the domains must be identical, so neither would align with a From domain of example.com unless the authenticated domain were also exactly example.com.
If you're debugging why a trusted vendor is failing DMARC despite "passing SPF," this is one of the most common places the answer is hiding. Check which domain they use for the SMTP MAIL FROM, then determine whether it aligns with the domain in the visible From header. Do the same with the d= domain in their DKIM signature.
Why nobody reads these by hand for long
A single day's report from one receiver might contain a dozen <record> blocks. Multiply that by every participating mail provider, every domain you manage, and every day you collect reports, and the volume gets unreadable fast—especially once you're trying to identify trends over weeks instead of inspecting one day's results.
That's the entire reason report-reading dashboards exist: not because an individual XML file is especially difficult to parse, but because collecting, deduplicating, grouping, and comparing the data doesn't scale past your first week of reports.
MyDMARC ingests these reports automatically and rolls them up by sending source, so you can see pass and fail trends over time instead of opening gzip attachments every morning. If you're still manually decompressing reports from your inbox, that's usually the sign it's time to send your rua= reports somewhere that can parse and organize them for you.