---
title: "Reports · API v1"
description: "Aggregate (RUA) reports as received, parsed and classified."
---
# Reports

Aggregate (RUA) reports as received, parsed and classified.

## List aggregate reports

`GET /reports`

Aggregate (RUA) reports received for the organization, newest first by default. Set format=csv or format=json to download the current page as an attachment instead of the paged envelope.

- Permission: `report:read`
- Key scope: `read`, `full`

### Query parameters

| Name | Type | Description |
| --- | --- | --- |
| `domain` | `string` | Keep only reports whose domain contains this text; a substring match, unlike the exact match elsewhere. |
| `org` | `string` | Reporting organization name as printed in the report; a substring match, e.g. google.com. |
| `result` | `string` | pass keeps reports in which every message passed DMARC; fail keeps reports in which at least one message failed. A report with no messages is in neither. One of "pass", "fail". |
| `source_ip` | `string` | Keep only reports that contain exactly this sending IP. |
| `from` | `string` | Earliest date to include, YYYY-MM-DD (UTC). A report is kept when its reporting window starts on or after this day. |
| `to` | `string` | Latest date to include, YYYY-MM-DD (UTC), inclusive. A report is kept when its reporting window ends on or before this day, so a window that straddles the boundary needs a range covering both days. |
| `search` | `string` | Fuzzy match against reporter names, domains and report IDs. Longer terms are truncated. |
| `sort_by` | `string` | Sort column. An unknown value falls back to the default. Default "date_end". One of "date_end", "received", "org_name", "domain", "total_messages", "report_id", "compliance". |
| `sort_dir` | `string` | Sort direction. Default "desc". One of "asc", "desc". |
| `limit` | `integer` | Page size. Values above the maximum are clamped; zero, negative or non-numeric values are ignored and the default applies. Default 50. |
| `offset` | `integer` | Number of rows to skip; combine with limit to page. A negative or non-numeric value is treated as 0. Default 0. |
| `format` | `string` | Download the current page (up to the page limit) as an attachment instead of the JSON envelope. The CSV is a subset of the JSON row: it omits received_at and prints the reporting window as YYYY-MM-DD. One of "csv", "json". |

### Example request

```bash
curl "https://api.dmarcguard.io/api/v1/reports?domain=example.com&org=google.com&source_ip=203.0.113.10&from=2026-01-01&to=2026-01-31" \
  -H "Authorization: Bearer dg_your_key"
```

### Response

| Field | Type | Notes |
| --- | --- | --- |
| `reports` | `object[]` | The requested page, newest first by default. Never null; an empty page is []. |
| `reports[].id` | `string` | Identifier, Crockford Base32. Opaque; never parse it as a number. |
| `reports[].report_id` | `string` | The reporter's own identifier for the report, as printed inside it. Not the id you fetch the report by. |
| `reports[].org_name` | `string` | The organization that sent the report. |
| `reports[].domain` | `string` | The domain the report is about. |
| `reports[].total_messages` | `integer` | Messages the report accounts for. |
| `reports[].compliant_messages` | `integer` | Of those, how many passed DMARC. |
| `reports[].compliance_rate` | `number` | Percentage from 0 to 100 of this report's messages that passed DMARC. |
| `reports[].policy_p` | `string` | The DMARC policy the reporter saw published: none, quarantine or reject. |
| `reports[].date_begin` | `string` (nullable) | RFC 3339 UTC instant with whole seconds, e.g. 2026-09-15T16:03:43Z; null when absent. |
| `reports[].date_end` | `string` (nullable) | RFC 3339 UTC instant with whole seconds, e.g. 2026-09-15T16:03:43Z; null when absent. |
| `reports[].received_at` | `string` (nullable) | RFC 3339 UTC instant with whole seconds, e.g. 2026-09-15T16:03:43Z; null when absent. |
| `total` | `integer` | Reports matching the filter, before paging. |
| `limit` | `integer` | The page size actually applied. |
| `offset` | `integer` | The offset actually applied. |
| `has_any` | `boolean` | True when the organization holds any report at all, ignoring filters; use it for empty states. |

### Errors

| Status | When |
| --- | --- |
| `400` | from or to is not YYYY-MM-DD. |
| `401` | Missing, malformed, revoked, expired or not-entitled API key. |
| `403` | The key's scope or its owner's role does not allow this operation. |
| `429` | Hourly quota or an abuse limit exceeded; the body's code says which. Honour Retry-After. |
| `500` | Unexpected server error; retry with backoff. |
| `503` | A transient failure resolving the organization or reading storage; retry after a short pause. |

Plus the errors every request can return: https://dmarcguard.io/docs/api/v1/#errors

## Report date range

`GET /reports/date-range`

The earliest and latest report dates the organization holds, for building date pickers and backfill loops.

- Permission: `report:read`
- Key scope: `read`, `full`

### Example request

