---
title: "Bulk Dmarc Reports"
method: POST
path: "/bulk-dmarc-reports"
tags: ["tools"]
---

# Bulk Dmarc Reports

`POST /bulk-dmarc-reports`

## Request

- Base URL: `https://dnsdoctor.dev/api/v1`
- URL: `https://dnsdoctor.dev/api/v1/bulk-dmarc-reports`
- Auth: none declared

Parse 2-20 DMARC aggregate reports in one paid call. **402 on every call.**

Priced per report. Per-report decode/parse failures are rows, not errors;
the batch is settled only when at least one report parsed, so an all-error
batch is returned unsettled.

## Request body

- BulkReportsIn: 2-20 reports, bounded at the model so an oversize body is a 422 at parse.
  - `reports` BulkReportIn[], required
    - `content_base64` string, required
    - `filename` string, nullable

## Response `200`

Successful Response

- BulkReportsOut: The batch. `reports_parsed` is what the settle gate reads. `combined` is one entry per reported domain (a report naming none is grouped under `domain: null`), computed over the pooled rows of every parsed report for it — a report sent twice (same reporter + report id) is pooled once, as intake's duplicate rule — `org_name`/`report_id` are null, the dates are the batch's min/max.
  - `reports` BulkReportRow[], required
    - `index` integer, required
    - `filename` string, nullable, required
    - `status` 'ok' | 'bad_base64' | 'too_large' | 'not_a_report' | 'unparseable' | 'deadline', required
    - `report` ReportParseResponse: One parsed DMARC aggregate report — metadata + per-source aggregates. Zero-persistence: assembled straight from the shared parser's output and discarded after the response. No record strings, no LLM — the read-only acquisition twin of the authenticated backfill upload.
      - `org_name` string, nullable, required
      - `report_id` string, nullable, required
      - `domain` string, nullable, required
      - `published_policy` string, nullable, required
      - `date_begin` string, nullable, required
      - `date_end` string, nullable, required
      - `total_messages` integer, required
      - `aligned_messages` integer, required
      - `aligned_pct` number, required
      - `dispositions` ReportDispositionMix, required: Report-wide message counts by evaluated disposition.
        - `none` integer, required
        - `quarantine` integer, required
        - `reject` integer, required
      - `sources` ReportSourceOut[], required
        - `ip` string, required
        - `total` integer, required
        - `spf_aligned_pct` number, required
        - `dkim_aligned_pct` number, required
        - `aligned_pct` number, required
  - `combined` ReportParseResponse[], required
    - `org_name` string, nullable, required
    - `report_id` string, nullable, required
    - `domain` string, nullable, required
    - `published_policy` string, nullable, required
    - `date_begin` string, nullable, required
    - `date_end` string, nullable, required
    - `total_messages` integer, required
    - `aligned_messages` integer, required
    - `aligned_pct` number, required
    - `dispositions` ReportDispositionMix, required: Report-wide message counts by evaluated disposition.
      - `none` integer, required
      - `quarantine` integer, required
      - `reject` integer, required
    - `sources` ReportSourceOut[], required
      - `ip` string, required
      - `total` integer, required
      - `spf_aligned_pct` number, required
      - `dkim_aligned_pct` number, required
      - `aligned_pct` number, required
  - `reports_parsed` integer, required

## Other responses

- `402`: Payment required — retry with an x402 payment header.
- `422`: Validation Error

---

[API](https://skmtc.dev/dnsdoctor/apis/dns-doctor-api.md) · [All operations](https://skmtc.dev/dnsdoctor/apis/dns-doctor-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dnsdoctor/apis/dns-doctor-api/revisions/1d0114806e76?raw)
