DMARCify
Developers

API reference

A small, predictable JSON REST API for automating DMARCify features: organisations, child tenants, domains, DNS providers and DMARC report data.

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/v1

Authentication

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.
Tokens are shown once at creation. Store them in your secret manager — we only persist the hashed prefix and the SHA-256 of the value, so a lost token can't be recovered.

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"
}
StatusWhen
400Malformed body or invalid FQDN.
401Missing or invalid bearer token.
403Token is missing the required scope (read/write).
404Domain, provider or path doesn't exist in this organisation.
405Method not allowed on this path.
409Domain limit for this plan has been reached.
429Rate-limited (1 manual recheck per hour per domain).
5xxServer 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.

GET/v1/organizationsscope: read

List the token organisation and any direct child organisations.

POST/v1/organizationsscope: write

Create a child organisation under the token organisation. Requires the token organisation to be Agency-tier.

Request body
{
  "name": "Client GmbH",
  "inheritBranding": true
}
Response
{
  "organization": {
    "id": "01J...",
    "name": "Client GmbH",
    "parentOrgId": "01J...",
    "brandingLocked": true,
    "effectiveBranding": {
      "brandName": "Agency name",
      "customDomain": "reports.agency.example"
    }
  }
}
GET/v1/organizations/{orgId}scope: read

Fetch the token organisation or one of its direct child organisations.

PATCH/v1/organizations/{orgId}scope: write

Rename an organisation or update Agency branding for organisations that do not inherit locked branding.

Request body
{
  "name": "Agency Operations",
  "branding": {
    "brandName": "Agency",
    "brandLogoUrl": "https://cdn.example.com/logo.png",
    "brandPrimaryColor": "#111827",
    "brandSupportEmail": "support@example.com",
    "customDomain": "reports.example.com"
  }
}

Domains

GET/v1/domainsscope: read

List every domain in the calling org.

Response
{
  "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"
    }
  ]
}
POST/v1/domainsscope: write

Add a new domain. Idempotent on (org, fqdn) — returns 200 with the existing row if it already exists, 201 otherwise.

Request body
{ "fqdn": "example.com" }
GET/v1/domains/{fqdn}scope: read

Fetch a single domain. Refreshes the cached DMARC TXT lookup synchronously.

DELETE/v1/domains/{fqdn}scope: write

Remove a domain. Reports are hard-deleted within 24 hours.

Response
204 No Content
POST/v1/domains/{fqdn}/recheckscope: write

Force 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 — optional teamId.
  • azuretenantId, clientId; secret is the client secret value.
  • godaddyapiKey; secret is the API secret.
  • awsaccessKeyId; secret is the secret access key.
  • google-cloudprojectId, clientEmail; secret is the full service-account JSON.
GET/v1/providers?includeJobs=1scope: read

List connected DNS providers, management scope and optional recent provider jobs.

POST/v1/providers/discover-zonesscope: write

Validate unsaved provider credentials and return manageable zones.

Request body
{
  "provider": "cloudflare",
  "secret": "cf_api_token",
  "metadata": {}
}
Response
{
  "zones": [
    { "name": "example.com", "accountName": "Production" }
  ]
}
POST/v1/providersscope: write

Create a provider. Can discover zones during create, onboard discovered zones as domains, and set domain management scope.

Request body
{
  "provider": "cloudflare",
  "name": "Cloudflare production",
  "secret": "cf_api_token",
  "discoverZones": true,
  "addDiscoveredDomains": true,
  "domainScope": "all",
  "automaticManagement": false
}
GET/v1/providers/{providerId}scope: read

Fetch one connected provider. Returned metadata excludes stored secrets and credential identifiers.

PATCH/v1/providers/{providerId}scope: write

Rename a provider or update management settings. For selected scope, pass managedDomainIds or managedDomains.

Request body
{
  "name": "Cloudflare EU",
  "domainScope": "selected",
  "managedDomains": ["example.com"],
  "automaticManagement": true
}
POST/v1/providers/{providerId}/refresh-zonesscope: write

Refresh cached zones from the upstream DNS provider.

POST/v1/providers/{providerId}/testscope: write

Run a provider connection test and store the job result.

POST/v1/providers/{providerId}/configure-dmarcscope: write

Publish a DMARC TXT record through the provider for a domain enabled in that provider's scope.

Request body
{
  "fqdn": "example.com",
  "policy": "quarantine",
  "pct": 100
}
DELETE/v1/providers/{providerId}scope: write

Delete a provider and its encrypted secret.

Response
204 No Content

Reports & 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.

GET/v1/domains/{fqdn}/summaryscope: read

Daily volume, pass/fail counts and alignment percentages for the selected range.

GET/v1/domains/{fqdn}/sourcesscope: read

Top source IPs with PTR/ASN/country enrichment and our sender categorisation (authorised / forwarded / unknown). Supports ?limit=1..100, default 25.

GET/v1/domains/{fqdn}/reportsscope: read

Most 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.

Build on top of your DMARC data.

Provision a token, point your script at /v1, and you're done. Same endpoints we use ourselves.

One DNS record · 60 seconds to set up