MailLandDocs

Domain Verification

Verify your domain ownership through DNS record validation

After configuring DNS records, trigger verification to prove domain ownership and unlock email sending. MailLand checks SPF, DKIM, DMARC, and return path records.

Verify a Domain

POST https://api.mail.land/v1/domains/{id}/verify

Authenticate with Authorization: Bearer <token> or X-API-Key: <api key>.

Verify a domain
curl -X POST https://api.mail.land/v1/domains/k3v9x2m8q1w7z4/verify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Response

Verification response
{
  "domainId": "k3v9x2m8q1w7z4",
  "domain": "yourdomain.com",
  "isVerified": true,
  "spfStatus": "valid",
  "dkimStatus": "valid",
  "dmarcStatus": "valid",
  "verifiedAt": "2026-02-17T10:15:00Z",
  "message": "Domain successfully verified with all DNS records valid",
  "dnsRecords": {
    "spfRecord": "v=spf1 include:spf.mlnd.net ~all",
    "dkimSelector": "mlnd",
    "dkimRecord": "v=DKIM1; k=rsa; p=MIIBIjANBgkq...",
    "dmarcRecord": "v=DMARC1; p=none; rua=mailto:[email protected]"
  },
  "trackingVerified": false,
  "bounceVerified": true,
  "bounceVerifiedAt": "2026-02-17T10:15:00Z",
  "mxVerified": false,
  "lastDnsCheckAt": "2026-02-17T10:15:00Z"
}
FieldTypeDescription
domainIdstringDomain public ID
domainstringDomain name
isVerifiedbooleantrue only when SPF, DKIM and DMARC are all valid
spfStatus, dkimStatus, dmarcStatusenumRecord status: pending, valid or invalid
verifiedAtdatetimeWhen the domain was verified
messagestringSummary of the result
dnsRecordsobjectThe SPF, DKIM (with dkimSelector) and DMARC records found in DNS, or the expected value when none was found
spfErrorMessage, dkimErrorMessage, dmarcErrorMessagestringWhy the record failed; omitted when it passed
trackingVerified, bounceVerified, mxVerifiedbooleanWhether the custom tracking domain, the custom bounce domain (Return-Path) and the inbound MX record are verified
trackingVerifiedAt, bounceVerifiedAt, mxVerifiedAtdatetimeWhen each of them was verified
trackingErrorMessage, bounceErrorMessage, mxErrorMessagestringWhy one of them failed; returned only for the ones you have turned on
lastDnsCheckAtdatetimeWhen DNS was last checked

Record Statuses

Each DNS record check returns one of these statuses:

StatusDescription
pendingNot checked yet
validRecord found and correct
invalidRecord missing or incorrect

Verification Requirements

For a domain to be verified (isVerified: true), SPF, DKIM and DMARC must all be valid:

RecordRequiredMust Pass
SPFYesinclude:spf.mlnd.net must be present
DKIMYesValid public key for the active DKIM key
DMARCYesA record at _dmarc that starts with v=DMARC1 and has a policy (p=none, p=quarantine or p=reject)
Return PathNo (on by default for new domains; does not block sending)CNAME pointing to bounce.mlnd.net, DNS-only (not proxied)
TrackingNo (only with a custom tracking domain)CNAME pointing to track.mlnd.net, DNS-only (not proxied)
Inbound MXNo (only with inbound email)MX pointing to mx.mlnd.net

The optional records do not change isVerified. A custom tracking or bounce domain is used for sending only after its own record is verified; until then, MailLand's default hosts are used.

Partial Verification

If some records are found but others are missing, the response shows exactly what needs attention:

Partial verification
{
  "domainId": "k3v9x2m8q1w7z4",
  "domain": "yourdomain.com",
  "isVerified": false,
  "spfStatus": "valid",
  "dkimStatus": "invalid",
  "dmarcStatus": "invalid",
  "message": "Domain verification incomplete. Please check DNS records below.",
  "dnsRecords": {
    "spfRecord": "v=spf1 include:spf.mlnd.net ~all",
    "dkimSelector": "mlnd",
    "dkimRecord": "v=DKIM1; k=rsa; p=MIIBIjANBgkq...",
    "dmarcRecord": "v=DMARC1; p=quarantine; pct=100; adkim=r; aspf=r; rua=mailto:[email protected]; sp=none"
  },
  "dkimErrorMessage": "DKIM record not found. Please add the TXT record to your DNS.",
  "dmarcErrorMessage": "DMARC record not found. Please add TXT record at _dmarc subdomain.",
  "trackingVerified": false,
  "trackingErrorMessage": "CNAME record not found. Please add a CNAME record for track.yourdomain.com pointing to track.mlnd.net",
  "bounceVerified": false,
  "mxVerified": false,
  "lastDnsCheckAt": "2026-02-17T10:20:00Z"
}

Automatic Re-verification

MailLand periodically re-checks verified domains in the background via the PendingDomainVerificationWorker and DnsHealthCheckWorker:

  • Pending domains: re-checked every hour for 7 days after they are added; after that, verify manually
  • Verified domains: DNS health checked periodically to detect removed or changed records

