DMARC monitoring
Free DMARC monitoring for every domain. Receivers such as Gmail, Outlook and Yahoo send a daily aggregate report of every server that sent mail using your domain. Groundmail receives those reports, reads them, and shows you:
- who sends as your domain, grouped by provider (Google, Microsoft 365, Groundmail, SendGrid, Mailchimp and more), with unknown senders flagged as possible spoofing
- pass rates: DMARC, plus SPF and DKIM alignment per source, by day
- a readiness verdict: when every legitimate source has passed DMARC for two weeks, it's safe to move your policy from
p=nonetop=quarantine - a weekly digest email with last week's volume, pass rate, and new or failing sources
Monitoring doesn't change how your mail is delivered. It's free on every plan, and also for domains that don't send through Groundmail.
Base URL: https://api.groundmail.dev
Authorization: Bearer gm_...How it works
- Turn monitoring on for a domain. You get a report address just for that domain, like
k3m9q2xw7hb4npr6@dmarc.groundmail.dev. - Add it to the
rua=tag of your DMARC record. Keep any report addresses you already have; receivers send to all of them. - Receivers send reports once a day, so the first ones arrive within 24 to 48 hours. Reports are kept for 180 days.
Before:
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"After:
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com,mailto:k3m9q2xw7hb4npr6@dmarc.groundmail.dev"With no DMARC record, publish v=DMARC1; p=none; rua=mailto:<your address>. p=none only asks for reports; it never affects delivery.
The API response includes dns.suggested_record: your current record with the address added. If the domain has no record of its own, the record that applies is your organisational domain's (for mail.example.com, _dmarc.example.com), and that's the one to change.
In the dashboard
Open Domains, pick a domain and scroll to DMARC monitoring. Enable monitoring shows the record to publish. If the domain's DNS provider is connected under Settings → Integrations (Cloudflare, Vercel or GoDaddy), one click updates the record for you.
Enable monitoring
POST /domains/{id}/dmarc
{id} is the domain id or name.
curl -X POST https://api.groundmail.dev/domains/example.com/dmarc \
-H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Optional body:
| Field | Type | Description |
|---|---|---|
apply |
cloudflare | vercel | godaddy |
Also write the DMARC record through this connected DNS provider. Only the rua= tag changes. |
digest_enabled |
boolean | Weekly digest email (default true). |
Response 201:
{
"dmarc": {
"domain": "example.com",
"enabled": true,
"status": "waiting",
"report_address": "k3m9q2xw7hb4npr6@dmarc.groundmail.dev",
"rua": "mailto:k3m9q2xw7hb4npr6@dmarc.groundmail.dev",
"dns": {
"host": "_dmarc.example.com",
"record": "v=DMARC1; p=none; rua=mailto:dmarc@example.com",
"rua_present": false,
"policy": "none",
"suggested_record": "v=DMARC1; p=none; rua=mailto:dmarc@example.com,mailto:k3m9q2xw7hb4npr6@dmarc.groundmail.dev"
},
"digest_enabled": true,
"readiness": {
"verdict": "no_data",
"label": "Waiting for reports",
"detail": "Receivers send aggregate reports once a day…",
"days_of_data": 0,
"blocking_sources": []
}
},
"applied": null
}Calling it again is safe: you get the same address back.
Get the report
GET /domains/{id}/dmarc
curl https://api.groundmail.dev/domains/example.com/dmarc \
-H "Authorization: Bearer $GROUNDMAIL_API_KEY"The summary covers the last 30 days:
{
"dmarc": {
"domain": "example.com",
"enabled": true,
"status": "receiving",
"last_report_at": "2026-10-18T00:00:00.000Z",
"window_days": 30,
"summary": {
"messages": 18420,
"dmarc_pass": 18391,
"dmarc_fail": 29,
"pass_rate": 0.9984,
"reports": 41,
"reporters": ["google.com", "Enterprise Outlook", "Yahoo"]
},
"sources": [
{
"key": "groundmail",
"provider": "groundmail",
"name": "Groundmail",
"status": "passing",
"messages": 16210,
"pass_rate": 1,
"spf_aligned_rate": 1,
"dkim_aligned_rate": 1,
"ips": ["209.188.111.11"],
"ip_count": 3
},
{
"key": "unknown:spoof.example",
"provider": "unknown",
"name": "spoof.example",
"status": "unknown",
"messages": 29,
"pass_rate": 0
}
],
"daily": [{ "date": "2026-10-17", "messages": 640, "dmarc_pass": 639, "pass_rate": 0.9984 }],
"readiness": {
"verdict": "ready_quarantine",
"label": "Safe to move to quarantine",
"detail": "Every legitimate source has passed DMARC for 14+ days…",
"days_of_data": 30,
"blocking_sources": []
}
}
}When monitoring is off, enabled is false and only dns (the current record) is returned.
Sources
Each source groups the servers of one provider, recognised from reverse DNS, the DKIM signing domain and published address ranges.
status |
Meaning |
|---|---|
passing |
98% or more of its mail passes DMARC (a little forwarded mail always fails). |
failing |
A known provider, or a server that holds your key, whose mail fails DMARC. Set up SPF or DKIM for it before you enforce. |
unknown |
An unrecognised server whose mail fails DMARC: spoofing, or a forwarder. Enforcement is what stops it. |
Readiness
verdict |
Meaning |
|---|---|
no_data |
No reports yet. |
collecting |
Every legitimate source passes so far; wait for two weeks of reports, so senders that only mail weekly or monthly show up. |
fix_sources |
A legitimate source fails DMARC (blocking_sources). Fix it first, or its mail goes to spam once you enforce. |
ready_quarantine |
Every legitimate source passed for 14+ days. Change p=none to p=quarantine. |
ready_reject |
Same, under p=quarantine. Change it to p=reject. |
enforced |
p=reject. |
Weekly digest
Every Monday the domain's owner gets a digest: last week's messages, pass rate, new and failing sources, and the readiness verdict. Turn it off in the dashboard or with:
PATCH /domains/{id}/dmarc
{ "digest_enabled": false }Stop monitoring
DELETE /domains/{id}/dmarc
Reports sent to the address are ignored from now on. Remove it from your DMARC record's rua= tag too.
SDK, CLI and MCP
import { Groundmail } from 'groundmail'
const groundmail = new Groundmail(process.env.GROUNDMAIL_API_KEY!)
const { dmarc } = await groundmail.dmarc.enable('example.com')
console.log(dmarc.dns.suggested_record)
const report = await groundmail.dmarc.get('example.com')
if (report.dmarc.enabled) console.log(report.dmarc.readiness.label)groundmail dmarc enable example.com # report address and the record to publish
groundmail dmarc enable example.com --apply cloudflare
groundmail dmarc status example.com # sources, pass rates, readiness (--json for the API response)
groundmail dmarc digest example.com --off
groundmail dmarc disable example.comMCP tools: enable_dmarc_monitoring and get_dmarc_report.
Scopes
| Route | Scope |
|---|---|
GET /domains/{id}/dmarc |
domains:read |
POST, PATCH, DELETE /domains/{id}/dmarc |
domains:write |