resellerhq

DMARC aggregate report reader

Read the RUA XML your mailbox provider emailed you by source IP, in this tab, without uploading it.

Paste the XML from the report, or upload the .xml or .xml.gz file your mailbox provider sent as an attachment.

Try

Result

The result updates as you fill the form.

What this is

This DMARC aggregate report reader takes the RUA XML a mailbox provider emailed you — RFC 7489’s schema or RFC 9990’s newer one — groups every record by the IP that connected, and names the one line that matters: a source that passed neither SPF nor DKIM alignment, which is unauthenticated mail sent as your domain.

An aggregate report is the file behind the rua= tag in your DMARC record. A receiver that handled mail claiming to be from your domain sends one for a time window, as an .xml or .xml.gz attachment, and it holds one record per combination of connecting IP and authentication outcome: the source_ip, how many messages it sent (count), and what the receiver decided in policy_evaluated — the DKIM and SPF alignment results, and the disposition it applied.

There are two schemas in circulation, and this reads both. RFC 7489 Appendix C is the one every report in the wild uses, in the namespace http://dmarc.org/dmarc-xml/0.1. RFC 9990, published in May 2026, obsoletes it and uses urn:ietf:params:xml:ns:dmarc-2.0: it drops the pct element, makes envelope_from and the whole auth_results/spf element optional, requires a selector whenever a DKIM signature is reported, and adds pass as a disposition value meaning no action was taken under an enforcing policy. The namespace is what tells the two apart, and the result says which one it recognised.

How to read the result

The SPF and DKIM columns are the aligned results from policy_evaluated, not the bare authentication results further down each record. That is the distinction DMARC actually turns on: a message can carry a valid DKIM signature or a passing SPF check for some other domain entirely, and still fail DMARC because neither identifier aligned with the From domain a person sees.

Read the table as a list of senders. A source IP you recognise — your mail provider, your ESP, your invoicing app — should show pass in at least one of the two columns. A source showing fail in both sent mail that nothing authorised, and that is the finding above the table: either a sender of yours you never published, or someone sending as you. A source whose messages did not all get the same outcome shows its most common disposition with (mixed) beside it, and its SPF and DKIM cells break the counts out.

The report never leaves the browser. It is parsed here, in the page, with the text you pasted or the file you chose, and nothing is sent to a server — which is also why this tool cannot go and get the report for you.

Common mistakes

  • Reading the auth_results instead of the policy_evaluated resultsEach record holds both: the raw SPF and DKIM outcomes, and the aligned ones the receiver judged DMARC on. A record can show a passing DKIM signature in auth_results and still fail DMARC, because the signing domain did not align with the header From domain under the adkim mode in force.
  • Treating a quarantine or a reject as proof the policy is workingEnforcement does the same thing to a sender you forgot to authorise as it does to a stranger. Before reading an enforced disposition as a win, check the source IP against the senders you own — mail of your own being thrown away looks identical in a report to an attacker being stopped.
  • Expecting a report to cover all of your mailA report comes from one receiver, for one domain, for one window, and only receivers that send aggregate reports send any. Mail to a receiver that reports nothing is in no report at all, so a quiet report is not evidence that nothing is being sent as you.

Resell mailbox licences for the domains you just checked, under your own brand.

Get started

Questions

What people ask about this tool.

Where do I get the XML file this wants?

It arrives by email, as an attachment, at whatever address your DMARC record’s rua= tag points at. RFC 9990 §3.5.2 says the attachment is an XML file, gzip-compressed as application/gzip with the extension .xml.gz, or text/xml and .xml when it is not compressed. Save the attachment and paste its text here, or upload the file itself — .gz included, which is unzipped in your browser.

Does my report get uploaded to your server?

No. This is one of our instant tools: the parsing runs in the page itself, on every keystroke, with no network call of any kind. Choosing a file reads it in the browser and writes the text into the field exactly as a paste would, and a .gz is decompressed with the browser’s own DecompressionStream — you can open your network panel and watch nothing happen.

What does it mean when one source IP fails both SPF and DKIM?

It means the receiver could not tie those messages to your domain by either mechanism, so DMARC treated them as unauthenticated. Every legitimate sender can be fixed — published in SPF, or signing with DKIM for your domain — so once the sources you recognise all pass, anything still failing is mail being sent as you by someone who cannot sign for you.

Why does the disposition say pass in one report and none in another?

Because the two schemas disagree. RFC 7489 allowed only none, quarantine and reject; RFC 9990 added pass, meaning no action was taken on a message that passed DMARC under an enforcing policy. A none under the old schema and a pass under the new one can describe the same delivered message, which is why this tool tells you which schema it read.

Can it read a forensic or failure report, or a TLS report?

No. A failure report (the ruf= tag) is a different format defined in RFC 9991, and an SMTP TLS report is JSON rather than XML. This reads aggregate reports only: XML whose root element is feedback, in either DMARC namespace. Anything else is reported as not being an aggregate report rather than parsed into numbers that would be wrong.

Sources

Every fixed value this page gives you comes from the provider’s own documentation. These pages change, so each one says the day it was last read here.

API example

