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 https://api.owlpost.to/v1/domains \
-H "Authorization: Bearer $OWLPOST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "example.com"}'{
"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.
| Record | Name | Type | Value | Why |
|---|---|---|---|---|
| DKIM ×3 | <token>._domainkey | CNAME | <token>.dkim.amazonses.com | SES Easy DKIM signs your mail with keys it rotates |
| SPF | bounces | MX, priority 10 | feedback-smtp.<region>.amazonses.com | A custom MAIL FROM: bounces come back under your domain |
| SPF | bounces | TXT | "v=spf1 include:amazonses.com ~all" | So SPF passes, aligned with your domain, for DMARC |
| DMARC | _dmarc | TXT | "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 status | Meaning |
|---|---|
pending | Waiting for the DNS records to be found |
verified | SES verified the domain for sending and DKIM passes. Live mail may leave from it. |
temporary_failure | SES could not check this time; it keeps trying, and so does Owlpost |
failed | SES 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:
{
"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.