Skip to content
machinemail

Domains

Add your own domain to MachineMail for custom inbox addresses and catch-all. Covers DNS records, verification states, DNS host detection, Domain Connect and the zone file.

Inboxes on machinemail.dev work out of the box. Add a custom domain when you want addresses like support@agents.acme.dev, catch-all addresses, or sending that is authenticated as your own domain. Subdomains work the same as apex domains, and keep your main domain’s mail setup untouched.

For a step-by-step walkthrough, see Use a custom domain.

Adding a domain

Terminal
curl https://api.machinemail.dev/v0/domains \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "agents.acme.dev", "catch_all": true, "catch_all_ttl_seconds": 86400}'
FieldNotes
domainRequired. A hostname. Must not already be registered by any organization.
inbound_enabledDefault true. When false, the MX record is not required and the domain is send-only.
catch_allDefault false. Accept mail to any address. See Catch-all.
catch_all_ttl_secondsMake catch-all inboxes ephemeral with this TTL.
feedback_enabledDefault false. Stored for AgentMail compatibility.
client_idIdempotency key.

The response lists the DNS records to publish:

Response
{
  "domain_id": "dom_01m4fy8hmc0000fpjt8gvxtayy",
  "organization_id": "org_01m4fy8h140000efvagszh6rpx",
  "pod_id": "pod_01m4fy8h150000w4xty1ehfnqa",
  "domain": "agents.acme.dev",
  "status": "NOT_STARTED",
  "reason": "Publish the DNS records, then call POST /v0/domains/{domain_id}/verify",
  "feedback_enabled": false,
  "inbound_enabled": true,
  "catch_all": true,
  "catch_all_ttl_seconds": 86400,
  "nameservers": ["ns23.domaincontrol.com", "ns24.domaincontrol.com"],
  "dns_provider": "godaddy",
  "dns_provider_url": "https://dcc.godaddy.com/control/acme.dev/dns",
  "subdomains_enabled": false,
  "tracking_enabled": false,
  "records": [
    { "type": "MX", "name": "agents.acme.dev", "value": "mx.machinemail.dev", "priority": 10, "status": "MISSING", "purpose": "inbound", "required": true },
    { "type": "TXT", "name": "agents.acme.dev", "value": "v=spf1 include:spf.machinemail.dev ~all", "status": "MISSING", "purpose": "spf", "required": true },
    { "type": "TXT", "name": "machinemail._domainkey.agents.acme.dev", "value": "v=DKIM1; k=rsa; p=MIIBIjANBgkqh...", "status": "MISSING", "purpose": "dkim", "required": true },
    { "type": "TXT", "name": "_dmarc.agents.acme.dev", "value": "v=DMARC1; p=none", "status": "MISSING", "purpose": "dmarc", "required": false }
  ],
  "created_at": "2026-10-09T09:00:16.207Z",
  "updated_at": "2026-10-09T09:00:16.207Z"
}

Always publish the records exactly as the API returns them for your domain. The set can differ between domains, for example when sending records are issued per domain, in which case purpose is provider.

DNS records

purposeWhat it doesRequired
inboundMX record that routes mail for the domain to MachineMail.Yes, when inbound_enabled is true.
spfAuthorises MachineMail to send for the domain.Yes
dkimPublic key receivers use to check MachineMail’s signature on your mail. A new 2048-bit key is generated per domain.Yes
providerExtra sending records some domains need.Yes
dmarcA starter DMARC policy.No. Skip it if you already have DMARC.

Each record has a status:

Record statusMeaning
MISSINGNothing found at that name.
INVALIDSomething is there but it does not match. reason shows what was found.
VALIDMatches.

An existing SPF record is fine as long as it contains include:spf.machinemail.dev. Any DMARC record counts as valid.

Verification

Terminal
curl -X POST https://api.machinemail.dev/v0/domains/dom_01m4fy8hmc0000fpjt8gvxtayy/verify \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"

verify looks up every record now and returns the updated domain.

Domain statusMeaning
NOT_STARTEDAdded but never verified.
PENDINGSome required records are still missing.
INVALIDA required record exists with the wrong value.
VERIFIEDEvery required record is valid. verified_at is set and a domain.verified event fires.