```bash
curl "https://api.dmarcguard.io/api/v1/reports/date-range" \
  -H "Authorization: Bearer dg_your_key"
```

### Response

| Field | Type | Notes |
| --- | --- | --- |
| `earliest` | `string` (nullable) | Calendar day, YYYY-MM-DD (UTC); null when absent. |
| `latest` | `string` (nullable) | Calendar day, YYYY-MM-DD (UTC); null when absent. |

### Errors

| Status | When |
| --- | --- |
| `401` | Missing, malformed, revoked, expired or not-entitled API key. |
| `403` | The key's scope or its owner's role does not allow this operation. |
| `429` | Hourly quota or an abuse limit exceeded; the body's code says which. Honour Retry-After. |
| `500` | Unexpected server error; retry with backoff. |
| `503` | A transient failure resolving the organization or reading storage; retry after a short pause. |

Plus the errors every request can return: https://dmarcguard.io/docs/api/v1/#errors

## Get one aggregate report

`GET /reports/{id}`

The parsed report as the reporter sent it, plus DMARCguard's per-record classification and identifier-alignment verdicts. Field names inside the report object are the report's own elements, re-cased (ReportMetadata, PolicyPublished, Records); take the names from the table below rather than transforming the XML yourself. Its date range is the reporter's epoch seconds.

- Permission: `report:read`
- Key scope: `read`, `full`

### Path parameters

| Name | Type | Description |
| --- | --- | --- |
| `id` (required) | `string` | Report ID as returned by List aggregate reports (Crockford Base32). |

### Example request

```bash
curl "https://api.dmarcguard.io/api/v1/reports/0J1Z2K3M4N5P6Q7R" \
  -H "Authorization: Bearer dg_your_key"
```

### Response

