Skip to main content

Need a powerful server for your projects?

Discover our offers

DNS and rDNS through the API and MCP

Everything the DNS & rDNS page does is also available in the YorkHost client API and MCP server, with exactly the same checks: ownership of domains and IP addresses, validations, protected NS and SOA, read-only cPanel zones, hourly change limit shared with the client area.

This page covers DNS and rDNS. For getting started (two-factor authentication required, key creation, connecting an assistant), see:

Permissions​

PermissionLabel in the client area (API keys)What it opens
dns:readRead domains and DNS zonesDomain list, DNS state, records, BIND export, DNSSEC state, template list
dns:writeEdit DNS recordsAdding, editing and deleting records, zone creation, BIND import, DNSSEC, applying a template
rdns:readRead IP reverse DNS (PTR)List of IP addresses and their PTR
rdns:writeEdit IP reverse DNS (PTR)Setting, resetting and bulk changes of PTR records

The Read only template contains dns:read and rdns:read; the AI agent (no destructive action) template adds dns:write and rdns:write.

Before writing
  • Protected NS and SOA: the SOA and the name servers of the domain itself (protected: true in the record list) cannot be changed or deleted (code rec_err_protected); an import ignores them.
  • Read-only cPanel zones: a zone hosted on cPanel (readonly: true) can be read through the API but is edited in cPanel. Any write returns 403 zone_err_readonly.
  • PATCH …/records/{record_id} replaces the whole record: send the complete record (name, type, TTL and every field of the type), not only the changed field. The id changes on every edit: use the one returned in the response.
  • /dns/* routes ignore the service restriction of the key: a domain is not a service. A dns:write key restricted to a single VPS can change every domain of the account. The /rdns* routes do follow the restriction (an IP address outside the allowed services returns 404).
  • Changes are public and cached by resolvers for the TTL.

Authentication​

Base: https://api.yorkhost.fr, header Authorization: Bearer <key>. The examples assume the key is in a variable:

export YH_KEY="yhk_client_v1_..."

Reverse DNS​

Method and routePurposePermission
GET /client/v1/rdns (?service_id=)IP addresses of the account's services and their PTRrdns:read
PUT /client/v1/rdns/{ip}Set the PTR of an IP address ({"hostname": "…"})rdns:write
DELETE /client/v1/rdns/{ip}Restore the default YorkHost PTRrdns:write
POST /client/v1/rdns/bulkBulk change (512 lines max)rdns:write

List IP addresses​

curl -H "Authorization: Bearer $YH_KEY" \
"https://api.yorkhost.fr/client/v1/rdns?service_id=12345"

Response: {"ips": [{ip, service_id, service_name, list, ptr, ipam_value, state, default}]}. state is ok, pending, nozone (reverse zone not managed by YorkHost) or error. Without service_id, every IP address the key can access is returned.

Set the PTR of an IP address​

curl -X PUT https://api.yorkhost.fr/client/v1/rdns/192.0.2.10 \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" \
-d '{"hostname": "mail.example.com"}'

Response: {"ip": "192.0.2.10", "hostname": "mail.example.com", "warning": null}. If the name does not resolve to the IP address, warning is rdns_warn_forward: add the matching A or AAAA record (see forward-confirmed reverse DNS). The name must be a fully qualified host name.

Restore the default PTR​

curl -X DELETE https://api.yorkhost.fr/client/v1/rdns/192.0.2.10 \
-H "Authorization: Bearer $YH_KEY"

Response: {"ip": "192.0.2.10", "hostname": "<default name>"}. PUT with an empty hostname is refused: use DELETE.

Bulk rDNS​

# 1. Check: nothing is changed
curl -X POST https://api.yorkhost.fr/client/v1/rdns/bulk \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" \
-d '{"check_only": true, "entries": [
{"ip": "192.0.2.10", "hostname": "mail.example.com"},
{"ip": "192.0.2.11", "hostname": "web1.example.com"},
{"ip": "2001:db8::10", "hostname": null}
]}'

# 2. Apply: same call with "check_only": false
  • Empty or null hostname: default PTR.
  • Response: {mode, needed, remaining, counts: {ok, unchanged, error, skipped}, rows: [{n, ip, hostname, name, result, error, message, warning}]}. needed is the number of changes in the batch, remaining what is left of the hourly limit.
  • From 1 to 512 lines per call, 5 calls per minute per key.
  • If the batch exceeds the remaining hourly limit, it is refused as a whole (429 err_rate): nothing is written.
check_only is required

A call without check_only (or with a non-boolean value) is rejected with 400 bad_request and nothing is changed. Send "check_only": true first to check, then the same call with "check_only": false to apply. The rdns_bulk MCP tool follows the same rule.

Each line follows the rules of the bulk change in the client area.

Domains and zones​

A domain is identified by its name in the path (example.com, IDN in Unicode or punycode), among the domains and DNS zones of your account.

Method and routePurposePermission
GET /client/v1/dns/domainsDomains and zones of the account, with their statedns:read
GET /client/v1/dns/domains/{domain}DNS state of a domain: name servers and zonedns:read
GET /client/v1/dns/domains/{domain}/recordsRecords of the zonedns:read
POST /client/v1/dns/domains/{domain}/recordsAdd a recorddns:write
PATCH /client/v1/dns/domains/{domain}/records/{record_id}Replace a recorddns:write
DELETE /client/v1/dns/domains/{domain}/records/{record_id}Delete a recorddns:write
POST /client/v1/dns/domains/{domain}/zoneCreate the zone ("Prepare the zone")dns:write
GET /client/v1/dns/domains/{domain}/exportExport the zone in BIND formatdns:read
POST /client/v1/dns/domains/{domain}/importImport a BIND zone (5 per minute per key)dns:write
GET /client/v1/dns/domains/{domain}/dnssecDNSSEC state, DS to set, keysdns:read
PUT /client/v1/dns/domains/{domain}/dnssecEnable or disable DNSSEC ({"enabled": true})dns:write
GET /client/v1/dns/domains/{domain}/presetsAvailable e-mail templatesdns:read
POST /client/v1/dns/domains/{domain}/presets/{preset}Apply a templatedns:write

List domains​

curl -H "Authorization: Bearer $YH_KEY" https://api.yorkhost.fr/client/v1/dns/domains

Response: {"domains": [{domain, display, kind, status, expires, ns_state, zone_exists, readonly, source}]}.

  • kind: domain or dnszone;
  • ns_state: managed (delegated to the YorkHost servers), mixed, external or unknown. Records are only served publicly for managed; an existing zone (zone_exists: true) can be edited whatever this state;
  • readonly: true: zone hosted on cPanel.

GET /client/v1/dns/domains/example.com returns {domain, display, ns: {state, live, registrar, managed}, zone: {exists, readonly, source, dnssec, serial, count}}.

Read records​

curl -H "Authorization: Bearer $YH_KEY" \
https://api.yorkhost.fr/client/v1/dns/domains/example.com/records

Response: {"domain": "example.com", "records": [{id, name, fqdn, type, ttl, content, protected, …fields of the type}]}. name is relative to the zone (@ for the domain itself); id (32 hexadecimal characters) is used to edit or delete.

Add a record​

curl -X POST https://api.yorkhost.fr/client/v1/dns/domains/example.com/records \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" \
-d '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 3600}'
curl -X POST https://api.yorkhost.fr/client/v1/dns/domains/example.com/records \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" \
-d '{"name": "@", "type": "MX", "priority": 10, "target": "mail.example.com"}'

Response 201: {"id": "…"}. Fields to send depend on the type (the same as the zone editor form); any other key is refused (400 bad_request):

TypeFields
A, AAAA, CNAME, ALIAS, TXT, NS, PTRvalue
MXpriority, target (or value)
SRVpriority, weight, port, target (or value)
CAAflags, tag (issue, issuewild, iodef), value
TLSAusage, selector, matching, data
SSHFPalgorithm, fptype, fingerprint
DSkeytag, algorithm, digesttype, digest
NAPTRorder, preference, flags, service, regexp, replacement

Plus name, type and, optionally, ttl (60 to 604800; default TTL when absent). For a TXT record, send the text without quotes: {"name": "_dmarc", "type": "TXT", "value": "v=DMARC1; p=none"}.

Edit a record​

curl -X PATCH https://api.yorkhost.fr/client/v1/dns/domains/example.com/records/0123456789abcdef0123456789abcdef \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" \
-d '{"name": "www", "type": "A", "value": "192.0.2.20", "ttl": 3600}'

Send the complete record. Response: {"id": "…"} with the new id.

Delete a record​

curl -X DELETE https://api.yorkhost.fr/client/v1/dns/domains/example.com/records/0123456789abcdef0123456789abcdef \
-H "Authorization: Bearer $YH_KEY"

Response: {"ok": true, "domain": "example.com", "id": "…"}.

Create the zone​

curl -X POST https://api.yorkhost.fr/client/v1/dns/domains/example.com/zone \
-H "Authorization: Bearer $YH_KEY"

Response: {"created": true}, or false if the zone already existed. For a .fr domain, create the zone before changing the name servers (see Point a domain to YorkHost).

Export and import (BIND)​

# Export: the zone file is in the "bind" field
curl -s -H "Authorization: Bearer $YH_KEY" \
https://api.yorkhost.fr/client/v1/dns/domains/example.com/export | jq -r .bind > example.com.zone

# Import: adds the records ("replace": true replaces those with the same name and type)
jq -Rs '{bind: ., replace: false}' example.com.zone | \
curl -X POST https://api.yorkhost.fr/client/v1/dns/domains/example.com/import \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" --data-binary @-

Import response: {added, skipped} (skipped lists the skipped lines and why). The SOA and NS of the domain are always ignored. Zone file of 256 KB max, 5 imports per minute per key.

DNSSEC​

curl -X PUT https://api.yorkhost.fr/client/v1/dns/domains/example.com/dnssec \
-H "Authorization: Bearer $YH_KEY" -H "Content-Type: application/json" \
-d '{"enabled": true}'

Response: {enabled, ds}. Then set the DS at your registrar. To disable, first remove the DS at the registrar, then send {"enabled": false}: see DNSSEC.

E-mail templates​

curl -H "Authorization: Bearer $YH_KEY" \
https://api.yorkhost.fr/client/v1/dns/domains/example.com/presets

curl -X POST https://api.yorkhost.fr/client/v1/dns/domains/example.com/presets/google_workspace \
-H "Authorization: Bearer $YH_KEY"

Template keys: google_workspace, microsoft365, zoho_eu, protonmail, ovh_mx. As in the client area, existing MX and SPF records are replaced (see E-mail templates). Response: {"applied": <number of records>}.

MCP tools​

ToolPurposePermission
dns_list_domainsDomains and zones of the account, name server state, read-only zonesdns:read
dns_get_domainDNS state of a domain (name servers, zone, DNSSEC, serial)dns:read
dns_list_recordsRecords of a zone, with their id and protecteddns:read
dns_export_zoneBIND export of the zone (backup before changes)dns:read
dns_add_recordAdd a record (A, AAAA, CNAME, MX, TXT, SRV, CAA…)dns:write
dns_update_recordReplace a record: the assistant must send the complete recorddns:write
dns_delete_recordDelete a recorddns:write
rdns_listIP addresses of the services and their PTRrdns:read
rdns_setSet the PTR of an IP addressrdns:write
rdns_resetRestore the default PTRrdns:write
rdns_bulkBulk rDNS (512 lines max), always in check mode (check_only: true) firstrdns:write

Zone creation, BIND import, DNSSEC and e-mail templates are not available as MCP tools: use the client area or the REST API. The tools ask the assistant to confirm with you before any DNS or rDNS change.

Examples of requests to a connected assistant:

  • "List my domains and tell me which ones do not point to YorkHost yet."
  • "Add a _dmarc TXT record on example.com with v=DMARC1; p=none."
  • "Set the reverse DNS of 192.0.2.10 to mail.example.com and check that the A record exists."

Limits​

  • Per key: 120 reads and 20 writes per minute (all routes together).
  • POST /client/v1/rdns/bulk: 5 calls per minute per key, 512 lines per call.
  • POST …/import: 5 calls per minute per key.
  • Hourly limit of DNS and rDNS changes for the account, shared with the client area: 429 err_rate once reached.

Errors​

Errors use the {"error": "<message>", "code": "<code>"} format. Business refusals carry a message in French, meant to be read as is.

HTTPcodeCause
400bad_requestMissing, extra or wrongly typed parameter; unreadable IP address or domain name; invalid record_id
400rec_err_*, zone_err_*Record or zone refused (type not allowed, invalid name or value, TTL out of range, CNAME conflict, protected record, unreadable import…)
400rdns_err_hostname, rdns_err_nozone, rdns_err_forward, rdns_bulk_err_*Invalid host name, reverse zone not managed, name that does not resolve to the IP address, invalid rDNS batch
400err_presetUnknown template
403zone_err_readonlyZone hosted on cPanel: read-only
403rdns_err_ownerIP address that this account cannot change
403missing_scopeMissing permission (required_scope tells which one)
404not_foundDomain, IP address, service or record unknown, outside the key restriction or belonging to another account
413too_largeRequest body too large
429err_rateHourly change limit reached (or rDNS batch too large for what is left)
502err_backend, err_internalDNS service failing or unreachable: try again later
503dns_unavailableDNS management temporarily unavailable through the API