# Set up a sending domain

Lessly Mail sends only from domains you control. You register one, publish the DNS records Mail returns, and Mail re-checks those records until the domain is verified — from then on you can send from any address at it.

Everything on this page belongs to one product. A domain registered in one product is invisible to every other product.

## Choose what to register

A product may register a domain only underneath a domain it already owns: the apex itself, or any subdomain of it. A name that is neither is rejected with `422` and the error name `domain_not_governed`.

| Register | Use when | |
|---|---|---|
| A dedicated subdomain, e.g. `mail.example.com` | Your product sends system email | **(Recommended)** — keeps your product's mail apart from the mail your team sends by hand |
| The apex, e.g. `example.com` | You need messages to come from the bare domain | Puts your product's mail DNS in the same zone as everything else you publish |

## Register the domain

1. **List the apexes you may use.** An agent calls [`mail_domain_allowed_list`](/reference/mcp-tools/mail_domain_allowed_list); over REST it is `GET /mail/domains/allowed` on the [Mail API](/reference/openapi/mail).

   ```json
   [
     {
       "domainId": "d3f1…",
       "domain": "example.com",
       "mode": "external",
       "extensionCount": 2
     }
   ]
   ```

2. **Register it.** An agent calls [`mail_domain_create`](/reference/mcp-tools/mail_domain_create); over REST, `POST /mail/domains`. `name` is required. `region` decides where the domain's sending identity lives, defaults to `eu-north-1`, and is fixed at creation — to move a domain to another region, delete it and register it again.

   ```json
   { "name": "mail.example.com", "region": "eu-north-1" }
   ```

   The response is the domain, `status` set to `pending`, with the records to publish:

   ```json
   {
     "id": "8e2c…",
     "name": "mail.example.com",
     "region": "eu-north-1",
     "status": "pending",
     "records": [
       {
         "type": "CNAME",
         "name": "abc123._domainkey.mail.example.com",
         "value": "<the signing host Mail returns>",
         "purpose": "dkim",
         "required": true
       },
       {
         "type": "TXT",
         "name": "_dmarc.mail.example.com",
         "value": "v=DMARC1; p=none;",
         "purpose": "dmarc",
         "required": false
       }
     ],
     "verificationStartedAt": "2026-08-02T09:00:00.000Z",
     "verifiedAt": null,
     "active": false
   }
   ```

3. **Publish every record** in `records` at your DNS provider, exactly as given, values included. Keep the domain's `id` — you need it to ask for a check.

4. **Wait, or check now.** An agent calls [`mail_domain_verify`](/reference/mcp-tools/mail_domain_verify); over REST, `POST /mail/domains/:id/verify`. The reply adds a `resolved` flag to each record, so you can see which ones Mail can read yet. `resolved` appears only in that reply — `GET /mail/domains/:id` returns the records without it.

> Two things stop a registration. The name is already registered in that region, by any product — `409`. Or the host is already claimed elsewhere in your product — `409` with the error code `host_taken`, naming what holds it. Neither leaves anything behind: a failed create is rolled back and the same name can be retried.

## The DNS records

Every record carries a `type`, the `name` to create it at, its `value`, a `purpose` and whether it is `required`. `MX` records also carry a `priority`.

| Purpose | Record | Required | What it does |
|---|---|---|---|
| `dkim` | One `CNAME` per signing key, at `<token>._domainkey.<your-domain>` | Yes | Lets Mail sign your messages, and lets receiving servers confirm the signature really comes from your domain. Without them a domain never verifies. |
| `dmarc` | A `TXT` at `_dmarc.<your-domain>`, value `v=DMARC1; p=none;` | No | Tells receiving servers what to do with mail claiming to be from your domain but not signed by it. `p=none` only asks them to report, so it is safe to publish on day one. Mail does not check it, but publishing it is what lets you tighten the policy later. |
| `spf`, `return-path-mx` | A `TXT` and an `MX` on a `send.` subdomain | Yes, where present | Carry the address receiving servers return bounces to. A domain registered today does not list them; if yours does, they must stay published. |

