Add a domain
Adds a domain to the workspace and returns it with every DNS record it needs. The domain starts unverified, with its catch-all on, and verifies on its own once the records answer in public DNS.
Runs the real call against your workspace, with your own key.
POST /domains
Adds a domain to the workspace and returns it with every DNS record it needs. The domain starts unverified, with its catch-all on, and verifies on its own once the records answer in public DNS.
The request
Parameters
domainstringrequired- A bare domain such as `acme.com` or `mail.acme.com`. It is trimmed and lowercased, and an international name is stored in its ASCII form. A scheme, a path or an address is refused.
Example
Needs domains:write, and a key with no address or domain restriction, because a new domain reaches the whole workspace.
curl -X POST "$OE/domains" -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "domain": "acme.com" }'{ "object": "domain", "id": "b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f", "domain": "acme.com", "receiving": { "verified": false, "verifiedAt": null, "catchAll": true, "lastCheckedAt": "2026-09-25T10:00:02.000Z", "error": "No MX records yet. DNS changes can take a few minutes to spread." }, "sending": { "status": "no_identity", "canSend": false, "checkedAt": null, "error": null, "note": "Sending is not set up yet. Publish the signing record listed for this domain, and sending switches on shortly after it answers in DNS." }, "tracking": { "host": null, "status": "none", "active": false, "target": "", "record": null, "checkedAt": null, "verifiedAt": null, "error": null }, "storage": { "host": null, "status": "none", "active": false, "target": "", "record": null, "checkedAt": null, "verifiedAt": null, "error": null }, "addresses": [], "createdAt": "2026-09-25T10:00:00.000Z", "records": [ { "type": "MX", "name": "acme.com", "value": "inbound.mail.example", "priority": 10, "purpose": null, "status": "missing" }, { "type": "TXT", "name": "acme.com", "value": "v=spf1 include:_spf.openemail.uk ~all", "priority": null, "purpose": null, "status": "missing" }, { "type": "TXT", "name": "_openemail-challenge.acme.com", "value": "oe-verify=4f1c9a2b7e8d6053a1c4", "priority": null, "purpose": "Proves you own this domain", "status": "missing" } ], "dmarc": null}The example trims records. A real response lists every record the domain needs, including DMARC, the signing CNAMEs and the return-path records, and GET /domains/{id} describes each field.
Publish the records exactly as given, then call POST /domains/{id}/verify or wait: the domain is checked again whenever it is read and by a background sweep, and domain.verified fires the moment it passes.
When it is refused
409domain_already_added: this workspace owner already has the domain.409domain_claimed: another account has it.409related_domain_owned: a parent or a subdomain of it belongs to another account, so it cannot be split between the two.409operator_domain: the domain belongs to OpenEmail itself.403domain_allowance_reached: the plan holds no more domains. The message names the plan that holds more.422capability_unsupported: the key is limited to particular addresses or domains.