Verify a domain

POST /api/v1/domains/{domainId}/verify Asks the email provider to re-read the DNS and stores the answer. Admin role required. This is the call to repeat after publishing your records — not GET /api/v1/domains, which returns the last known state. Propagation takes up to 72 hours, usually minutes.

Authorization

Role in the organizationThis endpoint
OWNERAllowed
ADMINAllowed
MEMBERRefused — 403
A key carries the role its creator holds in this organization — never a role on AgentsMail itself. See API keys.

Path parameters

ParameterTypeDescription
domainIdstringRequired. The domain to re-check

Example

bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" \
  "https://www.agentsmail.io/api/v1/domains/$DOMAIN_ID/verify"

Response

  • 200 OK — the call succeeded.
  • 401 Unauthorized — missing or invalid key, or its owner left the organization.
  • 403 Forbidden — the key's owner is a member of the organization, not an admin.
  • 404 Not Found — no such resource, or it belongs to another organization. The API never confirms that an id exists to a caller who has no right to it.
  • 429 Too Many Requests — over 120 requests in a minute for this key.

Response fields

The domain after the check, same shape as reading it.
Nothing is gained by publishing twice. Poll this endpoint until verified is true; on a provider outage it answers an error rather than a stale state, because verification is exactly what was asked for.
FieldTypeDescription
successbooleanIndicates if the operation was successful
data.idstringIdentifier of the declared domain
data.domainstringThe domain itself
data.dkimStatusstringPENDING, SUCCESS or FAILED — the signing keys
data.mailFromStatusstring \null
data.mailFromDomainstring \null
data.verifiedbooleanThe only question that decides sending: DKIM SUCCESS and ownership proved
data.ownershipProvedbooleanWhich of the two gestures is still missing
data.lastCheckedAtstring \null
data.sesUnavailablebooleantrue means you are reading the last known state, not the current truth
data.dnsRecordsarrayThe records to publish — {type, name, value, purpose}

Example response

json
{
  "success": true,
  "data": {
    "id": "4d19c0a2",
    "domain": "mail.example.com",
    "dkimStatus": "SUCCESS",
    "mailFromStatus": "SUCCESS",
    "mailFromDomain": "bounce.mail.example.com",
    "verified": true,
    "ownershipProved": true,
    "lastCheckedAt": "2026-08-23T10:22:05.000Z",
    "sesUnavailable": true,
    "dnsRecords": [
      {
        "type": "CNAME",
        "name": "abc123._domainkey.mail.example.com",
        "value": "abc123.dkim.amazonses.com",
        "purpose": "dkim"
      }
    ]
  }
}

Agent recipe

Declaring a domain, publishing its records and verifying it is a loop an agent can run end to end. Propagation takes minutes to 72 hours, so back off rather than hammer.
js
const {data: domain} = await (
  await fetch(`${base}/domains`, {
    method: 'POST',
    headers: {...headers, 'content-type': 'application/json'},
    body: JSON.stringify({domain: 'mail.example.com'}),
  })
).json()

await publishToDns(domain.dnsRecords) // your registrar, your API

for (let wait = 30_000; wait < 3_600_000; wait *= 2) {
  const {data} = await (
    await fetch(`${base}/domains/${domain.id}/verify`, {method: 'POST', headers})
  ).json()

  if (data.verified) break
  if (!data.ownershipProved) console.log('DKIM is fine — the TXT challenge is what is missing')
  await new Promise((resolve) => setTimeout(resolve, wait))
}