## Statuses

| Status | Meaning |
|---|---|
| `not_started` | Registered, not yet checked. |
| `pending` | Being checked. The required records are not all readable yet. |
| `verified` | All required records are in place. The domain can send. |
| `temporary_failure` | A check failed in a way that may clear by itself. Mail keeps trying. |
| `failed` | Verification will not complete on its own. |

A domain also carries `active`, which is `true` only when the domain is `verified` and its claim on the host is live. `active` is the honest answer to "can I send from this domain right now".

Sending from a domain that is not `verified` fails with `422` and the error name `invalid_from_address`. Sending from a domain that is not registered in your product fails with `404`.

## When verification does not complete

Verification starts by itself the moment the domain is created, and Mail re-checks every unsettled domain once a minute. How long it takes is decided by your DNS provider, not by Mail: most domains verify within a few minutes of the records going live, and a provider with a long TTL can take several hours. Mail keeps trying for 72 hours from `verificationStartedAt`; past that the domain becomes `failed`.

| Cause | What to do |
|---|---|
| The records are not published yet | Ask for an immediate check and read `resolved` on each record. Every record with `required: true` must be `true`. |
| A record was altered | Some providers append the zone name to a record you enter, turning `abc123._domainkey.mail.example.com` into `abc123._domainkey.mail.example.com.example.com`. Compare what you published against `name`, character for character. |
| The value was trimmed | DKIM values end in a trailing dot. Publish the value exactly as Mail returns it. |
| DNS has not propagated | Nothing to do but wait. Mail re-checks every minute on its own. |
| `temporary_failure` | A lookup failed in a way that may clear. Mail keeps checking, and the domain moves on by itself once the records answer. |

A `failed` domain does not resume. Fix the records, then delete the domain and register it again — a fresh registration restarts the 72-hour window.

## Change how the domain sends

An agent calls [`mail_domain_update`](/reference/mcp-tools/mail_domain_update); over REST, `PATCH /mail/domains/:id`. Send only the fields you want to change. `name` and `region` are fixed at creation and cannot be patched.

| Field | Values | Default | What it does |
|---|---|---|---|
| `openTracking` | `true` / `false` | `false` | Adds a tracking pixel to HTML bodies so opens are reported. |
| `clickTracking` | `true` / `false` | `false` | Rewrites links so clicks are reported. |
| `trackingSubdomain` | a host name or `null` | `null` | The host tracked links and the pixel are served from. |
| `tls` | `opportunistic` / `enforced` | `opportunistic` | `enforced` refuses to deliver to a server that will not encrypt the connection. |

Both tracking settings are off by default, which is usually where they stay for confirmations, receipts and access-recovery mail.

To read domains back: [`mail_domain_list`](/reference/mcp-tools/mail_domain_list) or `GET /mail/domains` for every domain in the product, and [`mail_domain_get`](/reference/mcp-tools/mail_domain_get) or `GET /mail/domains/:id` for one with its records. An id that belongs to another product is a `404`.

## Remove a domain

An agent calls [`mail_domain_delete`](/reference/mcp-tools/mail_domain_delete); over REST, `DELETE /mail/domains/:id`. The response is `{ "deleted": true }`. The domain stops being able to send at once and its claim on the host is released, so the name is free to register again — in this product or another. Emails already sent from the domain keep their history.

## Next steps

- [Create and rotate a sending key](/ship/mail/api-keys): authorise your code to send, and lock the key to the domain you just verified.
- [Send a message](/ship/mail/sending): the fields a send accepts, and what comes back.
- [Handle bounces and protect your sending](/ship/mail/deliverability): what a rejection does to your suppression list and your reputation.
- [How system email works](/ship/mail): where domain setup sits in the seven-step flow.
