ZoneDeck

API Documentation

The complete ZoneDeck REST API: zones & records, DDNS, ACME DNS-01, custom nameservers. Everything is scriptable with curl or delegable to an AI agent.

Quick start

Create an API token on the API Tokens page (the value is shown once), then replace dcp_… in the examples. For DDNS only, the DDNS Guide guide is enough.

curl -s https://dns.us.wr.rs/api/v1/zones \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

Authentication & scopes

Send Authorization: Bearer <token> on every request; requests and responses are JSON. Scopes are chosen at token creation; insufficient scope returns 403.

Isolation: a token only ever sees and manages the zones of the account that created it. Anyone else's zone answers 404 (as if it did not exist).

Token values are shown exactly once — save them immediately. If one leaks, revoke it on the API tokens page right away.

Scopes

ScopeDescription
zones:readList and inspect your own zones
zones:writeCreate and delete zones
records:readRead records and vanity-NS state
records:writeCreate/update/delete records, DDNS, vanity NS
dns01ACME DNS-01: publish/clear the _acme-challenge TXT
repairRun zone sync/repair
nodes:writeManage DNS nodes (admin accounts only)

Conventions

Success: {"ok":true,…}. Failure: {"ok":false,"error":"reason"} with HTTP 400 (bad input), 401 (unauthenticated), 403 (insufficient scope / admin required), 404 (zone missing or not yours). Zone names are lowercase. Every write is audit-logged (visible in your activity log). Paused zones are read-only; writes answer 403.

Endpoint overview

MethodPathScopeDescription
GET/api/v1/health—Platform & per-node health (no auth)
GET/api/v1/ddns/ip—Caller's public IP (no auth; for DDNS clients)
GET/api/v1/zoneszones:readList your zones; admin tokens may add ?all=1 for the whole platform (with owners)
POST/api/v1/zoneszones:writeCreate a zone owned by this account (multi-node sync takes minutes)
GET/api/v1/zones/{zone}zones:readZone health/status (primary/secondaries, SOA consistency)
POST/api/v1/zones/{zone}/repairrepairIdempotent sync/repair — call repeatedly until healthy
DELETE/api/v1/zones/{zone}zones:writeDelete the zone on all nodes (destructive)
GET/api/v1/zones/{zone}/recordsrecords:readList all records
POST/api/v1/zones/{zone}/recordsrecords:writeAdd a record
PUT/api/v1/zones/{zone}/recordsrecords:writeUpdate: body is {"old":{…exact existing…},"new":{…}}
DELETE/api/v1/zones/{zone}/recordsrecords:writeDelete: body is the exact record to remove
GET/api/v1/zones/{zone}/vanityrecords:readInspect vanity NS and glue state
POST/api/v1/zones/{zone}/vanityrecords:writeAdd vanity NS (hostnames array; all-or-nothing)
DELETE/api/v1/zones/{zone}/vanityrecords:writeRemove one vanity NS (unreferenced glue cleaned up)
POST/api/v1/ddnsrecords:writeDDNS upsert: afterwards the record exactly matches the request; returns action=created/updated/unchanged
POST/api/v1/acme/dns01dns01Publish the _acme-challenge TXT (TTL 60, idempotent; wildcards map to the base name)
DELETE/api/v1/acme/dns01dns01Clear the _acme-challenge TXT (optionally by value)
GET/api/v1/auditzones:read+adminAudit log (?limit=&user=&action= filters)
GET/POST/DELETE/api/v1/nodesnodes:write+adminNode registry (list/create/delete; new nodes are probed immediately)

{zone} is the lowercase zone name (e.g. example.com). Missing or invalid tokens return 401.

Zones

A zone is created as Primary on the primary node plus a Secondary on every registered secondary, with apex NS in place. healthy stays false until initial sync finishes.

Subdomains work too: create_zone accepts names like dev.example.com as standalone zones. Afterwards add NS records in the parent DNS to delegate — the parent zone stays untouched.

# 添加域名(创建后自动归属本令牌的账号,全节点同步需几分钟)
curl -s -X POST https://dns.us.wr.rs/api/v1/zones \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name":"example.com"}'

# 查看域名健康状态(主/从节点、SOA 序号是否一致)
curl -s https://dns.us.wr.rs/api/v1/zones/example.com \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

# 同步修复(幂等,可反复调用直到 healthy)
curl -s -X POST https://dns.us.wr.rs/api/v1/zones/example.com/repair \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

Records

Record names accept @ (apex), a short name (www), a FQDN (www.example.com) or a wildcard (*, *.sub). Minimum TTL is 30.

