Custom domains

What works and what does not#

PartState
Add a domain, get its DNS records, download a zone fileWorks.
Check the records with live DNS lookups, see a per-record status, get domain.verifiedWorks.
Dashboard screen to add, view, verify and delete domainsWorks.
Create inboxes on a verified domainWorks (the address is created).
Send mail from an address on your domainNot connected.
Receive mail at an address on your domainNot connected.

The reason is plain: the domain is never registered with the email provider that sends and receives for the service, and the generated records are a fixed template, not credentials issued for your domain. In particular the DKIM value is derived from the domain name and is not a real signing key, no private key exists for it, and nothing signs mail with it. Mail for an inbox on a custom domain therefore cannot be delivered or received by publishing these records alone. Until the provider integration exists, use inboxes on the shared service domain for real mail. The feature table tracks this.

Custom domains are available on Pro (1) and Scale (10) for the record generation and verification described below; the Free plan has none (402 plan_limit_exceeded).

Add a domain and read the records#

curl -X POST "$BASE_URL/v0/domains" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"domain": "mail.example.com", "subdomains_enabled": false}'

The response has a domain_id (the domain name itself), a status and a list of records. Each record has a type, name, value, optional priority and its own status of MISSING, INVALID or VALID. Publish them at your DNS host, then call POST /v0/domains/:domain_id/verify (it checks DNS at that moment) or read GET /v0/domains/:domain_id. The overall status starts as NOT_STARTED, becomes PENDING while records are missing, INVALID when a record exists with the wrong value, and VERIFIED when every record is valid; verifying emits a domain.verified event. GET .../zone-file downloads the records as a zone file.

What the records mean#

MX tells the world which server accepts mail for the domain. If the domain already receives mail elsewhere, use a subdomain such as agents.example.com, because changing the root MX would redirect your existing mail.

SPF is a TXT record that lists the servers allowed to send mail for the domain. A domain should have only one SPF record; duplicates make verification report duplicate_records.

DKIM is a TXT record that publishes a public key; receivers use it to check a signature on each message. See the note above: the value generated today is not a working key.

DMARC is a TXT record at _dmarc that tells receivers what to do when SPF and DKIM do not line up with the From address (none, quarantine or reject) and where to send reports. The generated record is p=none and has no report address unless the operator sets DMARC_REPORT_ADDRESS, which adds rua=mailto:<address>.

Verification only compares what DNS returns with the template the API generated. A VERIFIED domain means "the records are published", not "mail will be delivered".

Options#

OptionEffect
subdomains_enabledAdds a wildcard MX so any *.yourdomain can have inboxes.
tracking_enabledAdds a tracking CNAME. Open tracking does not need it; see open tracking.
feedback_enabledFeedback reporting flag (default true).
client_idIdempotency handle.

Create an inbox on a verified domain with {"username": "bot", "domain": "mail.example.com"}. An unverified or unknown domain is refused with 422 domain_not_verified, and an inbox on a subdomain needs subdomains_enabled on the parent (422 subdomains_not_enabled).

Domains can also be created inside a pod (POST /v0/pods/:pod_id/domains). The TypeScript SDK and MCP do not cover domains; the Python SDK has domains.create, list, get, verify, get_zone_file and delete.