REST API Reference

Base URL: http://<pi-ip>/api. All endpoints except /api/health require Bearer authentication.

Authentication

Protected endpoints accept three auth methods:

Public endpoints (no auth): /api/health, /api/login, /api/setup/*, /dns-query (DoH).

bash
# Bearer token
curl -H "Authorization: Bearer <YOUR_API_KEY>" \
  http://<PI_IP>/api/zones

# Session login
curl -c cookies.txt -X POST http://<PI_IP>/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"<PASSWORD>"}'
curl -b cookies.txt http://<PI_IP>/api/zones

Retrieve your root API key from the Dashboard → Settings → Security. Named API keys can be created per tool — see Named API Keys.

Health

GET /api/health No auth required
json
{ "status": "ok" }

Zones

GET /api/zones List all zones
GET /api/zones/{domain} Get zone details
POST /api/zones Create zone
PUT /api/zones/{domain} Update zone
DELETE /api/zones/{domain} Delete zone

Create Zone — Example

bash
curl -X POST http://<PI_IP>/api/zones \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "home.lan",
    "ttl": 3600,
    "nameservers": ["ns1.home.lan"],
    "admin_email": "admin@home.lan"
  }'

Records

GET /api/zones/{domain}/records List records
POST /api/zones/{domain}/records Add record
PUT /api/zones/{domain}/records/{id} Update record
DELETE /api/zones/{domain}/records/{id} Delete record

Add A Record — Example

bash
curl -X POST http://<PI_IP>/api/zones/home.lan/records \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "nas",
    "type": "A",
    "value": "192.168.1.100",
    "ttl": 3600
  }'

Blocklist

Blocklist URLs

GET /api/blocklist/urls List blocklist URLs with status and last-fetch info
POST /api/blocklist/urls Add blocklist URL (max 100)
PATCH /api/blocklist/urls/{id} Enable/disable a blocklist URL — body: {"enabled": true}
POST /api/blocklist/urls/{id}/fetch Manually re-fetch a specific blocklist URL
DELETE /api/blocklist/urls/{id} Remove blocklist URL

Manual Domains

GET /api/blocklist/domains List manually blocked domains
POST /api/blocklist/domains Block a domain manually — body: {"domain": "ads.example.com"}
DELETE /api/blocklist/domains/{domain} Remove manually blocked domain

Allow-list (Bypass Blocklist)

GET /api/blocklist/allowed List whitelisted domains (bypass blocklist)
POST /api/blocklist/allowed Add domain to allow-list — body: {"domain": "example.com"}
DELETE /api/blocklist/allowed/{domain} Remove domain from allow-list

Patterns (Wildcard / Regex)

GET /api/blocklist/patterns List blocking patterns
POST /api/blocklist/patterns Add pattern — body: {"pattern": "*.ads.example.com", "type": "wildcard"} or "type": "regex"
DELETE /api/blocklist/patterns/{id} Remove pattern

Client IP Whitelist

GET /api/blocklist/whitelist-ips List client IPs/CIDRs that bypass the blocklist entirely
POST /api/blocklist/whitelist-ips Add IP/CIDR — body: {"ip_cidr": "192.168.1.0/24"}. Use "localhost" to add 127.0.0.1 + ::1.
DELETE /api/blocklist/whitelist-ips/{ip_cidr} Remove IP/CIDR (URL-encode the slash: 192.168.1.0%2F24)

Config (Live Reload)

Some configuration values can be updated without restarting:

GET /api/config Get current runtime config
PATCH /api/config Update live-reloadable config

Update Upstream DNS — Example

bash
curl -X PATCH http://<PI_IP>/api/config \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "dnsserver": {
      "upstream": ["1.1.1.1", "1.0.0.1"]
    }
  }'

Split-Horizon DNS

Serve different zone content to different client subnets. Requires dnsserver.split_horizon.enabled: true. All zone/record endpoints accept an optional ?view=<name> query parameter.

GET /api/split-horizon Get current split-horizon view configuration
PUT /api/split-horizon Update split-horizon views (live-reload, persisted to config)

Configure Views — Example

bash
curl -X PUT http://<PI_IP>/api/split-horizon \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "views": [
      {"name": "internal", "subnets": ["192.168.0.0/16", "10.0.0.0/8"]},
      {"name": "external", "subnets": ["0.0.0.0/0"]}
    ]
  }'

DDNS / RFC 2136

DomU DNS accepts RFC 2136 DNS UPDATE messages on UDP/TCP port 53, authenticated with TSIG keys. Manage keys via the REST API. DHCP servers (e.g. ISC DHCP, Kea) can register A and PTR records automatically.

GET /api/ddns/status DDNS operation stats (total updates, failures, last error)
GET /api/ddns/keys List TSIG keys (names and algorithms only — secrets not returned)
POST /api/ddns/keys Create TSIG key — secret shown only once at creation
DELETE /api/ddns/keys/{name} Delete TSIG key

Create TSIG Key — Example

bash
curl -X POST http://<PI_IP>/api/ddns/keys \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name": "dhcp-key", "algorithm": "hmac-sha256"}'
# Response: {"data":{"name":"dhcp-key","algorithm":"hmac-sha256","secret":"<BASE64 — shown once>"}}

Cluster

GET /api/cluster/status Cluster health & sync status
POST /api/cluster/sync Force full sync to all slaves

Zone Import / Export

Import zones from RFC 1035 zone files or via live AXFR transfer. Export zones as standard zone files.

POST /api/zones/import Import zone from RFC 1035 zone file (multipart/form-data). Auto-detects domain from SOA record. Optional ?view= for split-horizon zones.
POST /api/zones/import/axfr Import zone via live AXFR transfer from a running DNS server
GET /api/zones/{domain}/export Download zone as RFC 1035 zone file. Supports ?view= for split-horizon zones.

Import Zone File — Example

bash
curl -X POST http://<PI_IP>/api/zones/import \
  -H "Authorization: Bearer <KEY>" \
  -F "file=@home.lan.zone"

Import via AXFR — Example

bash
curl -X POST http://<PI_IP>/api/zones/import/axfr \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "master": "192.168.1.1:53",
    "domain": "home.lan"
  }'

Export Zone — Example

bash
curl -O -J \
  -H "Authorization: Bearer <KEY>" \
  "http://<PI_IP>/api/zones/home.lan/export"
# Saves: home.lan.zone

Named API Keys

Create per-tool API keys (Traefik, Certbot, acme.sh, Proxmox) without sharing the root key. Each key is shown only once at creation time. Named keys and the root key are interchangeable for Bearer and Basic Auth.

GET /api/auth/api-keys List all named API keys (names only, not raw values)
POST /api/auth/api-keys Create a new named API key — returns the raw key once
DELETE /api/auth/api-keys/{id} Revoke a named API key

Create Named API Key — Example

bash
curl -X POST http://<PI_IP>/api/auth/api-keys \
  -H "Authorization: Bearer <ROOT_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name": "traefik"}'
# Response: {"data":{"name":"traefik","key":"<RAW_KEY — shown only once>"}}

ACME / DNS-01

Traefik httpreq provider endpoints for automating Let's Encrypt DNS-01 challenges. DomU DNS stores _acme-challenge TXT records and serves them over DNS. Works with Traefik, Certbot (certbot-dns-domudns), and acme.sh/Proxmox.

POST /api/acme/httpreq/present Store a DNS-01 challenge TXT record (Traefik httpreq protocol)
POST /api/acme/httpreq/cleanup Remove a DNS-01 challenge TXT record after validation

Present DNS-01 Challenge — Example

bash
curl -X POST http://<PI_IP>/api/acme/httpreq/present \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "fqdn": "_acme-challenge.home.lan.",
    "value": "base64encodedtoken..."
  }'

Cache

Inspect and control the DNS cache. Flush and per-entry delete are local-only operations — they are not propagated to cluster slaves. TTLs in cached responses are decremented in real time (RFC 1035).

GET /api/cache Cache statistics (entries, hits, misses, hit rate) and up to 500 entries sorted by remaining TTL
DELETE /api/cache Flush all cache entries (local only; allowed on slave nodes)
DELETE /api/cache/{name}/{type} Remove a specific entry by FQDN and record type (e.g. example.com/A)

Get Cache Stats — Example

bash
curl -H "Authorization: Bearer <KEY>" http://<PI_IP>/api/cache
# Response:
{
  "data": {
    "entries": 42,
    "hits": 1204,
    "misses": 38,
    "hit_rate": 96.9,
    "entry_list": [
      { "name": "example.com.", "type": "A", "remaining_ttl": 12, "expires_at": 1742900000, "cached_at": 1742899700 },
      ...
    ]
  }
}

Delete Cache Entry — Example

bash
curl -X DELETE \
  -H "Authorization: Bearer <KEY>" \
  "http://<PI_IP>/api/cache/example.com/A"