# Custom domains

Source: https://agentboxd.com/docs/custom-domains

> Send and receive from your own domain, such as ops@mail.yourcompany.com, with its own DKIM key.

New inboxes get an address on `homingbox.net`; inboxes created before 26 September 2026 keep their `agents.agentboxd.com` address. Temporary inboxes use `tmp.agentboxd.com`, and you can use your own domain on every plan. Connect a domain you own and your agents can use addresses on it instead. Mail to it arrives at our MX; mail from it is DKIM-signed with the domain’s own key.

## Use a subdomain

Connect something like `mail.yourcompany.com`, not `yourcompany.com`. To receive mail, a domain’s MX record has to point to us. On your main domain that would replace the MX your current email provider uses, so every email to `@yourcompany.com`, your team’s included, would come to Agentboxd, and addresses without an inbox here would bounce. A subdomain has its own MX, so nothing changes for the mail you already get.

> **Careful:** The dashboard warns you when a domain looks like a main domain. If you only want to send from one, turn receiving off: we then don’t ask for an MX record.

## Add the domain

In the dashboard go to [Domains](https://agentboxd.com/app/domains) and choose **Add domain**, or call the API. Leave `receiving` on (the default) if inboxes on the domain should get mail.

POST /v1/domains:

```bash
curl -s https://api.agentboxd.com/v1/domains \
  -H "Authorization: Bearer $AGENTBOXD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"mail.yourcompany.com","receiving":true}'
```

The response has `status: "pending"` and the `records` to publish.

## DNS records

| Record | Type | Name | Value | Needed |
| --- | --- | --- | --- | --- |
| verification | TXT | `_agentboxd.<domain>` | `agentboxd-verify=<token>` | Required |
| mx | MX | `<domain>` | `10 mx.agentboxd.com` | Required while receiving is on |
| spf | TXT | `<domain>` | `v=spf1 include:homingbox.net -all` | Required |
| dkim | TXT | `cm1._domainkey.<domain>` | `v=DKIM1; k=rsa; p=<key>` | Required |
| dmarc | TXT | `_dmarc.<domain>` | `v=DMARC1; p=none; rua=mailto:dmarc@agentboxd.com` | Recommended |

### Import them all at once

If your DNS provider imports zone files (Cloudflare and Amazon Route 53 do, among others), you don’t have to add the records one by one. Press **Download zone file** on the domain’s page, or call `GET /v1/domains/:id/zone-file`, and import the file. It has one line per record to publish, with full names, and the long DKIM value already split the way DNS wants it.

GET /v1/domains/:id/zone-file:

```bash
curl -s https://api.agentboxd.com/v1/domains/$DOMAIN_ID/zone-file \
  -H "Authorization: Bearer $AGENTBOXD_API_KEY" | jq -r .content > mail.yourcompany.com.zone.txt
```

> **Careful:** Read the file before you import it. If the name already has an SPF record, add our include to that record and delete the SPF line from the file: a name with two SPF records fails SPF. Do the same with the DMARC line if the domain already has a DMARC record.

The exact token and key are on the domain’s page in the dashboard, with copy buttons. Each record in the API looks like `{ key, type, name, value, required, status, found? }`, where `status` is `ok`, `missing` or `mismatch` and `found` lists what we saw instead.

- **SPF**: a name can have only one SPF record. If yours already has one, add `include:homingbox.net` to it instead of adding a second. A domain connected earlier with `include:agents.agentboxd.com` keeps verifying; nothing to change.
- **DKIM**: the `p=` value must match exactly. Paste it in one piece; if your provider splits long TXT values into several strings, that’s fine.
- **MX**: the check passes when the lowest-priority MX host is `mx.agentboxd.com`.

## At your DNS provider

Every provider has an “add record” form with a type, a name (or host) and a value. Many of them append your domain to the name automatically: then enter only the host part. For `mail.yourcompany.com`, the verification record’s host part is `_agentboxd.mail` and the MX host part is `mail`. Paste the full name into a field like that and the record ends up at `_agentboxd.mail.yourcompany.com.yourcompany.com`, which our check reports as missing.

The dashboard reads your domain’s nameservers to tell where its DNS is hosted, opens that provider’s steps and shows every record’s host part relative to the real zone. The API returns the same as `dns` on the domain (`{ zone, nameservers, provider: { id, name } | null, checked_at }`) and `host` on each record. If a record is missing but we find it under the doubled name, it carries `hint: "full_name_in_host"`: rename it to its host part.

### Cloudflare

- Open the `yourcompany.com` zone, go to **DNS → Records** and choose **Add record**.
- Add a `TXT` record: Name `_agentboxd.mail`, Content the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, Mail server `mx.agentboxd.com`, Priority `10`.
- Paste long values such as the DKIM key in one piece: Cloudflare adds the quotes and splits values over 255 characters itself.
- TXT and MX records are never proxied, so there is no proxy setting to change.
- Source: [Cloudflare’s help](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/), checked 2026-09-27.

### GoDaddy

- In your **Domain Portfolio** select `yourcompany.com` to open **Domain Settings**, then choose **DNS** and **Add New Record**.
- Add a `TXT` record: Name `_agentboxd.mail`, Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, Value `mx.agentboxd.com`, Priority `10`.
- A value can be up to 1,024 characters, so paste the DKIM key in one piece.
- GoDaddy warns that changing MX records can interrupt email already connected to the domain. An MX record on a subdomain such as `mail` only concerns mail to that subdomain.
- Source: [GoDaddy’s help](https://www.godaddy.com/en/help/add-a-txt-record-19232), checked 2026-09-27.

### Namecheap

- In your **Domain List** choose **Manage** next to `yourcompany.com`, then **Advanced DNS**. TXT records go under **Host Records → Add New Record**.
- Add a `TXT` record: Host `_agentboxd.mail`, Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Host `mail`, Value `mx.agentboxd.com`, Priority `10`.
- Click **Save all changes**.
- MX records go under **Mail Settings**: choose **Custom MX**, add the row, then **Save changes**. Mail Settings is one choice for the whole of `yourcompany.com`: if it uses Namecheap Email Forwarding or Private Email today, Custom MX replaces their MX records, so add theirs as Custom MX rows too (Private Email: `mx1.privateemail.com` and `mx2.privateemail.com`, priority `10`).
- Host Records appear when `yourcompany.com` uses Namecheap BasicDNS, PremiumDNS or FreeDNS. On Namecheap hosting nameservers, add the records in cPanel instead.
- Source: [Namecheap’s help](https://www.namecheap.com/support/knowledgebase/article.aspx/317/2237/how-do-i-add-txtspfdkimdmarc-records-for-my-domain/), checked 2026-09-27.

### Amazon Route 53

- In the Route 53 console open **Hosted zones → yourcompany.com → Create record**, keep **Simple routing**, and finish with **Create records**.
- Add a `TXT` record: Record name `_agentboxd.mail`, Value the record’s value in double quotes. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Record name `mail`, Value `10 mx.agentboxd.com`.
- Route 53 wants TXT values in double quotes, and a value over 255 characters as several quoted strings on one line. The dashboard shows this provider’s values already quoted and split: paste them as they are.
- Source: [Amazon Route 53’s help](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-values-basic.html), checked 2026-09-27.

### Squarespace Domains

- In the domains dashboard open `yourcompany.com`, then **DNS → DNS Settings**. Scroll to **Custom Records** and choose **Add record** (Squarespace asks for your password or two-factor code first).
- Add a `TXT` record: Name `_agentboxd.mail`, Text the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, Mail Server `mx.agentboxd.com`, Priority `10`.
- Squarespace adds `yourcompany.com` to the name itself.
- Source: [Squarespace Domains’s help](https://support.squarespace.com/hc/en-us/articles/31120980444429-Adding-TXT-records), checked 2026-09-27.

### Google Cloud DNS

- On the **Cloud DNS zones** page click `yourcompany.com`, then **Add standard**, and **Create** once the record set is filled in.
- Add a `TXT` record: DNS name `_agentboxd.mail`, record data the record’s value in double quotes. Repeat for each TXT record, with its own host part.
- Add an `MX` record: DNS name `mail`, Preference and mail server `10 mx.agentboxd.com.`.
- A TXT value over 255 characters has to be split into quoted strings of up to 255: the dashboard shows the DKIM value that way.
- Source: [Google Cloud DNS’s help](https://docs.cloud.google.com/dns/docs/records), checked 2026-09-27.

### OVHcloud

- In the Control Panel open **Web Cloud → Domain names → yourcompany.com**, then the **DNS zone** tab and **Add an entry**. Pick the type, fill in the form and choose **Add**.
- Add a `TXT` record: Subdomain `_agentboxd.mail`, Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Subdomain `mail`, Target `mx.agentboxd.com.`, Priority `10`.
- End the MX target with a dot, as shown: without it OVHcloud appends `yourcompany.com` to it.
- Source: [OVHcloud’s help](https://docs.ovhcloud.com/en/guides/web-cloud/domains/dns-zone-edit), checked 2026-09-27.

### Gandi

- Click **Domain** in the left menu, then `yourcompany.com`, the **DNS Records** tab and **Add**.
- Add a `TXT` record: name `_agentboxd.mail`, value the record’s value in double quotes. Repeat for each TXT record, with its own host part.
- Add an `MX` record: name `mail`, mail server `mx.agentboxd.com.`, priority `10`.
- Gandi requires the final dot on the mail server.
- A TXT string is limited to 255 characters, so the DKIM value goes in as several quoted strings: the dashboard shows it that way.
- Source: [Gandi’s help](https://docs.gandi.net/en/domain_names/common_operations/dns_records.html), checked 2026-09-27.

### IONOS

- Open **Domains & SSL**, click the gear icon under **Actions** next to `yourcompany.com`, choose **DNS**, then **Add record** and the type.
- Add a `TXT` record: Host name `_agentboxd.mail`, Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Host name `mail`, Points to `mx.agentboxd.com`, Priority `10`.
- Changes can take up to an hour to show outside IONOS.
- Source: [IONOS’s help](https://www.ionos.com/help/domains/configuring-txt-and-srv-records/managing-txt-records/), checked 2026-09-27.

### Porkbun

- In **Domain Management** click **DNS** on the `yourcompany.com` row, fill in the form and click **Add**.
- Add a `TXT` record: Host `_agentboxd.mail`, Answer the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Host `mail`, Answer `mx.agentboxd.com`, Priority `10`.
- Source: [Porkbun’s help](https://kb.porkbun.com/article/231-how-to-add-dns-records-on-porkbun), checked 2026-09-27.

### Hostinger

- In hPanel go to **Domains**, choose **Manage** next to `yourcompany.com`, then **DNS / Nameservers → Manage DNS records**.
- Add a `TXT` record: Name `_agentboxd.mail`, TXT value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, Mail server `mx.agentboxd.com`, Priority `10`.
- Source: [Hostinger’s help](https://www.hostinger.com/support/1583664-how-to-manage-txt-records-at-hostinger/), checked 2026-09-27.

### Vercel

- In your team dashboard choose **Domains** in the sidebar and click `yourcompany.com`. Use the DNS record form on its **Advanced Settings** page and click **Add**.
- Add a `TXT` record: Name `_agentboxd.mail`, Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, Value `mx.agentboxd.com`, Priority `10`.
- Source: [Vercel’s help](https://vercel.com/docs/domains/managing-dns-records), checked 2026-09-27.

### DigitalOcean

- Open **Networking → Domains → yourcompany.com**, choose **Create a record**, pick the type and click **Create Record**.
- Add a `TXT` record: Hostname `_agentboxd.mail`, TXT Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Hostname `mail`, Mail providers mail server `mx.agentboxd.com`, Priority `10`.
- Source: [DigitalOcean’s help](https://docs.digitalocean.com/products/networking/dns/how-to/manage-records/), checked 2026-09-27.

### Hetzner

- In the [Hetzner Console](https://console.hetzner.com) choose **DNS**, select `yourcompany.com`, pick the record type and click **Add**.
- Add a `TXT` record: Name `_agentboxd.mail`, Value the record’s value in double quotes. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, mail server `mx.agentboxd.com.` with priority `10` (or `10 mx.agentboxd.com.` if the form has a single value field).
- TXT values have to be typed in double quotes, and one string holds at most 255 characters: the dashboard shows the values quoted and the DKIM key split.
- Hetzner adds `yourcompany.com` to a name or target unless it ends with a dot, so end the mail server with one.
- Zones from the former DNS Console (dns.hetzner.com) now live in the Hetzner Console.
- Source: [Hetzner’s help](https://docs.hetzner.com/networking/dns/record-types/txt-record/), checked 2026-09-27.

### Azure DNS

- In the Azure portal open **DNS zones → yourcompany.com**, choose **+ Record sets**, then **+ Add**, and **Add** on the **Add record set** page.
- Add a `TXT` record: Name `_agentboxd.mail`, Value the record’s value. Repeat for each TXT record, with its own host part.
- Add an `MX` record: Name `mail`, Mail exchange `mx.agentboxd.com`, Preference `10`.
- Paste the DKIM key in one piece: the portal splits long values into 255-character strings itself.
- Source: [Azure DNS’s help](https://learn.microsoft.com/en-us/azure/dns/dns-zones-records), checked 2026-09-27.

### Any other provider

- Open the page where the zone’s DNS records are managed and add one record per row of the table above: type, name (or host) and value.
- If the form adds the zone to the name by itself, enter only the host part. If it asks for a fully qualified target and appends the zone to yours, end the mail server with a dot: `mx.agentboxd.com.`
- If the form rejects a value longer than 255 characters (the DKIM key), enter it as several quoted strings on one line: `"first 255 characters" "the rest"`.

## Verification

A domain is verified once every required record checks out. Press **Check now** on its page (or call `POST /v1/domains/:id/verify`, at most once every 10 seconds per domain). We also check pending domains every 10 minutes for 7 days, and verified ones every 6 hours. DNS changes usually show up within minutes, sometimes a few hours.

| Status | Meaning |
| --- | --- |
| `pending` | Waiting for the required records. Inboxes can’t use the domain yet. |
| `verified` | All required records pass. Create inboxes with `POST /v1/inboxes { username, domain }`. |
| `failed` | A verified domain failed its required records 3 checks in a row. Sending from it pauses (`403 domain_not_verified`), and mail already queued waits until it verifies again, for at most 48 hours, after which it fails with the reason in `x-mailroom-error`. Receiving continues while the MX still points to us. Fix the records and check again. |
| `disabled` | Turned off by us. Write to [support@agentboxd.com](mailto:support@agentboxd.com). |

Two webhook events follow the status, so you don’t have to poll: `domain.verified` when a domain passes its required records (the first time, or again after `failed`), and `domain.failed` when sending from it is paused. Both carry `{ domain, previous_status }`.

## Rotating the DKIM key

Each domain signs with its own 2048-bit key on selector `cm1`. Rotate it once a year, or at once if you think it leaked. Mail keeps flowing the whole time:

- Press **Rotate DKIM key** on the domain’s page, or call `POST /v1/domains/:id/dkim/rotate`. You get a new key on the other selector (`cm2`, then `cm1` again next time), listed as the `dkim_next` record. The current key keeps signing.
- Publish that TXT record next to the current one. Don’t remove the old one.
- As soon as a check sees the new record we switch signing to it: the background check runs every 10 minutes while a rotation is pending, and **Check and switch now** (`POST /v1/domains/:id/dkim/activate`) does it at once. A `domain.dkim_rotated` webhook fires.
- The old record is then listed as `dkim_previous` with a date 7 days out. Keep it until then, so mail signed just before the switch still verifies wherever it is delivered late; after that date it disappears from the list and you can delete it.

A domain’s `dkim` field shows the state: `{ selector, active_since, rotation_due, next, previous }`. Once a key has signed for a year, `rotation_due` turns true, the dashboard shows a badge, and a `domain.dkim_rotation_due` webhook fires once. Rotating again while the old key is still being retired answers `409 dkim_rotation_too_soon`; rotating while a rotation is pending replaces the pending key.

## Inboxes on your domain

Pass `domain` when creating an inbox. Reserved usernames (role names such as `admin@`, `support@` and `postmaster@`) apply only on our domains: on your own verified domains you may use any name. Mail to addresses that have no inbox is refused during the SMTP conversation.

An address stays reserved once it has been used, even after its inbox or the domain is deleted. This also holds when the domain was used by another workspace before you: creating such an address answers `409 address_taken` with “this address was used before and stays reserved”. Choose another username.

agent.ts:

```ts
const inbox = await mr.inboxes.create({ username: 'ops', domain: 'mail.yourcompany.com' });
console.log(inbox.address); // ops@mail.yourcompany.com
```

## API

| Method & path | Notes |
| --- | --- |
| `POST /v1/domains` | `{ domain, receiving? }` → 201 with the records. Refused: our own domains, public suffixes, malformed names, and domains verified by another workspace (`409 domain_taken`). How many depends on the plan (Free 1, Builder 10, Team 50; `403 plan_limit_domains` past it). |
| `GET /v1/domains` | List. |
| `GET /v1/domains/:id` | The domain with its `records`. |
| `GET /v1/domains/:id/zone-file` | The records to publish as a BIND zone file: `{ filename, content }`. Leaves out the MX while receiving is off, and the replaced DKIM key of a rotation, which is already published. |
| `POST /v1/domains/:id/verify` | Run the checks now (1 per 10 s per domain). |
| `POST /v1/domains/:id/dkim/rotate` | Start a DKIM key rotation: a new key on the other selector, listed as the `dkim_next` record. `409 domain_not_verified` for a pending domain, `409 dkim_rotation_too_soon` while the last replaced key is still being retired. |
| `POST /v1/domains/:id/dkim/activate` | Check DNS now and switch signing to the new key. `409 dkim_record_not_found` (with `details.record`) until the record is published; `409 no_dkim_rotation` without a rotation. Shares the 10-second check limit. |
| `PATCH /v1/domains/:id` | `{ receiving }`. |
| `DELETE /v1/domains/:id` | `409 domain_in_use` while inboxes use it. `?force=true` also deletes those inboxes, as if you deleted each one: their agent cards are erased and mail still queued from them is not sent. |

The SDKs have `domains.create/list/get/zoneFile/verify/update/delete` and `domains.rotateDkim/activateDkim` (`zone_file`, `rotate_dkim` and `activate_dkim` in Python). There is no MCP tool for domains: setting one up is a job for a person with access to DNS.