# 添加 A 记录(name 支持短名 www / 完整域名 / @ / 通配符 *)
curl -s -X POST https://dns.us.wr.rs/api/v1/zones/example.com/records \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name":"www","type":"A","value":"203.0.113.10","ttl":300}'

# 列出全部记录(name 为 @ 表示根域名)
curl -s https://dns.us.wr.rs/api/v1/zones/example.com/records \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

# 修改(old 必须与现有记录完全一致,等价删除+重建)
curl -s -X PUT https://dns.us.wr.rs/api/v1/zones/example.com/records \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"old":{"name":"www","type":"A","value":"203.0.113.10"},
       "new":{"name":"www","type":"A","value":"198.51.100.20"}}'

# 删除(按 name+type+值 精确匹配)
curl -s -X DELETE https://dns.us.wr.rs/api/v1/zones/example.com/records \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name":"www","type":"A","value":"203.0.113.10"}'

# 泛解析(通配符)
curl -s -X POST https://dns.us.wr.rs/api/v1/zones/example.com/records \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name":"*","type":"A","value":"203.0.113.10"}'

Required fields per type

TypeDescriptionFields
AIPv4 addressvalue
AAAAIPv6 addressvalue
CNAMETarget hostname (cannot coexist with A)value
NSTarget hostnamevalue
MXMail server hostname + priorityvalue + priority
TXTText content (paste raw — no extra quoting needed)value
SRVTarget hostname + priority/weight/portvalue + priority + weight + port
CAAValue + tag (issue/issuewild/iodef) + flagsvalue + tag + flags

Error messages name the exact invalid field (e.g. A requires a valid IPv4).

Protected records: SOA and the platform-managed apex NS (ns1.qn.cx / ns2.qn.cx) cannot be edited or deleted — writes answer 403. They are the skeleton of multi-node sync.

DDNS

One-call convergence for routers/scripts/agents: no read-then-delete-then-create — one call makes the record exactly match the request. Full client scripts on the DDNS Guide page.

# upsert:调用后该 (name,type) 恰好有一条此值记录;返回 action=created/updated/unchanged
curl -s -X POST https://dns.us.wr.rs/api/v1/ddns \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"zone":"example.com","name":"home","type":"A","value":"203.0.113.7","ttl":60}'

# 查询调用方公网 IP(免鉴权,供 DDNS 客户端取本机出口 IP)
curl -s https://dns.us.wr.rs/api/v1/ddns/ip

ACME DNS-01: wildcard certificates

Standard flow for issuing *.example.com with certbot in manual mode plus this API for the TXT record (no panel access needed):

# 1) 用 certbot manual 模式发起签发,拿到 TXT 挑战值
certbot certonly --manual --preferred-challenges dns \
  -d example.com -d "*.example.com"
# certbot 会显示:_acme-challenge.example.com → TXT 值

# 2) 把挑战值发布到 DNS(无需面板登录;泛域名自动落到 _acme-challenge.example.com;重复提交幂等)
curl -s -X POST https://dns.us.wr.rs/api/v1/acme/dns01 \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"domain":"*.example.com","value":"把certbot给的值粘在这里"}'

# 3) 确认已生效(平台 NS 直查,绕过本地缓存)
dig +short _acme-challenge.example.com @ns1.qn.cx

# 4) 回到 certbot 按回车完成验证;签发完成后清理 TXT
curl -s -X DELETE https://dns.us.wr.rs/api/v1/acme/dns01 \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"domain":"*.example.com"}'

The token only needs the dns01 scope. Works with acme.sh manual mode or any ACME client; clean the TXT up after issuing.

Vanity nameservers

Serve your zones from your own NS hostnames (e.g. ns1.example.com) instead of the platform defaults. In-zone hostnames get glue A records automatically; out-of-zone hostnames must point at the platform node IPs first (or pass skip_verify:true). The visual editor lives on the Custom NS page.

# 添加自定义 NS(区内主机名自动建 glue A;整体提交——任一主机名不合法则全部不生效)
curl -s -X POST https://dns.us.wr.rs/api/v1/zones/example.com/vanity \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"hostnames":["ns1.example.com","ns2.example.com"]}'

# 查看当前自定义 NS 与 glue 状态
curl -s https://dns.us.wr.rs/api/v1/zones/example.com/vanity \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

# 移除一个(顺带清掉无人引用的 glue)
curl -s -X DELETE https://dns.us.wr.rs/api/v1/zones/example.com/vanity \
  -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"ns2.example.com"}'

Admin endpoints

