Overview
The DMARCify API is organised around standard HTTP verbs and returns JSON. All requests are made over HTTPS to https://app.dmarcify.dev. The current version is v1; every path is prefixed with /v1.
- JSON request bodies, JSON response bodies (UTF-8).
- Errors follow RFC 7807 with
application/problem+json. - Timestamps are ISO-8601 in UTC; durations are integers in seconds.
- An unauthenticated discovery document is available at
GET /v1. - Agent clients can use the same bearer token through the MCP server.
curl https://app.dmarcify.dev/v1Authentication
Every request must include a bearer token in the Authorization header. Tokens are created from Settings → API tokens in the dashboard and are scoped to a single organisation.
Authorization: Bearer dm_live_...Tokens carry one or both of two scopes:
read— list organisations and domains, fetch summaries, sources, reports.write— create child tenants, add/delete domains, trigger DNS rechecks, connect and manage DNS providers.
Errors
Errors are returned as application/problem+json following RFC 7807. The HTTP status code is also reflected in the body's status field so you only need to read one place.
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "Domain not found: example.com"
}| Status | When |
|---|---|
| 400 | Malformed body or invalid FQDN. |
| 401 | Missing or invalid bearer token. |
| 403 | Token is missing the required scope (read/write). |
| 404 | Domain, provider or path doesn't exist in this organisation. |
| 405 | Method not allowed on this path. |
| 409 | Domain limit for this plan has been reached. |
| 429 | Rate-limited (1 manual recheck per hour per domain). |
| 5xx | Server error — retried automatically by the dashboard; safe to retry with exponential backoff. |
Rate limits
Reads are not currently rate-limited per-token, but excessive parallelism may be throttled to protect the underlying datastore. Manual DNS rechecks share a 1-per-hour budget with the dashboard recheck button and the MCP recheck tool. Provider credential verification and zone refreshes call upstream DNS provider APIs; retry those with exponential backoff.
Organizations
API tokens are scoped to one organisation. Agency-tier parent organisations can use that token to create and inspect child tenants; child tenants inherit branding when created with inheritBranding.
/v1/organizationsscope: readList the token organisation and any direct child organisations.
/v1/organizationsscope: writeCreate a child organisation under the token organisation. Requires the token organisation to be Agency-tier.
{
"name": "Client GmbH",
"inheritBranding": true
}{
"organization": {
"id": "01J...",
"name": "Client GmbH",
"parentOrgId": "01J...",
"brandingLocked": true,
"effectiveBranding": {
"brandName": "Agency name",
"customDomain": "reports.agency.example"
}
}
}/v1/organizations/{orgId}scope: readFetch the token organisation or one of its direct child organisations.
/v1/organizations/{orgId}scope: writeRename an organisation or update Agency branding for organisations that do not inherit locked branding.
{
"name": "Agency Operations",
"branding": {
"brandName": "Agency",
"brandLogoUrl": "https://cdn.example.com/logo.png",
"brandPrimaryColor": "#111827",
"brandSupportEmail": "support@example.com",
"customDomain": "reports.example.com"
}
}Domains
/v1/domainsscope: readList every domain in the calling org.
{
"domains": [
{
"id": "01J…",
"fqdn": "example.com",
"status": "verified",
"policy": { "p": "reject", "sp": "reject", "pct": 100, "raw": "v=DMARC1; p=reject; rua=…", "ruaOk": true },
"rua": "mailto:r-abc123@dmarcify.dev",
"dnsCheckedAt": "2026-05-19T09:12:00.000Z",
"lastReportAt": "2026-05-20T03:11:00.000Z",
"tags": ["production"],
"createdAt": "2026-04-02T11:24:00.000Z"
}
]
}/v1/domainsscope: writeAdd a new domain. Idempotent on (org, fqdn) — returns 200 with the existing row if it already exists, 201 otherwise.
{ "fqdn": "example.com" }/v1/domains/{fqdn}scope: readFetch a single domain. Refreshes the cached DMARC TXT lookup synchronously.
/v1/domains/{fqdn}scope: writeRemove a domain. Reports are hard-deleted within 24 hours.
204 No Content/v1/domains/{fqdn}/recheckscope: writeForce a fresh DNS lookup. Rate-limited to 1 per hour per domain.
Providers
Provider endpoints manage the same DNS integrations as the dashboard. Secrets are accepted only on create/discovery calls and are never returned. Provider-specific metadata depends on the provider:
cloudflare— no metadata.vercel— optionalteamId.azure—tenantId,clientId; secret is the client secret value.godaddy—apiKey; secret is the API secret.aws—accessKeyId; secret is the secret access key.google-cloud—projectId,clientEmail; secret is the full service-account JSON.
/v1/providers?includeJobs=1scope: readList connected DNS providers, management scope and optional recent provider jobs.
/v1/providers/discover-zonesscope: writeValidate unsaved provider credentials and return manageable zones.
{
"provider": "cloudflare",
"secret": "cf_api_token",
"metadata": {}
}{
"zones": [
{ "name": "example.com", "accountName": "Production" }
]
}/v1/providersscope: writeCreate a provider. Can discover zones during create, onboard discovered zones as domains, and set domain management scope.
{
"provider": "cloudflare",
"name": "Cloudflare production",
"secret": "cf_api_token",
"discoverZones": true,
"addDiscoveredDomains": true,
"domainScope": "all",
"automaticManagement": false
}/v1/providers/{providerId}scope: readFetch one connected provider. Returned metadata excludes stored secrets and credential identifiers.
/v1/providers/{providerId}scope: writeRename a provider or update management settings. For selected scope, pass managedDomainIds or managedDomains.
{
"name": "Cloudflare EU",
"domainScope": "selected",
"managedDomains": ["example.com"],
"automaticManagement": true
}/v1/providers/{providerId}/refresh-zonesscope: writeRefresh cached zones from the upstream DNS provider.
/v1/providers/{providerId}/testscope: writeRun a provider connection test and store the job result.
/v1/providers/{providerId}/configure-dmarcscope: writePublish a DMARC TXT record through the provider for a domain enabled in that provider's scope.
{
"fqdn": "example.com",
"policy": "quarantine",
"pct": 100
}/v1/providers/{providerId}scope: writeDelete a provider and its encrypted secret.
204 No ContentReports & sources
Read endpoints accept either an absolute from/to pair (Unix seconds) or a shorthand range=7d / 30d / 90d (up to 366 days). Default is the last 30 days.
/v1/domains/{fqdn}/summaryscope: readDaily volume, pass/fail counts and alignment percentages for the selected range.
/v1/domains/{fqdn}/sourcesscope: readTop source IPs with PTR/ASN/country enrichment and our sender categorisation (authorised / forwarded / unknown). Supports ?limit=1..100, default 25.
/v1/domains/{fqdn}/reportsscope: readMost recent raw aggregate reports for the domain. Supports ?limit=1..100, default 50.
Versioning
The path-versioned prefix (/v1) is stable. Breaking changes ship behind a new prefix (/v2) and the previous version is supported for at least 12 months. Additive changes — new fields, new endpoints — can land in /v1 at any time and should be treated as non-breaking; please ignore unknown fields.
Found a bug or have a feature request? Email hello@dmarcify.dev.
