---
name: dmarc-reporting
description: Query DMARCLoop for DMARC, SPF, DKIM, TLS-RPT and BIMI reporting and records: which domains fail authentication, which sources send as a domain, what blocks p=reject, and what to publish next. Use when the user asks about their email authentication, DMARC reports, sending sources or DNS records in DMARCLoop.
---

# DMARCLoop

DMARCLoop collects DMARC aggregate and TLS reports for an organization's domains and advises each domain towards p=reject. You read it through its API with an organization API key.

## Setup

- **MCP (preferred).** If the user's tools include this server's MCP tools (named like the operations below, for example `get_board`), use them and skip curl. To add the server:

  ```sh
  claude mcp add --transport http dmarcloop https://demo.dmarcloop.com/mcp
  ```

  then `/mcp` to sign in to DMARCLoop and choose the organisation; no key is needed. With an API key instead:

  ```sh
  claude mcp add --transport http dmarcloop https://demo.dmarcloop.com/mcp --header "Authorization: Bearer $DMARC_API_KEY"
  ```

- **curl.** Otherwise call `https://demo.dmarcloop.com/api/v1` with `Authorization: Bearer $DMARC_API_KEY`. The key is in the `DMARC_API_KEY` environment variable. Never print, log or echo it, and never put it in a URL. If it is unset, ask the user to create one under **API keys** in DMARCLoop and export it; don't ask them to paste it into the chat.

```sh
curl -sS -H "Authorization: Bearer $DMARC_API_KEY" https://demo.dmarcloop.com/api/v1/key
```

## How to work

