Docs · Domains

Sending domains

Live mail leaves Owlpost only from a domain your account verified, or a subdomain of one. Adding a domain registers it with Amazon SES and returns the DNS records that prove it is yours.

All domain routes need the domains:manage scope.

Add a domain

POST/v1/domainsBuilt

Body: {"name": "example.com"}. The name is lowercased and a trailing dot dropped; it must be letters, digits and hyphens with at least one dot. A domain can belong to one account only: adding it again answers 409 domain-taken, whichever account holds it.

curl
curl https://api.owlpost.to/v1/domains \
  -H "Authorization: Bearer $OWLPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "example.com"}'
response · 200
{
  "object": "domain",
  "id": "dm_01j9…",
  "name": "example.com",
  "status": "pending",
  "region": "eu-west-1",
  "created_at": "2026-10-03T09:00:00Z",
  "records": [
    { "record": "DKIM", "name": "abc123._domainkey", "type": "CNAME", "ttl": "Auto",
      "value": "abc123.dkim.amazonses.com", "status": "pending" },
    { "record": "DKIM", "name": "def456._domainkey", "type": "CNAME", "ttl": "Auto",
      "value": "def456.dkim.amazonses.com", "status": "pending" },
    { "record": "DKIM", "name": "ghi789._domainkey", "type": "CNAME", "ttl": "Auto",
      "value": "ghi789.dkim.amazonses.com", "status": "pending" },
    { "record": "SPF", "name": "bounces", "type": "MX", "ttl": "Auto",
      "value": "feedback-smtp.eu-west-1.amazonses.com", "status": "pending", "priority": 10 },
    { "record": "SPF", "name": "bounces", "type": "TXT", "ttl": "Auto",
      "value": "\"v=spf1 include:amazonses.com ~all\"", "status": "pending" },
    { "record": "DMARC", "name": "_dmarc", "type": "TXT", "ttl": "Auto",
      "value": "\"v=DMARC1; p=none;\"", "status": "recommended" }
  ]
}

The DKIM tokens above are placeholders: SES issues three of its own per domain. Other answers: 422 for a name that is not a domain, 502 ses-refused or 503 ses-unavailable when SES refuses or does not answer.

The DNS records

Record names are relative to your domain: bounces means bounces.example.com. The records array uses Resend's shape.

RecordNameTypeValueWhy
DKIM ×3<token>._domainkeyCNAME<token>.dkim.amazonses.comSES Easy DKIM signs your mail with keys it rotates
SPFbouncesMX, priority 10feedback-smtp.<region>.amazonses.comA custom MAIL FROM: bounces come back under your domain
SPFbouncesTXT"v=spf1 include:amazonses.com ~all"So SPF passes, aligned with your domain, for DMARC
DMARC_dmarcTXT"v=DMARC1; p=none;"Recommended, not checked. Keep your own DMARC record if you have one.

The MAIL FROM subdomain is bounces., not Resend's send., so a domain moving from Resend can keep Resend's records until the move is done.

Verification

POST/v1/domains/{id}/verifyBuilt

SES checks the records on its own. This route reads SES's current verdict, stores it and returns the domain. You do not have to call it: Owlpost rechecks pending and temporary_failure domains in the background, each about every five minutes.

Domain statusMeaning
pendingWaiting for the DNS records to be found
verifiedSES verified the domain for sending and DKIM passes. Live mail may leave from it.
temporary_failureSES could not check this time; it keeps trying, and so does Owlpost
failedSES gave up on the DKIM records. The background check stops; fix the records and call verify. If SES still says failed, remove the domain and add it again.

Each record has a status too: verified, pending, temporary_failure, failed or not_started (the MAIL FROM records before SES has looked), and recommended for DMARC.

List, read and remove

GET/v1/domainsBuilt

{"object": "list", "data": [domain, …]}, newest first.

GET/v1/domains/{id}Built

One domain, with its records. Another account's id answers 404.

DELETE/v1/domains/{id}Built

Removes the SES identity, then the domain: {"object": "domain", "id": "dm_…", "deleted": true}. Live mail from it is refused from then on.

Live mail only from verified domains

With an op_live_… key, the from domain of every message must be a verified domain of the key's account, or a subdomain of one (mail.example.com passes when example.com is verified). Anything else is refused before it is stored:

response · 403 · application/problem+json
{
  "type": "https://owlpost.to/problems/domain-not-verified",
  "title": "The sender's domain is not verified for this account",
  "status": 403,
  "detail": "`news.example.net` is not a verified sending domain for this account: add it with POST /v1/domains and add its DNS records.",
  "instance": "01J9ZQ…"
}

A batch is checked once per sender domain and refused whole. Test keys skip this check, since their mail never leaves Owlpost. A verified domain can also receive mail and host agent inboxes.