The first DMARC aggregate report is usually a letdown. You published a record with a rua= address, waited a day, and got an email carrying an attachment named something like mail.receiver.example!example.com!1013662812!1013749130.xml.gz. Inside is a page of XML that seems to say either that your mail is fine or that it is on fire, with no obvious way to tell which.
The file is smaller than it looks. An aggregate report has roughly twenty distinct fields, most restricted to a short list of legal values, and once you know those values the report reads in about two minutes. What follows is a walk through the schema as the IETF republished it in May 2026, a rewrite that changed enough that older guides describe elements a current report will never contain.
Where the reports come from#
Nothing arrives unless you asked for it. Reports go only to the addresses named in the rua tag of your DMARC record, so a domain with a policy but no rua gets enforcement and no visibility. The DMARC checker shows the record as receivers read it, the record generator will build one with reporting attached, and our guide to creating a DMARC record covers the tag choices.
Every large mailbox provider generates these independently, from the mail it received, at a cadence RFC 9990 describes as daily or more frequent. One report is therefore one receiver's view of one domain over one window, not a picture of your sending. Six providers means six files a day, each covering a different slice of your audience.
What actually arrives in the mailbox#
The delivery envelope is specified tightly enough to be worth recognising. The subject line must follow a fixed shape:
Report Domain: example.com Submitter: mail.receiver.example Report-ID: <id>
The attachment must be an XML file, and the spec says it should be GZIP compressed, because an uncompressed report can grow past the receiving server's message size limit. The filename is not decorative either. It is built from a required grammar:
receiver!policy-domain!begin-timestamp!end-timestamp[!unique-id].extension
Both timestamps are seconds since the Unix epoch, marking the window the report covers, and the extension must be xml or xml.gz. The optional unique id lets one receiver send several reports for the same window without a filename collision, and a re-sent report must reuse its original filename, so you can safely overwrite rather than double-count.
You will still meet the occasional .zip attachment in practice, even though neither the current specification nor the 2015 one it replaced ever named that extension.
The metadata block: who is reporting, and when#
The XML opens with <report_metadata>, which identifies the sender of the report rather than the sender of your mail: org_name, a contact email, an optional extra_contact_info, a report_id, an optional generator naming the software that produced the file, and a date_range holding begin and end as epoch seconds.
Two of those matter more than they look. The report_id is what you quote if you need to ask a provider about a specific report, and the date range is the only thing telling you what period the counts describe. A report arriving on Tuesday routinely covers Sunday.
What the receiver thought your policy was#
Next comes <policy_published>: the DMARC record as that receiver read it at evaluation time. It holds domain, the policy tags p, sp and np, the alignment modes adkim and aspf, the failure-reporting options fo, a testing flag, and discovery_method.
This is more useful than a restatement of what you published. If you changed your record partway through the window, it shows which version was in force. If a receiver reports p as none when you published quarantine, the record is not resolving the way you think it does.
discovery_method is new and takes two values. psl means the receiver used the public suffix list approach from the 2015 specification to find your organizational domain; treewalk means it used the DNS tree walk that replaced it. For most domains both land in the same place, but under a multi-level suffix the two can disagree about which record applies.
Anatomy of a record#
Then the body: <record> elements, each with three children.
<row>holds thesource_ipthat connected, acount, and thepolicy_evaluatedblock.<identifiers>holdsheader_from, which is required, plus optionalenvelope_fromandenvelope_to.<auth_results>holds the raw authentication outcomes: zero or moredkimresults, each withdomain,selectorandresult, and at most onespfresult withdomain,scopeandresult.
The most misread field is count. It is not a list of messages you can inspect. A record describes a group that shared an identical outcome, and the count says how many fell into it. Twelve thousand messages from one well-behaved mail server collapse into a single record reading <count>12000</count>, which is why these files stay small enough to email.
Two verdicts per row, and why they disagree#
Every record carries the authentication result twice, and the two answer different questions. This is the part that confuses everyone once.
auth_results is the raw verdict, before DMARC has an opinion. An SPF result there can be any of none, pass, fail, softfail, policy, neutral, temperror or permerror; DKIM takes the same list without softfail.
policy_evaluated holds the DMARC verdict, and its dkim and spf elements have only two legal values: pass or fail. They report alignment, not authentication. A message passes DMARC when at least one authenticated identifier aligns with the domain in the From: header. Relaxed alignment, the default, accepts a shared organizational domain; strict requires the two to be identical.
So a perfectly ordinary row can read pass under auth_results and fail under policy_evaluated for the same mechanism. Nothing is broken in the report. Something authenticated correctly for a domain that was not yours, which is the signature of a mailing platform sending on your behalf with its own bounce domain. Our DMARC report viewer flags those rows separately for that reason, because the fix is to get the sender aligned rather than to add it to SPF. The three mechanisms and how they fit together are covered in SPF, DKIM and DMARC explained.
When a receiver overrides your policy#
The disposition element records what the receiver did, and now takes four values: none, pass, quarantine and reject. pass is the newcomer, and it fills a real gap: it means the message passed DMARC while an enforcing policy was in place, which the old schema could not distinguish from none, where there was no enforcement to pass.
When alignment fails but the receiver applies something other than your published policy, the schema requires it to say why in one or more reason elements. The permitted types are local_policy, mailing_list, other, policy_test_mode and trusted_forwarder, each optionally accompanied by a free-text comment.
Reading these saves wasted investigation. Failures tagged mailing_list are a list server rewriting your messages, not an attacker. trusted_forwarder is a relationship the receiver already trusts overriding your reject. local_policy means it simply decided otherwise, and the comment is the only clue why.
What the 2026 rewrite changed#
For eleven years DMARC was RFC 7489, an Informational document. In May 2026 the IETF replaced it with three Standards Track RFCs: 9989 for the core mechanism, 9990 for aggregate reporting, and 9991 for failure reporting. RFC 9989 obsoletes both 7489 and RFC 9091.
Three record tags moved to historic status in the process. pct, which applied your policy to a sampled percentage of mail, is gone, replaced in part by a t tag that marks a policy as being in test mode. rf, which requested a failure report format, is gone. So is ri, which requested an interval between aggregate reports; you can no longer ask for a cadence. Three tags arrived instead: np, psd and t.
The report schema moved with it, to the namespace urn:ietf:params:xml:ns:dmarc-2.0. Two override reason types were dropped, sampled_out and forwarded, with policy_test_mode taking their place. Older reports stay readable throughout, deliberately: almost every element is optional, so a reader that ignores what it does not recognise handles both generations without branching.
Turning reports into a policy decision#
Reading one report tells you about one day. The value is in the sequence, which has a shape.
Publish p=none with a rua address and collect reports for two or three weeks. List every source IP that appears and put a name to each. Some will be your mail servers. Some will be a CRM, an invoicing system, a helpdesk, or a marketing platform somebody signed up for without telling anyone. Some will be forwarding, and a few may be genuinely unauthorised.
Then work the list until every legitimate sender aligns, which usually means DKIM signing with your own domain, and watch the aligned pass rate climb across successive reports rather than trusting one file. When the remaining failures are ones you are content to have blocked, move to quarantine, watch again, and only then to reject. Skipping the observation period is how legitimate mail disappears quietly; our DMARC explainer covers what each policy level instructs a receiver to do.
What the report cannot tell you#
Aggregate reports hold counts and nothing else. No subject lines, no recipients, no message bodies, no per-message timestamps. That detail lives in failure reports, the separate format defined by RFC 9991 and sent to a ruf address, which most large providers stopped sending years ago on privacy grounds.
The source IP is weaker evidence than it feels. Large senders operate shared pools, so an address belonging to a major cloud names the platform, not which of its customers sent the message. Our IP lookup and ASN explorer will turn an unfamiliar address into an operator name, usually enough to recognise a system you already pay for.
And a clean report is not a deliverability report. DMARC measures authentication and alignment; a perfectly aligned domain can still land in spam for reasons of reputation, content or list hygiene that no aggregate report can see.
Opening one#
Our DMARC report viewer reads these files directly. Drop in an .xml, .xml.gz or .zip attachment and it shows the reporting window, the published policy, the message count and aligned pass rate, the disposition breakdown, and every source IP rolled up with its own pass and fail counts.
It picks the format from the file's leading bytes rather than its name, because mail clients rename attachments freely, and it refuses any file declaring a DOCTYPE or defining XML entities, since a report needs neither and both are the entry point for entity expansion attacks. Elements it does not interpret are counted and reported rather than quietly dropped.
The whole thing runs inside the browser tab, and nothing is uploaded. That matters more here than for most tools: an aggregate report is a map of your mail infrastructure, naming every system that sends as you and how much each one sends. It is not a file to hand to a website in exchange for a chart.
Open a DMARC aggregate report
Drop in the XML, GZIP or ZIP attachment from your rua mailbox and read it as a summary instead of markup. Parsed in your browser tab, never uploaded.