Audit log and node management require a token created by an admin account. New nodes are probed for reachability immediately and the result is returned.

curl -s "https://dns.us.wr.rs/api/v1/audit?limit=100" -H "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

Nodes: GET lists (token redacted), POST creates (fields: name, api_url, api_token, role=primary|secondary, ns_name, public_ip), DELETE /api/v1/nodes/{id} removes.

MCP (native AI clients)

A built-in MCP server (Streamable HTTP) lets Claude Desktop, Claude Code and other clients manage DNS natively — one config snippet, tools mirroring the REST API, same API Tokens, same account isolation and audit.

# Claude Code —— 一条命令接入
claude mcp add --transport http dns-control https://dns.us.wr.rs/mcp \
  --header "Authorization: Bearer dcp_xxxxxxxxxxxxxxxx"

// Claude Desktop / 任意支持 Streamable HTTP 的 MCP 客户端
{
  "mcpServers": {
    "dns-control": {
      "type": "http",
      "url": "https://dns.us.wr.rs/mcp",
      "headers": { "Authorization": "Bearer dcp_xxxxxxxxxxxxxxxx" }
    }
  }
}

Tools: list_zones, create_zone (any subdomain), zone_status, delete_zone, list_records, create/update/delete_record, upsert_record (DDNS), acme_set_txt / acme_clear_txt, get/add/remove_vanity, dns_health, my_ip.

Copy-paste prompt for AI agents

Send the whole block below — together with your token — to any AI agent (Claude, GPT, local tooling) and it will know how to manage your DNS:

You manage DNS through the ZoneDeck REST API (managed authoritative DNS).
An MCP server with the same tools is available at https://dns.us.wr.rs/mcp (Streamable HTTP,
same token) — prefer it when the client supports MCP.

Base URL: https://dns.us.wr.rs
Auth: every request needs the header `Authorization: Bearer <TOKEN>` — use the
API token the user provides (format dcp_...). All bodies are JSON.
Responses: success `{"ok":true,...}`; failure `{"ok":false,"error":"message"}` with
HTTP 400 (bad input), 401 (bad/missing token), 403 (missing scope or admin-only
endpoint), 404 (zone not found **or not owned by this token's account**).

ZONES
- GET    /api/v1/zones                      list owned zones (name, healthy, sync state)
- POST   /api/v1/zones                      {"name":"example.com"} create + claim (sync takes minutes)
- GET    /api/v1/zones/example.com          zone health/status
- POST   /api/v1/zones/example.com/repair   idempotent sync/repair
- DELETE /api/v1/zones/example.com          delete on all nodes (destructive — confirm first)

RECORDS  (name accepts "@", "www", FQDN, or "*" wildcard)
- GET    /api/v1/zones/example.com/records
- POST   /api/v1/zones/example.com/records   {"name":"www","type":"A","value":"203.0.113.10","ttl":300}
- PUT    /api/v1/zones/example.com/records   {"old":{...exact existing...},"new":{...}}
- DELETE /api/v1/zones/example.com/records   {"name":"www","type":"A","value":"203.0.113.10"}
Types: A (IPv4), AAAA (IPv6), CNAME (hostname), NS (hostname),
MX (value + priority), TXT (raw text, no extra quotes),
SRV (value=target + priority + weight + port), CAA (value + tag + flags).

DDNS (upsert; A/AAAA/CNAME/TXT only)
- POST /api/v1/ddns    {"zone":"example.com","name":"home","type":"A","value":"203.0.113.7","ttl":60}
  -> {"ok":true,"action":"created|updated|unchanged",...}; zone may be omitted (auto-resolved from name)
- GET  /api/v1/ddns/ip -> the caller's public IP (no auth needed)

ACME DNS-01 (wildcard certificates)
- POST   /api/v1/acme/dns01  {"domain":"*.example.com","value":"<challenge>"} publish TXT (TTL 60, idempotent)
- DELETE /api/v1/acme/dns01  {"domain":"*.example.com"} clear it afterwards

CUSTOM NAMESERVERS
- GET  /api/v1/zones/example.com/vanity
- POST /api/v1/zones/example.com/vanity  {"hostnames":["ns1.example.com","ns2.example.com"]}
- DELETE /api/v1/zones/example.com/vanity {"hostname":"ns2.example.com"}

RULES
- The token sees ONLY the zones of the account that created it; other zones answer 404.
- TTL must be >= 30. Platform apex NS records and SOA are protected (403 if you try).
- Never guess zone names — list first. Confirm with the user before deleting
  anything you did not create in this session.