reason explains what is outstanding, for example "MX agents.acme.dev is missing". It is absent once the domain is verified. The values FAILED and VERIFYING exist in the AgentMail enum but MachineMail does not currently use them.

After the first verify, MachineMail keeps re-checking PENDING and INVALID domains in the background about every 5 minutes, for up to 7 days after the domain was added. A domain in NOT_STARTED is not checked automatically, so call verify once after publishing the records. last_checked_at shows when the last check ran.

Note

You can create inboxes on a domain right after adding it. Mail only arrives once the MX record resolves, and catch-all only works once the domain is VERIFIED. Wait for verification before sending from the domain; until SPF and DKIM are in place, receivers are likely to reject or junk your mail.

DNS host detection

When you add or verify a domain, MachineMail looks up the zone’s nameservers and recognises common DNS hosts. The domain then carries:

  • nameservers: the authoritative nameservers it found.
  • dns_provider: cloudflare, route53, google, vercel, namecheap, godaddy, digitalocean, hetzner, porkbun or other.
  • dns_provider_url: a link straight to the zone’s DNS settings at that host, when known.

The dashboard uses these to show an Open in [provider] button on the domain page.

Domain Connect

Domain Connect lets a DNS host add records for you after you approve them in its own UI. Cloudflare, GoDaddy, IONOS and others support it. When it works for your zone, the dashboard shows Add records at [provider], which opens the provider with MachineMail’s records prefilled.

Terminal
curl https://api.machinemail.dev/v0/domains/dom_01m4fy8hmc0000fpjt8gvxtayy/domain-connect \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
Response
{
  "supported": false,
  "provider_id": "cloudflare.com",
  "provider_name": "Cloudflare",
  "url_sync_ux": "https://dash.cloudflare.com/domainconnect",
  "reason": "template_not_supported",
  "checked_at": "2026-10-09T09:05:12.000Z"
}

When supported is true, apply_url is a signed link that adds only the records still outstanding. Open it in a browser (a popup of width by height, or a new tab). After you approve, the provider sends you back to the domain page in the dashboard, which runs a verify. The domain object also carries a short domain_connect summary (supported, provider_name, apply_url) from the last check.

reason explains why there is no apply URL:

reasonMeaning
no_domain_connectThe zone’s DNS host does not support Domain Connect.
no_sync_flowThe host supports Domain Connect but not the synchronous flow MachineMail uses.
template_not_supportedThe host has not onboarded MachineMail’s template yet.
records_not_templatableThe domain’s records don’t fit the template.
already_configuredEvery record is already in place.
provider_errorThe host’s Domain Connect API failed. Retried after about 10 minutes.
not_configuredDomain Connect is turned off.

Warning

Domain Connect onboarding with DNS providers is still pending. Until MachineMail’s template is published and accepted by each provider, discovery returns template_not_supported (or records_not_templatable when the records don’t fit the template), and the dashboard falls back to the Open in [provider] console link and the zone file. Add the records by hand in the meantime.

Zone file

GET /v0/domains/{domain_id}/zone-file returns the records as a BIND zone file (text/plain, downloaded as agents.acme.dev.zone). Many DNS hosts, including Cloudflare, can import it.

Terminal
curl https://api.machinemail.dev/v0/domains/dom_01m4fy8hmc0000fpjt8gvxtayy/zone-file \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" -o agents.acme.dev.zone

Catch-all

With catch_all: true on a verified domain with inbound enabled, mail to any address at the domain is accepted. The first message to a new address creates the inbox with created_by: "catch_all" in the domain’s pod. With catch_all_ttl_seconds set, those inboxes are ephemeral and are deleted after that long without new mail.

The local part must be a valid username (a-z, 0-9 and . _ + -). Mail to invalid local parts is not delivered.

Catch-all is off by default and never applies to the shared machinemail.dev domain.

Updating and deleting

PATCH /v0/domains/{domain_id} accepts catch_all, catch_all_ttl_seconds (null to clear), inbound_enabled and feedback_enabled. Turning inbound_enabled off makes the MX record optional and recomputes the status.

DELETE /v0/domains/{domain_id} removes the domain from MachineMail. Inboxes already created on it are not deleted; delete them separately. Inbox-scoped keys cannot add domains; pod keys see and add domains in their pod only.

Next