Check your domain's DNS records

18 views 0 found helpful Sep 05, 2026

Mail only works when a domain's DNS says what AtrioMail expects: an MX record that points at mail.atriomail.com, an SPF record that authorises it, the DKIM key it signs with and, for domains that send through Amazon SES, the records Amazon issued. The DNS Checker reads a domain's live DNS, compares every record with the expected value and prints the exact change for each one that is missing or wrong, so you can finish a cutover or answer a customer's "mail is not arriving" without an outside tool.

What the checker looks at

Record Host Value you publish Required
MX @ mail.atriomail.com, priority 10 Yes
SPF (TXT) @ v=spf1 mx a:mail.atriomail.com ~all (one record only) Yes
DKIM (TXT) dkim._domainkey The key shown in Email Domains > DNS Setup Yes
DMARC (TXT) _dmarc v=DMARC1; p=none; rua=mailto:postmaster@customer.com Recommended
Amazon SES ownership (TXT) _amazonses The token shown in DNS Setup Yes, when the domain sends through Amazon SES
Amazon SES DKIM (CNAME) <token>._domainkey The targets shown in DNS Setup, one CNAME each Yes, when the domain sends through Amazon SES

Replace customer.com with your domain. @ stands for the domain itself: some DNS providers want the domain name in the host field, others want it left empty. On a connected Cloudflare or Namecheap account, automatic DNS setup can write mail.atriomail.com as the MX record for you. Remove any MX records that still point at your previous provider yourself, once your mailboxes are moved. If you publish the records yourself, use the values above.

Required records must be right for mail to flow. DMARC is recommended: a missing one is reported as something to review and never fails the domain.