Example request
curl -X POST https://163-123-236-68.sslip.io/api/tools/dmarc-aggregate-report-reader \
  -H "Content-Type: application/json" \
  -d '{"report":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<feedback xmlns=\"http://dmarc.org/dmarc-xml/0.1\">\n  <report_metadata>\n    <org_name>enterprise.protection.outlook.com</org_name>\n    <report_id>a1b2c3d4e5</report_id>\n    <date_range><begin>1760313600</begin><end>1760399999</end></date_range>\n  </report_metadata>\n  <policy_published><domain>example.com</domain><p>quarantine</p></policy_published>\n  <record>\n    <row>\n      <source_ip>40.107.220.1</source_ip>\n      <count>6</count>\n      <policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>pass</spf></policy_evaluated>\n    </row>\n    <identifiers><envelope_from>example.com</envelope_from><header_from>example.com</header_from></identifiers>\n    <auth_results><dkim><domain>example.com</domain><result>pass</result></dkim><spf><domain>example.com</domain><scope>mfrom</scope><result>pass</result></spf></auth_results>\n  </record>\n  <record>\n    <row>\n      <source_ip>40.107.220.9</source_ip>\n      <count>3"}'
Example response
{
  "tool": "dmarc-aggregate-report-reader",
  "input": {
    "report": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<feedback xmlns=\"http://dmarc.org/dmarc-xml/0.1\">\n  <report_metadata>\n    <org_name>enterprise.protection.outlook.com</org_name>\n    <report_id>a1b2c3d4e5</report_id>\n    <date_range><begin>1760313600</begin><end>1760399999</end></date_range>\n  </report_metadata>\n  <policy_published><domain>example.com</domain><p>quarantine</p></policy_published>\n  <record>\n    <row>\n      <source_ip>40.107.220.1</source_ip>\n      <count>6</count>\n      <policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>pass</spf></policy_evaluated>\n    </row>\n    <identifiers><envelope_from>example.com</envelope_from><header_from>example.com</header_from></identifiers>\n    <auth_results><dkim><domain>example.com</domain><result>pass</result></dkim><spf><domain>example.com</domain><scope>mfrom</scope><result>pass</result></spf></auth_results>\n  </record>\n  <record>\n    <row>\n      <source_ip>40.107.220.9</source_ip>\n      <count>3"
  },
  "result": {
    "verdict": {
      "tone": "danger",
      "headline": "1 source IP · all authenticated",
      "summary": "6 messages from 1 source IP, in the window this report covers."
    },
    "findings": [
      {
        "id": "report-truncated",
        "severity": "fail",
        "title": "1 record could not be read",
        "detail": "The document opens 2 records but only 1 of them could be read all the way through: 1 is missing its closing tag, its row, or its source_ip. Everything below counts only the records that were whole, so the totals are lower than the real ones.",
        "fix": "Paste the file again from the beginning, or upload the attachment itself rather than copying out of a mail client — a report copied from a preview pane is usually cut off at the end."
      },
      {
        "id": "report-schema",
        "severity": "info",
        "title": "Read as an RFC 7489 report (the dmarc-xml/0.1 namespace)",
        "detail": "RFC 9990 obsoleted RFC 7489 in May 2026 and gave aggregate reports a new namespace, so there are two schemas in circulation. This one declared http://dmarc.org/dmarc-xml/0.1, and is read as RFC 7489 Appendix C."
      },
      {
        "id": "all-authenticated",
        "severity": "pass",
        "title": "Every source IP passed SPF or DKIM alignment",
        "detail": "All 6 messages in this report came from a source that passed at least one aligned check, which is what DMARC asks for. Nothing in this window was sent as example.com without authentication."
      }
    ],
    "parts": [
      {
        "kind": "facts",
        "title": "Report",
        "rows": [
          [
            "Reporting organisation",
            "enterprise.protection.outlook.com"
          ],
          [
            "Report ID",
            "a1b2c3d4e5"
          ],
          [
            "Window begins",
            "2025-10-13 00:00 UTC"
          ],
          [
            "Window ends",
            "2025-10-13 23:59 UTC"
          ],
          [
            "Domain",
            "example.com"
          ]
        ]
      },
      {
        "kind": "table",
        "title": "By source IP",
        "columns": [
          "Source IP",
          "Messages",
          "SPF",
          "DKIM",
          "Disposition"
        ],
        "mono": [
          0
        ],
        "rows": [
          [
            "40.107.220.1",
            "6",
            "pass",
            "pass",
            "none"
          ]
        ]
      }
    ],
    "limits": [
      "This reads the report you give it — it cannot fetch the report itself, since it arrives by email as an attachment, not at a URL.",
      "Neither RFC states a maximum report size, so the 256 KB cap on a pasted or uploaded report is ours, not the specification’s.",
      "RFC 9990 was published in May 2026 and no mailbox provider’s own documentation was read for this page, so which of the two schemas your reporter sends, and how often it sends anything, is not something this tool can tell you.",
      "A report says what one receiver saw in one time window for one domain. Messages no reporting receiver handled are in no report at all."
    ]
  }
}

Related tools

Email authentication

DMARC checker

Read a DMARC record in plain words, and see exactly what a receiver does with mail that fails.

Live DNS checkOpen tool
Email authentication

DMARC record generator

Build a DMARC record step by step, from monitoring through to full enforcement.

Runs in your browserOpen tool

Company names and logos are trademarks of their owners, shown only to identify each service.