Replace a domain's subordinate hosts
Points a domain at a new set of subordinate host objects, the host records that live in the domain's own zone, replacing whatever it currently has. Send the full set you want: hosts you leave out are removed, and an empty array leaves the domain with none.
The response is always a change request, never the domain.
Host objects are held at the registry, so this endpoint records what
you asked for and answers 202 while it is carried out. Read the
set back once the change reports applied.
Where to read the set back: GET /domains/{id}/hosts, not
GET /domains/{id}. The domain resource carries the nameserver
delegation, which is a different thing: names anywhere on the
internet, with no addresses. The host endpoint returns each host's
glue as address objects, and addresses[].address from there is
what this endpoint's addresses array takes, so a set read back
and resent evaluates as a no-op.
| Status | Meaning | What to do |
|---|---|---|
202 |
Accepted and running. | Poll GET /changes/{id} until state is applied or failed, then re-read GET /domains/{id}/hosts. |
409 |
Another change already holds part of this domain. | Wait for the change named in conflict.change_request_id, then try again. |
422 |
Refused. The body lists every reason. | Follow each issue's next_step. |
Send ?dry_run=true to see what would happen without recording
anything. The answer is a ChangeDryRun carrying the same verdict
and the same reasons, and no change is created either way. A dry
run draws on a rate limit of its own, wider than the one changes
use, so previewing a change while you compose it does not spend
the requests you need to send it.
A hostname has to be a subdomain of this domain; one that is not is
refused with host_unknown. Removing a host still named in the
domain's own nameservers set is refused with
host_still_delegated; detach the delegation first. More hosts or
addresses than the registry accepts, a malformed hostname or
address, or a repeated one, is refused with payload_invalid.
Every host has to carry at least one address. A host in this
set lives inside this domain's own zone, so nothing can reach the
name unless this domain publishes an address for it. A host with an
empty addresses array is refused with payload_invalid naming
the hostname. There are two ways to clear that: give the host an
address, or leave it out of the set entirely, which removes it.
Every address has to be publicly routable. The registry
publishes glue to every resolver, so an address the public
internet cannot reach is refused with payload_invalid, and the
detail names the first such address as you sent it.
The update lock refuses this endpoint entirely. A domain
carrying clientUpdateProhibited or serverUpdateProhibited is
refused with domain_locked, whatever the set names: the glue
under a name is where that name resolves, so editing it while the
name is frozen would move the domain without changing the domain.
Clear the lock through PATCH /domains/{id} with
{"locks":{"update":false}}, make the host change, then set it
again. PATCH /domains/{id} takes hosts beside locks, so the
unlock and the host change travel in one request there and the name
spends no time unlocked between them. That applies to every host in
the set, including ones this domain does not itself delegate to.
Permissions
| Key type | Accepted | Permission required | Notes |
|---|---|---|---|
| Org API key | yes | domain:write |
Reaches every team in the key's org |
| Personal API key | yes | domain:write |
Caller must have an active membership in the org named by X-Org-ID; a domain outside their teams returns 403 |
Error Codes
| Code | HTTP | Description |
|---|---|---|
| domain.change.unauthorized | 401 | No valid credentials |
| domain.change.no_org | 403 | No active organization in scope for the call |
| domain.change.rate_limited | 429 | Too many change requests; Retry-After says how long to wait |
| domain.change.dry_run_rate_limited | 429 | Too many dry runs; Retry-After says how long to wait |
| domain.change.invalid_id | 400 | Domain id is malformed |
| domain.change.invalid_dry_run | 400 | dry_run is not true or false |
| domain.change.subordinate_hosts.decode_failed | 400 | Request body is not readable JSON |
| domain.change.subordinate_hosts.validation_failed | 400 | Request body is missing hosts, or a field is out of bounds |
| domain.change.not_found | 404 | Domain not found, or it belongs to another organization |
| domain.change.no_team_access | 403 | Domain belongs to your organization but is outside your team scope |
| domain.change.conflict | 409 | Another open change already holds part of this domain |
| domain.change.denied | 422 | The change cannot be applied; see issues |
| domain.change.subordinate_hosts.failed | 500 | The change could not be recorded |
Path Parameters
- Type: stringidrequired
Domain typeid
Query Parameters
- Type: booleandry
_run Judge the change and return the verdict without recording anything
Body·
The subordinate-host set
The subordinate-host set a domain should end up with.
- Type:hostsrequiredProperties: 2
The full subordinate-host set, replaced whole. An empty array leaves the domain with no host objects of its own. Required: omit it and the request is refused rather than read as an empty set.
- Type: integerbase
_version min:1The domain
versionthis change was composed against. Send it and a domain that moved since you read it refuses the change instead of writing over the move. Omit it and nothing is asserted. - Type: stringcommentmax length:2000
Your own note, kept on the change and shown wherever it is read. Never interpreted.
Responses
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
curl 'https://api.nametrust.com/domains/{id}/hosts' \
--request PUT \
--header 'Content-Type: application/json' \
--data '{
"hosts": [
{
"hostname": "ns1.example.com",
"addresses": [
"1.1.1.1",
"2606:4700:4700::1111"
]
}
],
"base_version": 7,
"comment": "add a second nameserver'\''s glue"
}'
{
"object": "change_dry_run",
"domain_id": "dom_01h455vb4pex5vsknk084sn02q",
"outcome": "allow",
"applies_immediately": false,
"kinds": [
"domain.nameservers"
],
"slots": [
"delegation"
],
"issues": [
{
"object": "change_issue",
"code": "domain_locked",
"next_step": "clear_lock",
"detail": "clientUpdateProhibited",
"kind": "domain.nameservers"
}
]
}