Three ways to run it

  • In the control panel: Tools > DNS Checker (https://system.atriomail.com/admin/dns-checker). Type a domain and run the check. A domain in your account is checked against its exact expected values, including the DKIM key and the Amazon SES records issued for it. Any other domain gets a generic check: an MX record, one valid SPF record that authorises mail.atriomail.com, a DKIM record for the selector you name, and DMARC. Up to 20 checks per minute per user.
  • On the public page at https://system.atriomail.com/tools/dns-checker. No login needed. It runs the generic check (MX, SPF, DKIM presence, DMARC) and never shows whether a domain is hosted with AtrioMail, so you can hand the link to a customer, or to a prospect, before they sign up. Limited to 6 checks per minute and 60 per day from one IP address.
  • Through the API, for scripts, monitoring and the tools you already run. See "Using the API" below.

Reading a result

Every record gets one of four verdicts:

Verdict Meaning
Correct The record exists and says what it should.
Check The record works, but there is something to review: an SPF record that ends in +all, an MX that is listed but not first, extra mail servers, a DKIM record that exists but could not be verified, or a missing DMARC.
Missing or wrong The record is absent, points elsewhere, is malformed or is duplicated. Mail is affected until it is fixed.
Could not check The DNS lookup failed or took too long. This says nothing about your zone; run the check again in a few minutes.
  • Required records (MX, SPF, DKIM and the Amazon SES records) must be Correct or Check for the domain to pass. DMARC is recommended, so a missing one shows as Check and never fails the domain.
  • The score is the share of required records that are in place. Correct and Check both count as in place; DMARC does not move it.
  • Found and expected. Every record shows the values your zone serves right now next to the value we expect, so a typo, a stray quote character or a second record stands out at a glance.
  • Presence only. For a domain outside your account, and on the public page, the checker has no DKIM key to compare with, so it can only confirm that a DKIM record exists for the selector (present). For a domain in your account whose key is not available at that moment, it reports present_unverified as Check and verifies the value on the next run.
  • Not issued yet. Amazon SES records appear only for domains in your account that send through Amazon SES. Until Amazon has issued them the check shows not_ready; wait a few minutes and run it again.
  • Propagation. A record you changed a moment ago can take up to 48 hours to reach every resolver, though most changes show within the hour. If the checker still reports the old value, wait rather than change it again. See DNS Propagation Explained.
  • Recheck. A check of the same domain within 60 seconds returns the previous result, marked as reused. After that, Recheck reads DNS again.

Fixing each record

Every verdict except Correct comes with the exact change to make. These are the fixes the tool prints, with the pitfalls it looks for, using customer.com as the domain.

MX

Fix: Add an MX record on customer.com (host @) with value mail.atriomail.com and priority 10.

  • Points elsewhere. If the MX records name another server, add ours and remove the records that point elsewhere once the mailboxes have been migrated.
  • Listed but not first. Mail goes to the MX with the lowest priority number. If another server has a lower number than mail.atriomail.com, give ours the lowest number or remove the other record.
  • Extra mail servers. MX records left behind by a previous provider send part of the mail astray. Remove them, unless you route mail to more than one server on purpose (split delivery), in which case the Check verdict is expected.

SPF

Fix: Add a TXT record on customer.com (host @) with value v=spf1 mx a:mail.atriomail.com ~all.

  • A second SPF record. A domain may have exactly one record that starts with v=spf1. Two is a permanent error: receivers reject SPF entirely, and mail from every service you send through is affected. Merge them into one record, for example v=spf1 mx a:mail.atriomail.com include:other-service.example ~all.
  • Quotation marks stored in the value. Some DNS panels store the quote characters you type as part of the value, so receivers see "v=spf1 ..." and cannot read it. Enter the value without quotes.
  • Does not authorise mail.atriomail.com. If a record exists but does not name our server, add a:mail.atriomail.com before the all mechanism instead of replacing the whole record. An existing include:mail.atriomail.com is accepted too.
  • Ends in +all or ?all, or has no all at all. Either lets anyone send as your domain. End the record with ~all or -all.
  • Too many lookups. SPF allows at most 10 DNS lookups; include, a, mx and redirect cost one each. Beyond that receivers return a permanent error. Remove the includes of services you no longer send from.

DKIM

Fix: Add a TXT record at host dkim._domainkey with the DKIM value shown in the DNS Setup dialog for customer.com (Email Domains, then the DNS icon next to the domain).

  • A leftover key. A TXT record at dkim._domainkey that does not match is usually the key of a previous provider. Replace it with the value from DNS Setup; mail signed with our key fails verification until you do.
  • Two values at the selector. DKIM fails when a selector has several TXT records. Delete the extra ones and keep the current key.
  • Quotation marks. As with SPF, re-enter the record without the surrounding quote characters.
  • The selector. It is dkim for most domains, but always read the host from the DNS Setup dialog.
  • Copying the value. The key is long. Copy it whole and add no spaces or line breaks.

DMARC

Fix: Add a TXT record at host _dmarc with value v=DMARC1; p=none; rua=mailto:postmaster@customer.com.

  • Two DMARC records. Receivers ignore DMARC entirely when there are several. Keep one and delete the others.
  • Malformed record. A record without a p tag, or with a policy other than none, quarantine or reject, is reported as malformed. Use the value above as a starting point.
  • Inherited from the parent domain. A subdomain with no DMARC record of its own falls under the parent domain's policy. The checker reports this as Correct and names the policy it found, so nothing needs adding.
  • Policy none. p=none only asks for reports. Move to quarantine or reject once the reports look clean.

Amazon SES records

Fix: Add a TXT record at _amazonses.customer.com with the ownership token, and a CNAME at each <token>._domainkey.customer.com name pointing to the target shown in the DNS Setup dialog.

  • CNAME flattening. Some providers replace a CNAME with the TXT value it points at. The key is then visible, but Amazon SES needs the CNAME itself, and the checker reports flattened. Publish the name as a CNAME, or ask the provider to disable flattening for that name.
  • Wrong target. A CNAME that points elsewhere is reported with the target it found. Point it at the value from DNS Setup.
  • Other TXT values at _amazonses. They can stay. The ownership token only has to be one of them.
  • Not issued yet. If Amazon has not issued the records, there is nothing to publish yet. Check back in a few minutes.

Automatic DNS setup can write the MX, SPF, DMARC and DKIM records for you only on a connected Cloudflare or Namecheap account, and the Amazon SES records are always added by hand from the domain's DNS Setup dialog; see Understanding Automatic DNS Setup. If you publish them by hand, follow Manual DNS Setup Instructions, and when a record still fails after the fix, DNS Troubleshooting Guide covers the usual causes.

Using the API

The three endpoints live on the v1 API and take your API key in the X-API-KEY header. Every role may run them for the domains its key can list.

Check a domain in your account by its id:

curl -X POST \
  https://system.atriomail.com/api/v1/domains/123/dns-check \
  -H 'X-API-KEY: your-api-key-here' \
  -H 'Accept: application/json'

Check a domain by name. mode is auto (the default: a domain in your account gets the exact check, any other domain the generic one), account (404 unless the domain is in your account) or generic (never consults your account). selector names the DKIM selector for a generic check and defaults to dkim.

curl -X POST \
  https://system.atriomail.com/api/v1/dns-check \
  -H 'X-API-KEY: your-api-key-here' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"domain": "customer.com", "mode": "auto"}'

Your current allowance, always free:

curl -X GET \
  https://system.atriomail.com/api/v1/dns-check/quota \
  -H 'X-API-KEY: your-api-key-here' \
  -H 'Accept: application/json'

A check answers with the domain, its mode (account or generic), the mail_host it was checked against, checked_at, cached, cache_expires_at, a summary (status, required_total, required_passed, percentage), one entry per record in records, ses (null for a generic check) and your quota. Shortened to one record:

{
  "domain": "customer.com",
  "domain_id": 123,
  "mode": "account",
  "mail_host": "mail.atriomail.com",
  "checked_at": "2026-09-04T10:15:00+00:00",
  "cached": false,
  "cache_expires_at": "2026-09-04T10:16:00+00:00",
  "summary": {
    "status": "fail",
    "required_total": 5,
    "required_passed": 4,
    "percentage": 80
  },
  "records": [
    {
      "key": "spf",
      "label": "SPF",
      "type": "TXT",
      "host": "@",
      "name": "customer.com",
      "required": true,
      "status": "fail",
      "code": "missing",
      "expected": "v=spf1 mx a:mail.atriomail.com ~all",
      "found": [],
      "message": "No SPF record found on customer.com.",
      "fix": "Add a TXT record on customer.com (host @) with value v=spf1 mx a:mail.atriomail.com ~all.",
      "notes": []
    }
  ],
  "ses": {
    "enabled": true,
    "processing_status": "completed",
    "verification_status": "pending"
  },
  "quota": {
    "domains": 25,
    "daily": {
      "limit": 50,
      "used": 1,
      "remaining": 49,
      "resets_at": "2026-09-05T10:15:00+00:00"
    },
    "weekly": {
      "limit": 200,
      "used": 1,
      "remaining": 199,
      "resets_at": "2026-09-11T10:15:00+00:00"
    },
    "generic": {
      "daily": {
        "limit": 10,
        "used": 0,
        "remaining": 10,
        "resets_at": null
      },
      "weekly": {
        "limit": 40,
        "used": 0,
        "remaining": 40,
        "resets_at": null
      }
    },
    "burst": {
      "per_minute": 30,
      "scope": "ip"
    },
    "cache_seconds": 60
  }
}
  • status is pass, warn, fail or unknown: the Correct, Check, Missing or wrong and Could not check verdicts above.
  • code is the stable, machine-readable reason and the field to build on: ok, missing, mismatch, multiple, quoted, syntax, not_primary, extra_hosts, not_authorising, permissive, no_terminal, too_many_lookups, present, present_unverified, inherited, flattened, not_ready and lookup_failed. The wording of message, fix and notes may change without notice.
  • cached is true when the result was reused from a check of the same domain in the last 60 seconds; such a response is not counted. Send {"fresh": true} to query DNS again; that check counts.
  • A 429 means the allowance is used up. Read the Retry-After header (seconds) and wait; the body carries window, retry_after and your quota. Results from the last 60 seconds are still returned while you wait.
  • A 404 (domain_not_found) means the id or name is not a domain your key can list. A 422 (validation_failed) lists the invalid fields under errors.

Limits

Checks through the API are metered per account. With X domains in your account (every domain your API key can list, active or not) you can run 2 x X checks per rolling 24 hours, never fewer than 10, and 8 x X per rolling 7 days, never fewer than 40. An account with 25 domains therefore gets 50 checks a day and 200 a week. Every check you ask for counts, whether or not the lookups answered. The one free case is reuse: repeating a check of the same domain within 60 seconds returns the previous result, marked cached, and costs nothing. Checks of domains that are not in your account share this allowance and are also capped at 10 per 24 hours and 40 per 7 days on their own. All API keys on the same account share one allowance, and checks you run in the control panel or on the public checker page never count. When the allowance is used up the API answers 429 with a Retry-After header and the exact limits, while results from the last 60 seconds are still returned. Separately, the API accepts at most 30 requests per minute from one IP address across all endpoints. GET /api/v1/dns-check/quota shows your current usage at any time and is always free.

In the control panel you can run up to 20 checks per minute per user, and the public page accepts 6 checks per minute and 60 per day from one IP address. Neither counts against the API allowance.

Privacy

Checks on the public page keep the domain, the IP address of the caller and the result for 30 days, after which they are deleted. API calls are logged with the caller's IP address for 30 days, as every API call is. A check of a domain outside your account, on the public page or through the API, reads only public DNS and never reveals whether that domain is hosted with AtrioMail.

Related articles

dns mx spf dkim dmarc ses api

Was this article helpful?

Thank you for your feedback!

We appreciate you helping us improve our documentation.