21 June 2026 · 15 min read
What the API is for
DMARC Engine has a full HTTP API behind the dashboard. Anything you can do by clicking, you can also do with a script: list your domains, read their DMARC, SPF, DKIM, MTA-STS and BIMI configuration, pull aggregate-report analytics, raise or resolve alerts, manage your team and read your billing history. The dashboard at https://app.dmarcengine.com calls exactly these endpoints, so there is no second-class "public" API; you reach the same surface the product itself uses.
This reference covers the two things you need to use it well: how to create and manage an API key from your account settings, and what each group of endpoints does, with copy-and-paste examples. It assumes you are already set up. If you have not added a domain yet, start with getting started and adding your first domain, then come back here to automate.
The API is for automation and integration: provisioning domains from your own onboarding flow, exporting compliance data into a warehouse or a dashboard of your own, wiring alerts into a system that does not speak one of the built-in channels, or scripting a bulk change across many domains. For one-off checks you do not need a key at all; the free DMARC checker, SPF checker, DKIM checker, MTA-STS checker and BIMI checker run read-only lookups in the browser.
The base URL for every endpoint is:
https://app.dmarcengine.com
Every path below is relative to that base. So GET /api/domains means GET https://app.dmarcengine.com/api/domains.
Creating an API key
API keys live in your account settings, on a dedicated tab. To get there, open the dashboard, click your account area and go to Settings. Settings is split into seven tabs across the top: Profile, Organization, Security, Notifications, API Keys, Team and Billing. Select API Keys.
The API Keys tab shows a table of every key on your organisation. The columns are:
- Name: the human label you gave the key, so you can tell one from another.
- Permissions: whether the key is read (it can fetch data but not change anything) or read-write (it can also create, update and delete).
- Created: when the key was made.
- Last used: when the key last authenticated a request, which is the quickest way to spot a key nothing is using any more.
- Per-row actions to Regenerate or Delete the key.
To make a new one, click Create API Key. A modal opens with these fields:
- Key Name: a short, descriptive label, for example
ci-deployoranalytics-export. Choose something that tells future-you what the key is for; you cannot see the secret again later, so the name is how you will recognise it. - Permissions: a set of checkboxes that decide what the key may do. Grant the minimum the integration needs. If a script only reads analytics, give it read access and leave write off. A key that has to create domains or change DNS needs write.
Click create, and the modal shows you the raw key once, and only once. This is the single most important thing to understand about the whole flow: the full secret is displayed a single time, at the moment of creation, and is never shown again. Only a SHA-256 hash of the key is stored, so even DMARC Engine cannot reveal it later. Copy it immediately and store it somewhere safe (a secrets manager, your CI provider's encrypted variables, a password manager). If you close the modal without copying it, the key is not recoverable; you will have to delete it and create another. The table afterwards shows only the name and metadata, never the secret.
Treat an API key like a password. Anyone holding it can act on your organisation with whatever permissions you granted. Do not commit keys to source control, paste them into chat, or email them. Keep separate keys for separate uses so you can revoke one without breaking the others.
Permissions: read versus read-write
The permission you pick maps directly onto the HTTP method an endpoint uses:
- A read key can call
GETendpoints. It can list domains, read a DMARC record, pull analytics and so on. It cannot change anything. - A read-write key can additionally call the mutating methods (
POST,PUT,PATCH,DELETE). That is what you need to add a domain, edit an SPF record, create a DKIM selector or resolve an alert.
Some endpoints are admin-only regardless of the key's read or write setting, because they touch the organisation itself: changing organisation settings, managing team members and managing API keys. Those require a key (or session) belonging to an admin. See team and roles for how admin, editor and viewer differ.
Regenerating and deleting keys
Two actions sit on every row of the API Keys table.
- Regenerate issues a fresh secret for the same key entry, keeping its name and permissions, and invalidates the old secret straight away. Use this when a key may have leaked, or on a routine schedule, without having to repoint everything to a new name. As with creation, the new secret is shown once; copy it and update wherever the key is stored, because the old value stops working the instant you regenerate.
- Delete removes the key entirely and permanently. Any request still using it will start failing with an authentication error. Use this to retire an integration for good. The Last used column is your friend here: a key that has not been used in months is usually safe to delete.
There is no separate "disable" state; the model is deliberately simple. To rotate, regenerate. To retire, delete.
Authenticating your requests
There are two ways to authenticate, and which one you use depends on whether you are a browser or a script.
- Session cookie. When you sign in to the dashboard, the server sets a
sessioncookie that the browser sends on every request automatically. This is how the dashboard itself is authenticated. You will not use this from a script. - API key. For your own integrations, send the key in the
x-api-keyrequest header. This is the method to use everywhere outside the browser.
A minimal authenticated call looks like this:
curl https://app.dmarcengine.com/api/domains \
-H "x-api-key: YOUR_API_KEY"
The CSRF rule (browser sessions only)
There is one extra rule that catches people out, so it is worth stating plainly. Mutating requests (POST, PUT, PATCH, DELETE) that authenticate with a session cookie must also send an X-CSRF-Token header whose value matches the csrf cookie set at login. This is a cross-site-request-forgery protection for the browser.
It does not apply to API-key requests. If you authenticate with x-api-key, you do not need a CSRF token at all, for any method. So for scripting, use an API key and ignore CSRF entirely; it is only relevant if you are driving the product through a logged-in browser session.
What unauthenticated requests get
Almost every endpoint requires authentication. The exceptions are the health checks and the authentication endpoints themselves (signup, login, password reset). Everything else returns an authentication error if the key is missing, wrong, deleted or regenerated out from under you.
Reading the endpoint list
The sections below group the endpoints by what they touch. Where an endpoint accepts a request body, it is JSON. Path parameters are written :id and mean a domain identifier you got back from a list call. Query parameters are listed where they apply.
A note on method notation: GET/PUT /api/domains/:id/dmarc means the same path answers both a GET (read the current value) and a PUT (replace it). Read keys can use the GET; only read-write keys can use the PUT.
Auth endpoints
These manage sessions and passwords. Scripts rarely need them, because an API key already authenticates you, but they are listed for completeness and for anyone building a sign-in flow on top of the platform.
- POST
/api/auth/signupcreates a new organisation and its first admin user. Body:{ "orgName": "Acme Inc", "name": "John", "email": "john@acme.com", "password": "..." }. - POST
/api/auth/loginsigns a user in. Body:{ "email": "john@acme.com", "password": "..." }. It returns the user and organisation, and sets thesessionandcsrfcookies described above. - POST
/api/auth/logoutdestroys the current session. - GET
/api/auth/mereturns the currently authenticated user. Handy as a quick "is this credential valid?" probe. - POST
/api/auth/forgot-passwordstarts a password reset. Body:{ "email": "john@acme.com" }. It always returns{ "sent": true }, on purpose, so that an outsider cannot use it to discover which emails have accounts. It is rate limited to three requests a minute. - POST
/api/auth/reset-passwordcompletes the reset with the token from the email. Body:{ "token": "...", "password": "newpassword" }.
Domain endpoints
This is the core of the API: the domains you protect and their lifecycle.
- GET
/api/domainslists every domain on your organisation. This is usually your first call, because theidof each domain is what the per-domain endpoints below need. - POST
/api/domainsadds a domain. Body:{ "domain": "example.com", "group": "Production" }. Thegroupis the optional domain group it belongs to, the same grouping you see in the dashboard. This is the scripted equivalent of the add-domain wizard in adding your first domain. - GET
/api/domains/:idreturns one domain in full, including the status of each of its five services. Use it to check, for example, whether DKIM is verified before you flip a policy. - DELETE
/api/domains/:idremoves a domain from your account. - POST
/api/domains/:id/verifytriggers verification of domain ownership via the DNSTXTrecord. Call it after you have published the verification token the platform gave you. Verification is covered in delegating your DNS.
Here is the create-then-verify pair in practice:
# Add a domain
curl https://app.dmarcengine.com/api/domains \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "example.com", "group": "Production" }'
# After publishing the TXT token, verify ownership
curl -X POST https://app.dmarcengine.com/api/domains/DOMAIN_ID/verify \
-H "x-api-key: YOUR_API_KEY"
DNS record endpoints
These read and write the actual authentication records for a domain. Each maps to one of the five standards and to its dashboard page.
- GET/PUT
/api/domains/:id/dmarcreads or replaces the DMARC policy. ThePUTbody looks like{ "policy": "reject", "subdomain_policy": "reject", "pct": 100, "rua": "mailto:dmarc@example.com" }, wherepolicyis the main policy (none,quarantineorreject),subdomain_policyis the policy for subdomains,pctis the percentage of mail the policy applies to, andruais where aggregate reports are sent. Treat changes here with care; movingpolicytorejectenforces. The background is in hosted DMARC and understanding your DMARC record, and the safe path is in the enforcement journey. - GET/PUT
/api/domains/:id/spfreads or replaces the SPF mechanisms. ThePUTbody carries amechanismsarray, for example{ "mechanisms": [{"type": "include", "value": "_spf.google.com", "qualifier": "+"}] }. The platform handles flattening and the ten-lookup limit for you; see hosted SPF and how SPF flattening works. - GET/POST/PATCH/DELETE
/api/domains/:id/dkimlists, creates, updates and removes DKIM selectors. DKIM key pairs are generated in your browser (via WebCrypto): the private key is shown to you once and is never sent to or stored by DMARC Engine, so you install it in your own sending platform, which does the signing. DMARC Engine publishes only the public key through the CNAME delegation. Because of this, thePOSTbody must include the public key you generated:{ "selector": "google", "keyType": "rsa", "keySize": 2048, "publicKey": "..." }. ThePATCHmethod toggles the RFC 6376t=ytesting flag on an existing selector, which lets you run a new selector in test mode before you rely on it. More on how this works in hosted DKIM. - GET/PUT
/api/domains/:id/mtastsreads or replaces the MTA-STS policy. See hosted MTA-STS. - GET/PUT
/api/domains/:id/bimireads or replaces the BIMI record. BIMI only displays once DMARC is enforced; see hosted BIMI.
A read of one record is as simple as:
curl https://app.dmarcengine.com/api/domains/DOMAIN_ID/dmarc \
-H "x-api-key: YOUR_API_KEY"
A full reference for the values these records can hold is in the DNS record reference, and if a tag is unfamiliar the glossary explains it.
Analytics endpoints
This is where you export the data behind the home screen and the analytics page.
- GET
/api/analytics/statsreturns aggregate-report statistics. Query parameters:domainIdto scope to one domain,startDateandendDateto set the window, andtypewhich selects the shape of the data:dailyfor the day-by-day pass and fail trend,sourcesfor a breakdown by sending source, andgeofor a breakdown by geography. - POST
/api/analytics/ingestingests stats manually, for advanced cases where you are feeding data in yourself rather than relying on the receivers' reports.
To pull the last month of daily figures for one domain:
curl "https://app.dmarcengine.com/api/analytics/stats?domainId=DOMAIN_ID&type=daily&startDate=2026-05-23&endDate=2026-06-22" \
-H "x-api-key: YOUR_API_KEY"
If you have a raw report you want to read rather than ingest programmatically, the browser-based DMARC report analyzer does it without a key.
Reports endpoints
- GET/POST/DELETE
/api/reportslists, schedules and removes scheduled reports, the same recurring summaries you can configure in the dashboard. - POST
/api/reports/uploaduploads a DMARC aggregate (RUA) report so the platform can parse it into your analytics. It acceptsapplication/xmlfor a plain report orapplication/gzipfor the compressed form that most receivers send.
Alerts endpoints
These drive the alerting you would otherwise manage in the dashboard. The full feature is documented in setting up alerts.
- GET
/api/alertslists alerts. Query parameters:status(active,acknowledgedorresolved), pluslimitandoffsetfor paging. - POST
/api/alertscreates an alert. - PATCH
/api/alertsupdates an alert's status, for example{ "alertId": "...", "status": "resolved" }. This is how you resolve an alert from a script once you have dealt with whatever it flagged. - GET/POST/PATCH
/api/alerts/ruleslists, creates and updates alert rules, the conditions that decide when an alert fires in the first place.
Settings endpoints
These mirror the Settings tabs. Several are admin-only for the mutating method, as noted.
- GET/PATCH
/api/settings/orgreads or updates organisation settings, the same fields as the Organization tab (Organization Name, Organization Email, Timezone). ThePATCHis admin-only. - GET/POST/DELETE
/api/settings/teamlists, invites and removes team members, mirroring the Team tab. ThePOSTandDELETEare admin-only. Roles are covered in team and roles. - GET/POST/PATCH
/api/settings/api-keysmanages API keys, the programmatic version of the API Keys tab you used above. It supportslimitandoffsetpaging, and thePOSTandPATCHare admin-only. As in the UI, the secret is only returned at creation. - GET/POST
/api/settings/channelslists and creates outbound notification channels of typeemail,slack,webhookorpagerduty, matching Create Notification Channel on the Notifications tab. These are the channels alerts are pushed to; there are no inbound webhooks, so for automation poll the REST endpoints above. - GET
/api/settings/billingreturns your billing history, the same read-only invoice records shown on the Billing tab. Signup is free and self-serve and your account is fully usable without payment; there is no in-product checkout or plan management, so to change or cancel a paid arrangement you contact the team.
The remaining Settings tabs, Profile (your Profile Name and read-only Email) and Security (two-factor authentication, sessions, password change and the login activity log), are managed in the dashboard rather than through these endpoints. For hardening your own access, see security and 2FA.
Other endpoints
A handful of utility endpoints round things out.
- GET
/api/threatslists threat events. Query parameters:severity,domainId,limit,offset. This is the data behind the Threat events detected figure and the threats view. - GET
/api/auditreturns the audit log of who did what. Query parameters:userId,action,limit,offset. - GET
/api/lookupruns a live DNS lookup, the same read-back the dashboard's DNS Check uses. Query parameters:domainandtype. Thetypevalues are case-sensitive and must be one ofDMARC,SPF,DKIM,MTA-STS,BIMI,MXorTLS-RPT. This is read-only and a quick way to confirm what is actually published. - GET
/api/healthis a basic health check (database connectivity). It needs no authentication. - GET
/api/health/readyis a fuller readiness check across all of the platform's bindings, also unauthenticated. Use either for uptime monitoring.
Handling errors and limits
A few habits will keep an integration healthy:
- Check the status code, not just the body. A
2xxis success. A401or403means your key is missing, wrong, deleted, regenerated, or lacks the permission or admin rights the call needs; re-read the permissions section above. A404usually means a domainidthat does not belong to your organisation. A429means you are being rate limited (the forgot-password endpoint, for instance, allows three a minute); back off and retry. - Send
Content-Type: application/jsonon any request with a body, and send valid JSON. The upload endpoint is the exception, takingapplication/xmlorapplication/gzip. - Page through lists. Endpoints that take
limitandoffsetwill not hand you everything at once on a large account; loop until you have read the lot. - Fail safe on writes. The DNS endpoints change live records that decide whether your mail is trusted. Read the current value first, change one thing, and verify the result, especially before moving DMARC towards
reject.
Putting it together
A typical first integration reads like a short script: list your domains, pick one, read its DMARC, pull a month of daily analytics, and resolve any alert you have handled. Each of those is one of the calls above, authenticated with a single x-api-key header from a key you created on the API Keys tab with exactly the permissions it needs.
When you are ready to go further, the per-standard docs explain what good configuration looks like before you automate it: hosted DMARC, hosted SPF, hosted DKIM, hosted MTA-STS and hosted BIMI. For the product overview of what you are automating, see the products. And whenever you just need to check a domain by hand, the free tools are always there, no key required.