Docs

Custom domains

Send and receive from your own domain: DNS records, verification and limits.

View as Markdown

Use your own domain (or a subdomain such as agents.your-domain.eu) so recipients see your brand. A subdomain is recommended: it keeps agent mail separate from your team's mail.

1. Add the domain

cURL
curl -s -X POST https://api.mueslimail.com/v0/domains \
  -H "Authorization: Bearer $MAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "agents.your-domain.eu"}'

The response contains the DNS records to publish. A fresh DKIM key is generated for each domain. Copy the values exactly as returned; the table shows their shape.

TypeNameValuePurpose
MXagents.your-domain.eu10 <platform mail host>.Routes inbound mail to the platform.
TXTagents.your-domain.euv=spf1 mx include:<relay> -allSPF: authorizes the platform to send.
TXT<selector>._domainkey.agents.your-domain.euv=DKIM1; k=rsa; p=<public key>DKIM: public key for signatures.
TXT_dmarc.agents.your-domain.euv=DMARC1; p=quarantine; rua=mailto:dmarc@agents.your-domain.euDMARC policy.

2. Verify

Call POST /v0/domains/{domain}/verify to check DNS now. The scheduler also checks every 5 minutes for 7 days after you add the domain. Each record reports verified or missing; when all four are found the domain becomes verified and the domain.verified event fires.

3. Create inboxes on it

Pass domain when you create an inbox. Until the domain is verified the call returns 403 domain_not_verified.

Catch-all: one inbox for unknown addresses

A verified domain can nominate one inbox that receives the mail addressed to a local part nobody created: PATCH /v0/domains/{domain} with {"catch_all_inbox": "triage@agents.your-domain.eu"}, and null turns it off. The lookup is exact-address first, so an address that exists always wins and the catch-all only ever catches what would otherwise be refused.

  • The nominated inbox must be on that same domain (422 catch_all_inbox_wrong_domain) and the domain must be verified (422 domain_not_verified); an unknown address is 404.
  • A quarantined address is never caught: an address whose inbox was deleted less than 90 days ago is refused, because the quarantine exists so that mail for the previous owner reaches nobody.
  • One sender may fill at most MAI_CATCH_ALL_PER_HOUR unknown local parts per domain per hour (20 by default). Past that, the recipient is refused, not accepted: a catch-all without a budget is a backscatter amplifier.
  • Caught mail is ordinary inbound mail: the same screening, the same rules, the same events. If the nominated inbox is deleted the catch-all switches itself off, and an unknown recipient is a 550 at RCPT, never a silent drop.

Limits and deletion

  • The number of custom domains depends on the plan: 1 on Free, 5 on Builder, 20 on Growth, 50 on Team.
  • A domain can be deleted only when it has no inboxes left; otherwise the API returns 409 cannot_delete.
  • GET /v0/domains lists your domains and the shared platform domain.

Endpoints

POST/v0/domains

Add a custom domain; returns the DNS records to publish

Body

NameTypeNotes
domainstringrequired

Responses

  • 201 Created: Domain
  • 403 Error envelope: Error
  • 409 Error envelope: Error

GET/v0/domains

List own and shared domains

Responses

  • 200 OK

GET/v0/domains/{domain}

Get a domain with record status

Parameters

NameTypeNotes
domainstringin path, required

Responses

  • 200 OK: Domain

DELETE/v0/domains/{domain}

Delete a domain without inboxes

Parameters

NameTypeNotes
domainstringin path, required

Responses

  • 204 Deleted
  • 409 Error envelope: Error

POST/v0/domains/{domain}/verify

Check DNS now (the scheduler also checks every 5 minutes for 7 days)

Parameters

NameTypeNotes
domainstringin path, required

Responses

  • 200 OK: Domain

Domain object

NameTypeNotes
domain_idstring (uuid)
domainstring
sharedboolean
statuspending | verified
recordsobject[]
last_checked_atstring (date-time)
verified_atstring (date-time)
catch_all_inboxstring (email)The inbox that receives mail addressed to a local part nobody created on this domain, or null for no catch-all (D5)