If a verified domain's DNS records are later removed or changed, the affected record's status (spfStatus/dkimStatus/dmarcStatus) becomes invalid, isFullyVerified becomes false and sending stops until the record is fixed. verificationStatus itself stays Verified: MailLand restores the record's status automatically when DNS is correct again.

Domain Health Check

Run an on-demand health check that goes beyond basic verification:

POST https://api.mail.land/v1/domains/{id}/health-check
Run a health check
curl -X POST https://api.mail.land/v1/domains/k3v9x2m8q1w7z4/health-check \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Health check response
{
  "domainName": "yourdomain.com",
  "status": "warning",
  "previousStatus": "healthy",
  "issues": [
    {
      "code": "DmarcNonePolicy",
      "message": "DMARC policy is set to 'none' (monitoring only)",
      "severity": "warning",
      "record": "DMARC",
      "details": "Consider upgrading to p=quarantine or p=reject"
    }
  ],
  "statusChanged": true,
  "checkedAt": "2026-02-17T10:30:00Z",
  "observationId": { "value": "3f2c9a1e-7b4d-4c1a-9e0f-5a6b7c8d9e0f" },
  "spf": {
    "lookupCompleted": true,
    "isValid": true,
    "value": "v=spf1 include:spf.mlnd.net ~all",
    "issues": [],
    "recordVerdict": "valid"
  },
  "dkim": {
    "lookupCompleted": true,
    "isValid": true,
    "issues": [],
    "recordVerdict": "valid"
  },
  "dmarc": {
    "lookupCompleted": true,
    "isValid": true,
    "value": "v=DMARC1; p=none; rua=mailto:[email protected]",
    "issues": [
      {
        "code": "DmarcNonePolicy",
        "message": "DMARC policy is set to 'none' (monitoring only)",
        "severity": "warning",
        "record": "DMARC",
        "details": "Consider upgrading to p=quarantine or p=reject"
      }
    ],
    "recordVerdict": "valid"
  }
}

Health Statuses

StatusDescription
healthyAll checks pass: no issues detected
warningDomain works but there are potential improvements
criticalDomain has issues that may prevent email delivery
unknownUnable to determine status (DNS lookup failure)

Issue Codes

CodeSeverityDescription
DnsNxDomaincriticalThe domain does not exist (confirmed by a second, independent resolver)
SpfMissingcriticalNo SPF record found
SpfMissingIncludecriticalThe SPF record does not include spf.mlnd.net
SpfPermErrorcriticalMore than one SPF record (a permanent SPF error)
SpfTooManyLookupswarningThe SPF record needs more than 10 DNS lookups
DkimMissingcriticalNo DKIM record found
DkimMissingSelectorcriticalNo DKIM record at an active key's selector
DkimKeyMismatchcriticalThe DKIM key in DNS does not match MailLand's key
DkimRevokedcriticalThe DKIM key has been revoked (empty p= tag)
DmarcMissingwarningNo DMARC record found
DmarcNonePolicywarningDMARC policy is none (monitoring only)
MxMissingcriticalNo MX record (checked only when inbound email is on)
MxWrongHostwarningThe MX record does not point to mx.mlnd.net
DnsTimeout, DnsServFail, DnsLookupFailedwarningThe DNS lookup did not complete; nothing was concluded about the record

Health History

View historical health check results. Optional from/to query parameters (ISO 8601 dates) narrow the range:

GET https://api.mail.land/v1/domains/{id}/health-history
Get health history
curl https://api.mail.land/v1/domains/k3v9x2m8q1w7z4/health-history \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Health history response
[
  {
    "domainName": "yourdomain.com",
    "prevStatus": "healthy",
    "newStatus": "warning",
    "issues": ["DmarcNonePolicy"],
    "reason": "Scheduled health check",
    "checkedAt": "2026-02-17T10:30:00Z"
  },
  {
    "domainName": "yourdomain.com",
    "prevStatus": "warning",
    "newStatus": "healthy",
    "issues": [],
    "reason": "Scheduled health check",
    "checkedAt": "2026-02-16T10:30:00Z"
  }
]

The response is a plain array (no items wrapper, no nextCursor/hasMore).

Troubleshooting

SPF record not found

  • Ensure you have exactly one SPF TXT record on your root domain
  • Multiple SPF records cause failures: merge them into a single record
  • Check that include:spf.mlnd.net appears before the ~all or -all mechanism

DKIM record not found

  • Verify the record is at mlnd._domainkey.yourdomain.com (not mlnd._domainkey)
  • The record type must be TXT (not CNAME)
  • Some DNS providers require quotes around long TXT values
  • Check for trailing spaces or newlines in the pasted public key

Return path CNAME not found

  • Confirm the CNAME is at bounce.yourdomain.com (or your custom bounce subdomain)
  • The CNAME value must be bounce.mlnd.net (not an IP address, and not an MX record)
  • Cloudflare users: ensure the proxy is disabled (grey cloud / DNS only)

DNS propagation delay

  • Most DNS changes propagate within 5–15 minutes
  • If using a low TTL (300s), changes should appear quickly
  • Existing records with high TTL may take up to 48 hours to update globally
  • Try verifying again after waiting 15 minutes

Next Steps

On this page