| Field | Type | Notes |
| --- | --- | --- |
| `Version` | `string` | Report format version, 1.0 for every conformant report. |
| `ReportMetadata` | `object` | Who sent the report, when, and how to reach them. |
| `ReportMetadata.OrgName` | `string` | Name of the organization that generated the report. |
| `ReportMetadata.Email` | `string` | Address to contact that organization about the report. |
| `ReportMetadata.ExtraContactInfo` | `string` | Further contact details the reporter chose to include. |
| `ReportMetadata.ReportID` | `string` | The reporter's unique identifier for this report. A resent report reuses it. |
| `ReportMetadata.DateRange` | `object` | The reporting period this report covers, typically one UTC day. |
| `ReportMetadata.DateRange.Begin` | `integer` | Start of the reporting period, in seconds since the Unix epoch (UTC). This object is the reporter's own data, so it keeps the encoding the standard mandates. |
| `ReportMetadata.DateRange.End` | `integer` | End of the reporting period, in seconds since the Unix epoch (UTC). |
| `ReportMetadata.Errors` | `string[]` (nullable) | Problems the reporter hit while reading your DMARC record. Worth reading when a report looks wrong. |
| `ReportMetadata.Generator` | `string` | Name and version of the software that produced the report. |
| `PolicyPublished` | `object` | The DMARC policy the reporter read from your DNS while it handled this mail. |
| `PolicyPublished.Domain` | `string` | The domain whose DMARC record the reporter applied. |
| `PolicyPublished.ADKIM` | `string` | DKIM alignment mode published by the domain: r (relaxed) or s (strict). |
| `PolicyPublished.ASPF` | `string` | SPF alignment mode published by the domain: r (relaxed) or s (strict). |
| `PolicyPublished.P` | `string` | Policy the domain asked receivers to apply: none, quarantine or reject. |
| `PolicyPublished.SP` | `string` | Policy published for subdomains, when the domain publishes one separately. |
| `PolicyPublished.NP` | `string` | Policy published for subdomains that do not exist, when the domain publishes one. |
| `PolicyPublished.PCT` | `integer` | Sampling percentage from the older DMARC specification, 0 to 100. Current reports omit it; the tag was removed from the standard. |
| `PolicyPublished.FO` | `string` | Failure-reporting options the domain published, deciding when a receiver sends a failure report. |
| `PolicyPublished.T` | `string` | Testing flag the domain published, y or n: y asks receivers to report without enforcing the policy. |
| `PolicyPublished.PSD` | `string` | Flag, y or n, marking the record as published by a public suffix domain rather than an ordinary one. Only reporters that send the element supply it. |
| `PolicyPublished.DiscoveryMethod` | `string` | How the reporter found the record: treewalk for the current algorithm, psl for the older public-suffix-list one. Diagnostic only. |
| `Records` | `object[]` (nullable) | One entry per sending IP and authentication outcome. A report always carries at least one. |
| `Records[].Row` | `object` | The sending IP, how many messages it sent, and what the receiver did with them. |
| `Records[].Row.SourceIP` | `string` | IP address the messages were delivered from. |
| `Records[].Row.Count` | `integer` | Messages this entry accounts for. |
| `Records[].Row.PolicyEvaluated` | `object` | What DMARC concluded for these messages. |
| `Records[].Row.PolicyEvaluated.Disposition` | `string` | What the receiver actually did with the messages: none, quarantine or reject. |
| `Records[].Row.PolicyEvaluated.DKIM` | `string` | Whether DKIM produced an aligned pass for DMARC: pass or fail. Not the raw DKIM result. |
| `Records[].Row.PolicyEvaluated.SPF` | `string` | Whether SPF produced an aligned pass for DMARC: pass or fail. Not the raw SPF result. |
| `Records[].Row.PolicyEvaluated.Reason` | `object[]` (nullable) | Why the disposition differs from the published policy, when it does. |
| `Records[].Row.PolicyEvaluated.Reason[].Type` | `string` | Why the receiver departed from your policy: local_policy, mailing_list, trusted_forwarder, policy_test_mode, or other. |
| `Records[].Row.PolicyEvaluated.Reason[].Comment` | `string` | Free text the receiver added about the override. |
| `Records[].Identifiers` | `object` | The domains the policy was matched against. |
| `Records[].Identifiers.EnvelopeTo` | `string` | Recipient domain the messages were addressed to. |
| `Records[].Identifiers.EnvelopeFrom` | `string` | Domain of the envelope sender SPF checked. May be present but empty when the message had no return path. |
| `Records[].Identifiers.HeaderFrom` | `string` | The From domain the recipient saw. DMARC aligns everything against this one. |
| `Records[].AuthResults` | `object` | The raw SPF and DKIM results, before DMARC alignment was applied to them. |
| `Records[].AuthResults.DKIM` | `object[]` (nullable) | One entry per DKIM signature the receiver verified. |
| `Records[].AuthResults.DKIM[].Domain` | `string` | Signing domain from the signature's d= tag. |
| `Records[].AuthResults.DKIM[].Selector` | `string` | Selector from the signature's s= tag, naming the key in DNS. |
| `Records[].AuthResults.DKIM[].Result` | `string` | Raw DKIM verification result: none, pass, fail, policy, neutral, temperror or permerror. Alignment is decided separately. |
| `Records[].AuthResults.DKIM[].HumanResult` | `string` | The reporter's own explanation of a verification failure. |
| `Records[].AuthResults.SPF` | `object[]` (nullable) | The SPF check the receiver ran. |
| `Records[].AuthResults.SPF[].Domain` | `string` | Domain the SPF check was run against. |
| `Records[].AuthResults.SPF[].Scope` | `string` | Which identity was checked: mfrom, the envelope sender, is the only one DMARC uses; older reporters also send helo. |
| `Records[].AuthResults.SPF[].Result` | `string` | Raw SPF verification result: none, neutral, pass, fail, softfail, temperror or permerror. Alignment is decided separately. |
| `Records[].AuthResults.SPF[].HumanResult` | `string` | The reporter's own explanation of a verification failure. |
| `parse_warnings` | `string[]` (nullable, optional) | Ways this report departs from the standard. DMARCguard keeps such a report and flags it rather than dropping it; not part of the report itself. |
| `record_classifications` | `string[]` | DMARCguard's verdict on each record, in the same order as Records: authenticated, likely_forwarded, suspicious or needs_review. |
| `record_alignment` | `object[]` | Which of each record's raw SPF and DKIM results aligned with the From domain, in the same order as Records. |
| `record_alignment[].spf` | `boolean[]` (nullable) | One flag per SPF result on the record, in order, saying whether it aligned with the From domain under the published alignment mode. |
| `record_alignment[].dkim` | `boolean[]` (nullable) | One flag per DKIM result on the record, in order, saying whether it aligned with the From domain under the published alignment mode. |
| `failure_summary` | `object` | How the report's records split by which mechanism failed. |
| `failure_summary.spf_only` | `integer` | Records where only SPF failed to produce an aligned pass. |
| `failure_summary.dkim_only` | `integer` | Records where only DKIM failed to produce an aligned pass. |
| `failure_summary.both` | `integer` | Records where both failed, which is what makes a record fail DMARC. The three buckets are mutually exclusive. |
| `failure_summary.total` | `integer` | Records in the report, failing or not. |

### Errors

| Status | When |
| --- | --- |
| `400` | The id is not a valid identifier. |
| `401` | Missing, malformed, revoked, expired or not-entitled API key. |
| `403` | The key's scope or its owner's role does not allow this operation. |
| `404` | No report with that ID in this organization. |
| `429` | Hourly quota or an abuse limit exceeded; the body's code says which. Honour Retry-After. |
| `500` | Unexpected server error; retry with backoff. |
| `503` | A transient failure resolving the organization or reading storage; retry after a short pause. |

Plus the errors every request can return: https://dmarcguard.io/docs/api/v1/#errors