# Hosts

A key manages a list of hosts. A host is a whole domain (`example.com`) or a name inside one (`advisor.example.com`). You manage records **at** each host and **under** it (`_dmarc.advisor.example.com`, `google._domainkey.advisor.example.com`).

When a key's host is a name inside a domain, everything else in that root domain belongs to someone else: you never see it and can't change it. The rest of this page is about that case; for a whole-domain host, the host and the root are the same name.

## Two ways to address a host

Every API style accepts either form, call by call.

### 1. The host as its own domain (simplest)

Put the host in `domain` (Dynadot style) or in the path (Spaceship style):

- Records **at** the host are main records (Dynadot) or name `@` (Spaceship).
- Records **under** it use the part in front of the host as the subhost or name: `_dmarc`, `google._domainkey`.

```bash
curl https://api.maildns.io/api3.json \
  --data-urlencode key=KEY --data-urlencode command=set_dns2 \
  --data-urlencode domain=advisor.example.com \
  --data-urlencode add_dns_to_current_setting=1 \
  --data-urlencode subdomain0=_dmarc \
  --data-urlencode sub_record_type0=txt \
  --data-urlencode 'sub_record0=v=DMARC1; p=quarantine'
```

### 2. Root domain plus subhost

Put the root domain in `domain` and the full name under it as the subhost, the way registrars normally store subdomain records. Scripts written against a registrar often work this way already.

```bash
curl https://api.maildns.io/api3.json \
  --data-urlencode key=KEY --data-urlencode command=set_dns2 \
  --data-urlencode domain=example.com \
  --data-urlencode add_dns_to_current_setting=1 \
  --data-urlencode subdomain0=_dmarc.advisor \
  --data-urlencode sub_record_type0=txt \
  --data-urlencode 'sub_record0=v=DMARC1; p=quarantine'
```

In root form, `get_dns` lists only your hosts' records, all as subhosts. Main records (Dynadot) and `@` (the other styles) mean the root domain itself, which is not yours: sending one is an error.

### Both forms in each style

| Style | Host form | Root form |
|---|---|---|
| Dynadot | `domain=advisor.example.com`; main records, or `subdomain0=_dmarc` | `domain=example.com`; `subdomain0=_dmarc.advisor` |
| Spaceship | `/dns/records/advisor.example.com`; names `@`, `_dmarc` | `/dns/records/example.com`; names `advisor`, `_dmarc.advisor` |
| Cloudflare | the host's zone (`/zones?name=advisor.example.com`); names `@`, `_dmarc` or full | the root's zone (`/zones?name=example.com`); full names |
| GoDaddy | `/v1/domains/advisor.example.com/records`; names `@`, `_dmarc` | `/v1/domains/example.com/records`; names `advisor`, `_dmarc.advisor` |
| Porkbun | `/dns/create/advisor.example.com`; `name` empty or `_dmarc` | `/dns/create/example.com`; `name` `advisor` or `_dmarc.advisor` |
| Namecheap | `SLD=advisor.example&TLD=com`; `HostName1=@` or `_dmarc` | `SLD=example&TLD=com`; `HostName1=advisor` or `_dmarc.advisor` |

Replies name records the way each style does: Cloudflare and Porkbun give full names, the others give names relative to what you addressed.

## The same records in both forms

| Full name | Type | Host form: `domain=advisor.example.com` | Root form: `domain=example.com` |
|---|---|---|---|
| `advisor.example.com` | TXT, MX | main record, or name `@` | subhost `advisor` |
| `google._domainkey.advisor.example.com` | TXT | `google._domainkey` | `google._domainkey.advisor` |
| `_dmarc.advisor.example.com` | TXT | `_dmarc` | `_dmarc.advisor` |
| `mail.advisor.example.com` | CNAME | `mail` | `mail.advisor` |
| `example.com` | any | not reachable | not yours: error |
| `www.example.com` | any | not reachable | not yours: error |

## Which form to use

- **One host at a time:** host form. Names are shorter and there is only one way to get them right.
- **Several hosts in the same root domain in one call:** root form. One call (`set_dns2`, `PUT`, `PATCH`, `setHosts`) can carry records for every host your key has in that root, and it is applied as one change.
- **Reading:** either. In root form, reads return the records of all your hosts in that root, and nothing else.

## Replace calls work per host

Some calls **replace** instead of adding: every host the call mentions ends up with exactly the records in the call, including the records under it. Hosts the call does not mention keep all their records.

| Style | Replaces | Adds |
|---|---|---|
| Dynadot | `set_dns2` without `add_dns_to_current_setting=1` | `set_dns2` with `add_dns_to_current_setting=1` |
| GoDaddy | `PUT /records`; `PUT /records/{type}` (that type only) | `PATCH /records` |
| Namecheap | `setHosts`, always | none: read with `getHosts` and send everything back |
| Spaceship | never | `PUT` |
| Cloudflare | never (`PUT` replaces one record by id) | `POST` |
| Porkbun | never (`editByNameType` changes one type at one name) | `/dns/create` |

- In host form, the call mentions the host in `domain`.
- In root form, it mentions each host that one of its subhosts belongs to: `_dmarc.advisor` mentions `advisor.example.com`.

> **Careful:** a replace that sends only the DMARC record (`subdomain0=_dmarc.advisor`, or `HostName1=_dmarc`) leaves `advisor.example.com` with that one record. Its MX, SPF, verification and DKIM records are removed. Use the add call unless you mean to replace.

On Dynadot, GoDaddy and Namecheap themselves a replace rewrites the whole domain. MailDNS limits it to the hosts you name, so a record forgotten for one host can't take another host down.

## Listing hosts

Every style lists your key's hosts and nothing else: `list_domain` (Dynadot), `GET /api/v1/domains` (Spaceship), `GET /zones` (Cloudflare), `GET /v1/domains` (GoDaddy), `/domain/listAll` (Porkbun), `namecheap.domains.getList` (Namecheap). Hosts can't be added through the API.

## Names that are not yours

| You sent | Reply (`Error` or `detail`) |
|---|---|
| A domain that is neither one of your hosts nor the root of one | `could not find domain in your account` (404 in the styles that use status codes) |
| A name in the root that is not at or under one of your hosts | `www.example.com is not one of your hosts` (403) |
| A main record with `domain` set to the root | `main records of example.com are not on your account; set records on your hosts with subdomain0 / sub_record_type0 / sub_record0` (403) |
