PDNS Manager

Panel API

Anything the web UI can do, a script can do too. The manager offers panel API tokens that act like a normal user – including audit, webhooks and multi-server fan-out.

Endpoint base

  • Every endpoint starts with /api/v1.
  • Responses and bodies are JSON.
  • Optional: interactive docs under /docs, if DOCS_ENABLED=true in .env.

Authentication

1 · Panel token (recommended for scripts)

As the desired user in the panel: Settings → API & Security → Tokens → New token. The token has prefix dnsmgr_usr_ and is shown once in plain text.

curl -H "Authorization: Bearer dnsmgr_usr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
     https://pdns.example.com/api/v1/auth/me

2 · Classic JWT (e.g. after interactive login)

curl -c cookies.txt -X POST "https://pdns.example.com/api/v1/auth/login" \
     -F "username=admin" -F "password=secret"

curl -b cookies.txt "https://pdns.example.com/api/v1/auth/me"

Cookie name is dns_manager_token, HttpOnly + (with HTTPS) Secure.

3 · ACME token

Restricted to _acme-challenge.* in allowed zones, see ACME docs.

Important endpoints

Servers

# List all registered PowerDNS servers + reachability
GET /api/v1/servers

# Single server detail + statistics
GET /api/v1/servers/master-fra1
GET /api/v1/servers/master-fra1/statistics

Zones

# Zones on a server
GET /api/v1/zones/master-fra1

# Detail (incl. rrsets)
GET /api/v1/zones/master-fra1/example.com./detail

# Create
POST /api/v1/zones
{
  "name": "example.com",
  "kind": "Native",
  "nameservers": ["ns1.example.com.", "ns2.example.com."],
  "soa_edit_api": "DEFAULT"
}

# Import with diff preview
POST /api/v1/zones/import/preview
POST /api/v1/zones/import

# Trigger NOTIFY
POST /api/v1/zones/master-fra1/example.com./notify

Records

# List
GET /api/v1/records/master-fra1/example.com.

# Create / upsert RRSet
POST /api/v1/records/master-fra1/example.com.
{
  "name": "www.example.com.",
  "type": "A",
  "ttl": 300,
  "records": [{ "content": "203.0.113.10", "disabled": false }]
}

# Bulk
POST /api/v1/records/master-fra1/example.com./bulk
{
  "create": [ ... ],
  "delete": [ ... ]
}

DNSSEC

GET    /api/v1/dnssec/master-fra1/example.com./keys
GET    /api/v1/dnssec/master-fra1/example.com./keys/12345
POST   /api/v1/dnssec/master-fra1/example.com./enable
POST   /api/v1/dnssec/master-fra1/example.com./disable
GET    /api/v1/dnssec/master-fra1/example.com./ds

Audit

GET /api/v1/audit-log?action=DELETE&resource_type=RECORD&limit=50
GET /api/v1/audit-log/export?max_rows=10000

Settings

GET /api/v1/settings/app-info             # Public app info
GET    /api/v1/settings/servers
POST   /api/v1/settings/servers
PUT    /api/v1/settings/servers/{id}
DELETE /api/v1/settings/servers/{id}
POST   /api/v1/settings/servers/test
GET    /api/v1/settings/servers/{id}/api-key   # one-time reveal, audited

GET/PUT /api/v1/settings/smtp
POST    /api/v1/settings/smtp/test
POST    /api/v1/settings/smtp/test-email
GET/PUT /api/v1/settings/captcha
GET/PUT /api/v1/settings/welcome-email
POST    /api/v1/settings/welcome-email/test

Example: bulk-create 50 subdomains

for i in $(seq 1 50); do
  curl -s -X POST "https://pdns.example.com/api/v1/records/master-fra1/example.com." \
    -H "Authorization: Bearer dnsmgr_usr_xxxxx" \
    -H "Content-Type: application/json" \
    -d "{
      \"name\":\"app${i}.example.com.\",
      \"type\":\"A\",
      \"ttl\":300,
      \"records\":[{\"content\":\"203.0.113.${i}\"}]
    }"
done

Error responses

  • 200 / 201 – ok.
  • 400 – invalid body. Detail in detail.
  • 401 – missing or wrong token.
  • 403 – token valid, no permission (e.g. write to a read-only zone).
  • 404 – zone, record, server or user not found.
  • 429 – login rate limit (only on /auth/login).
  • 500 – internal error. Detail stays in the server log; the user gets a friendly message.

Rotate / revoke tokens

In the panel: Settings → API & Security → Tokens – delete button per row. Effective immediately.