Skip to content

DMARC monitoring

Put a domain under DMARC monitoring from your own code: enroll it, publish the DNS record we hand back, and check when reports start flowing.

POST /v1/dmarc/domains
GET /v1/dmarc/domains
GET /v1/dmarc/domains/{id}
PATCH /v1/dmarc/domains/{id}
DELETE /v1/dmarc/domains/{id}
GET /v1/dmarc/domains/{id}/dns
POST /v1/dmarc/domains/{id}/verify

Every endpoint takes a secret key in the Authorization header — see Authentication.

Three steps, and the first one gives you everything you need for the second:

  1. Enroll the domain with POST /v1/dmarc/domains. The response includes the exact TXT records to publish.
  2. Publish those records in the domain’s DNS.
  3. Verify with POST /v1/dmarc/domains/{id}/verify to confirm we can see them. Reports begin arriving at the next reporting interval — mailbox providers send aggregate reports roughly daily, so allow up to 24–48 hours before the first one lands.
POST /v1/dmarc/domains
Field Type Required Description
domain string Yes The domain to monitor, e.g. example.com.
org_id string No Your own grouping key, echoed back on reads.
is_subdomain boolean No Mark this as a subdomain enrollment.
parent_domain string No The parent, when is_subdomain is true.
ruf_enabled boolean No Also provision a forensic (RUF) address.
tlsrpt_enabled boolean No Also provision a TLS-RPT address.
retention_days integer No How long to keep reports (1–3650).
Terminal window
curl -X POST https://api.tryverifyemail.com/v1/dmarc/domains \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'

201 Created on first enrollment.

{
"created": true,
"domain": {
"id": "665f1c2a9b4e7d0012ab34cd",
"domain": "example.com",
"status": "pending",
"is_subdomain": false,
"parent_domain": null,
"org_id": null,
"ruf_enabled": false,
"tlsrpt_enabled": false,
"retention_days": 90,
"published_policy": null,
"reporting": {
"rua": "a1b2c3d4e5f6...@dmarc.tryverifyemail.com",
"ruf": null,
"tlsrpt": null
},
"created_at": "2026-08-19T10:04:11.000Z",
"updated_at": "2026-08-19T10:04:11.000Z"
},
"dns_records": [
{
"record": "dmarc",
"host": "_dmarc.example.com",
"type": "TXT",
"action": "create",
"value": "v=DMARC1; p=none; rua=mailto:a1b2c3d4e5f6...@dmarc.tryverifyemail.com",
"current": null
}
]
}

dns_records is generate-or-merge, not a blind template. We read whatever is currently published at _dmarc.<domain> first:

  • "action": "create" — nothing is published yet. value is a safe monitoring-only starter (p=none), so enrolling cannot affect delivery.
  • "action": "update" — a DMARC record already exists. value is that record with our reporting address spliced into its rua= list and every one of your existing policy tags preserved (p, pct, sp, adkim, aspf). current shows what is published today so you can diff before applying.

Publish value verbatim at host.

POST /v1/dmarc/domains/{id}/verify

Re-reads the domain’s live DNS and moves its status.

Terminal window
curl -X POST https://api.tryverifyemail.com/v1/dmarc/domains/{id}/verify \
-H "Authorization: Bearer sk_your_api_key"
{
"domain": "example.com",
"state": "waiting",
"status": "verified",
"token_found": true,
"published_policy": {
"p": "none",
"rua": "mailto:a1b2c3d4e5f6...@dmarc.tryverifyemail.com",
"raw_record": "v=DMARC1; p=none; rua=mailto:a1b2c3d4e5f6...@dmarc.tryverifyemail.com"
},
"last_report_received_at": null
}
state Meaning
pending Our reporting address is not in your published _dmarc record yet.
waiting DNS is correct. No reports have arrived yet — this is normal for the first 24–48 hours.
receiving Reports are flowing. Monitoring is live.

A "dns_error": true field means the DNS lookup itself failed transiently. The domain’s status is deliberately left unchanged in that case — a lookup that did not answer is not evidence that your record is gone. Retry.

GET /v1/dmarc/domains/{id}/dns

Returns the same records array as enrollment, re-read against current DNS. Use it to re-check what still needs publishing without creating anything.

GET /v1/dmarc/domains
{ "domains": [ { "id": "665f...", "domain": "example.com", "status": "receiving" } ] }
GET /v1/dmarc/domains/{id}

Returns a single domain object in the shape shown under enroll. Unknown or not yours → 404.

PATCH /v1/dmarc/domains/{id}

Accepts org_id, is_subdomain, parent_domain, retention_days, ruf_enabled and tlsrpt_enabled. At least one field is required.

Setting ruf_enabled or tlsrpt_enabled to true provisions that reporting address if it does not exist yet — call GET /{id}/dns afterwards to pick up the record you now need to publish.

DELETE /v1/dmarc/domains/{id}
{ "deleted": true }

Removes the enrollment and revokes its reporting addresses; reports for the domain are purged. Deleting a domain you do not have enrolled answers {"deleted": false} rather than a 404 — the end state you asked for already holds.