1. **Start with `get_key`** (`GET /api/v1/key`). It returns the `orgId` every other call takes, the key's role, and, for a partner's admin key, the client organizations it reaches (each its own `orgId`).
2. **For "how are we doing" or "what needs attention", call `get_board`.** One call gives every domain's adviser stage, blockers, days in stage and pass rate. Lead with the domains that have blockers or a low pass rate.
3. **Drill in** with `get_domain_overview` (totals, daily series, the policy receivers see), `list_sources` (who sends as the domain, classified), `get_source` (one IP's results by reporter, and raw rows) and `get_adviser` (the next step, its blockers, tasks and what it would affect).
4. **Dates.** Reporting reads take `days` (7, 30 or 90, ending today, UTC) or `from` and `to` (YYYY-MM-DD, at most 90 days, ending no later than today).
5. **Writes need the user's yes.** Before `publish_dns_record`, call `preview_dns_record` with the same body and show the user the records it would publish and any issues; publish only after they agree. `rollback_dns_record` returns to a version from `list_dns_record_versions`. After a change, `check_dns_records` and `refresh_adviser` show its effect. A read-only key can't write: say so rather than retrying.
6. **Explain in plain terms.** Pass rate is the share of messages aligned by SPF or DKIM. A source classified as forwarded usually isn't the domain's own mail. Name senders the API names; don't guess at an unknown source's owner.

## Limits and errors

- 120 requests a minute per key; a `429` carries `Retry-After`. Prefer `get_board` to a call per domain.
- Errors are `{ "error": "<code>" }`: `not_available_to_api_keys` (the app can, the API can't), `feature_not_in_plan` (the plan has no API access), `forbidden` (the key's role), `not_found` (including another organization's ids), `invalid_range`.
- Failure (forensic) reports, members, billing and keys aren't available to API keys.
- The full reference, with every response's schema: `https://demo.dmarcloop.com/api/v1/openapi.json`.

## Operations

| Tool | Request | Needs | What it returns |
|---|---|---|---|
| `get_key` | `GET /api/v1/key` | org.read | The calling key: its organization, role, and the client organizations it reaches. Call this first: every other operation takes an orgId. |
| `list_clients` | `GET /api/v1/orgs/{orgId}/clients` | clients.read | A partner's client organizations. |
| `get_report_addresses` | `GET /api/v1/orgs/{orgId}/report-addresses` | domains.read | The organization's DMARC aggregate, failure and TLS report addresses. |
| `get_board` | `GET /api/v1/orgs/{orgId}/board` | domains.read | Every domain's adviser stage, blockers, days in stage and pass rate, in one call: the organization's and, for a partner's admin key, its clients'. Start here to find what needs attention. |
| `list_domains` | `GET /api/v1/orgs/{orgId}/domains` | domains.read | Domains, each with its totals and daily series for the window. |
| `add_domain` | `POST /api/v1/orgs/{orgId}/domains` | domains.write | Add a domain. |
| `get_domain_overview` | `GET /api/v1/orgs/{orgId}/domains/{domainId}` | reports.read | A domain's overview: totals, daily series, published policy and its changes. |
| `list_sources` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/sources` | reports.read | Every sending source for the domain, classified, busiest first. |
| `get_source` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/sources/{ip}` | reports.read | One source IP: its results by reporter and authentication, and its raw report rows. |
| `get_adviser` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/adviser` | reports.read | The reject adviser's assessment: stage, next step, blockers, tasks and what the next step would affect. |
| `refresh_adviser` | `POST /api/v1/orgs/{orgId}/domains/{domainId}/adviser/refresh` | reports.read | Re-read the domain's DNS and reassess now, after a record changed. |
| `get_dns_records` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/dns` | domains.read | The domain's managed records (DMARC, SPF, TLS-RPT, DKIM, BIMI) and their checks. |
| `preview_dns_record` | `POST /api/v1/orgs/{orgId}/domains/{domainId}/dns/{type}/preview` | domains.read | What publishing a record would do, without saving it: the records to publish, what we'd host, and any issues. `type` is dmarc, spf, tlsrpt, dkim or bimi. |
| `publish_dns_record` | `PUT /api/v1/orgs/{orgId}/domains/{domainId}/dns/{type}` | dns.write | Publish a record as a new version: hosted or self-managed. `type` is dmarc, spf, tlsrpt, dkim or bimi. Preview it first. |
| `list_dns_record_versions` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/dns/{type}/versions` | domains.read | A record's published versions, newest first: what rollback can return to. |
| `rollback_dns_record` | `POST /api/v1/orgs/{orgId}/domains/{domainId}/dns/{type}/rollback` | dns.write | Republish an earlier version of a record as a new version. |
| `get_dns_history` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/dns/history` | domains.read | The domain's DNS history, newest first: each record's value changes with before and after, and status changes and publishes. DMARC and SPF are read hourly, MX, MTA-STS, TLS-RPT and BIMI every six hours. |
| `get_dns_snapshot` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/dns/snapshot` | domains.read | The watched records as they stood at a moment: each one's value then and since when. Only covers what was read from when monitoring started; it restores nothing. |
| `check_dns_records` | `POST /api/v1/orgs/{orgId}/domains/{domainId}/dns/check` | domains.read | Check the domain's records in DNS now. |
| `get_tls_reports` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/mail/tls` | reports.read | TLS reporting (RFC 8460): sessions by day, by policy and reporter, and failures. |
| `list_dkim_keys` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/mail/dkim` | domains.read | DKIM selectors seen for the domain, their keys and key ages. |
| `get_mail_checks` | `GET /api/v1/orgs/{orgId}/domains/{domainId}/mail/checks` | domains.read | The latest MTA-STS and BIMI checks. |
| `list_alerts` | `GET /api/v1/orgs/{orgId}/alerts` | domains.read | Active alerts and recent alert history. |
| `export_csv` | `GET /api/v1/orgs/{orgId}/exports/{kind}.csv` | domains.read | A CSV export: `domains` and `sources` (with `domainId`) need reports.read, `records` domains.read, and `usage` (partners: per-client usage for the month) billing.read. |
| `export_pdf` | `GET /api/v1/orgs/{orgId}/exports/report.pdf` | reports.read | The organization's report as a PDF, under its brand. |
