Skip to content

API Endpoints

This page is a task-shaped map of the API, not the complete list. The API Reference is generated from the source and documents every operation with its full request and response schemas - use that when you need the authoritative answer, and this page when you want to find the right endpoint for a job.

A running instance can also serve the same specification interactively.

Interactive API documentation

When api.docs_enabled = true in config/settings.php, every Poweradmin instance hosts its own Swagger UI:

https://your-poweradmin-host/api/docs

From there you can:

  • Browse every endpoint grouped by tag (Zones, Records, Users, ...)
  • See the exact JSON request and response schemas
  • Paste in an API key via the Authorize button
  • Issue real requests against the running instance and inspect the response

For production deployments, leave docs_enabled off and use a staging instance for exploration.

High-level endpoint map (API v2)

API v2 is the recommended version. All paths are prefixed with /api/v2.

Zones

Method Path Purpose
GET /zones List zones
POST /zones Create zone
GET /zones/{id} Get zone
PUT /zones/{id} Update zone
DELETE /zones/{id} Delete zone
GET /zones/{id}/owners List zone owners (v4.2.0+)
POST /zones/{id}/owners Add owner(s), supports batch (v4.2.0+)
DELETE /zones/{id}/owners/{user_id} Remove owner (v4.2.0+)

Setting the serial policy on create (v4.5.0+)

POST /zones accepts an optional soa_edit_api string that sets the zone's SOA-EDIT-API serial policy:

{
  "name": "example.com",
  "type": "MASTER",
  "soa_edit_api": "INCREASE"
}

Omit the field, or send an empty string, to apply the dns.soa_edit_api configuration default. Send OFF to disable the policy for this zone specifically; in API backend mode that also clears the default PowerDNS applies.

The accepted values are DEFAULT, INCREASE, EPOCH, SOA-EDIT, SOA-EDIT-INCREASE and OFF, narrowed to whatever the dns.soa_edit_api_options configuration list allows. A value outside that set returns 400, and so does a non-string value. This differs deliberately from the add-zone form, which silently drops an unoffered value and falls back to the default.

Two limits worth knowing before scripting against it. The field is create-only: PUT /zones/{id} does not accept it, and an existing zone's policy is changed through PUT /zones/{id}/metadata/SOA-EDIT-API instead. And a SLAVE zone takes its serial from the primary, so a value accepted there is validated but never applied.

Zone metadata and DNSSEC

Method Path Purpose
GET /zones/{id}/metadata List all metadata kinds for a zone (v4.3.0+)
GET /zones/{id}/metadata/{kind} Get one metadata kind (v4.3.0+)
PUT /zones/{id}/metadata/{kind} Set one metadata kind (v4.3.0+)
DELETE /zones/{id}/metadata/{kind} Remove one metadata kind (v4.3.0+)
GET /zones/{id}/dnssec Get DNSSEC status and keys (v4.5.0+)
POST /zones/{id}/dnssec Sign or unsign the zone (v4.5.0+)

Dynamic DNS

Method Path Purpose
POST /dynamic-dns Update a record from a dynamic DNS client (v4.5.0+)

This is the API v2 equivalent of the standalone dynamic_update.php script described in Dynamic DNS. Both remain supported; the standalone script keeps the dyndns2-compatible query-string interface that routers expect.

Records

Method Path Purpose
GET /zones/{id}/records List records in a zone
POST /zones/{id}/records Create record
GET /zones/{id}/records/{recordId} Fetch a single record
PUT /zones/{id}/records/{recordId} Update record
DELETE /zones/{id}/records/{recordId} Delete record
POST /zones/{id}/records/bulk Bulk create records
GET /zones/{id}/rrsets List RRsets
GET /zones/{id}/rrsets/{name}/{type} Get a specific RRset

When ttl is omitted on a record create call, Poweradmin applies the configured default (dns.ttl_reverse for PTR records in reverse zones when set, dns.ttl otherwise). See DNS settings for details.

Records in Secondary and Consumer zones are read-only - they replicate from a primary. Create, update, delete, and bulk-write calls against such a zone return 403 with a message stating the zone is read-only (rather than a permission error). Edit the records on the primary instead.

Users

Method Path Purpose
GET /users List users
POST /users Create user
GET /users/{id} Get user
PUT /users/{id} Update user
DELETE /users/{id} Delete user

Writing perm_templ is gated separately from the write itself, and a rejected assignment returns 403:

  • Callers holding user_is_ueberuser may assign any template.
  • Callers holding user_edit_templ_perm may assign any template that does not grant user_is_ueberuser, and may not change their own account's template unless they also hold user_edit_others.
  • Everyone else may not send perm_templ at all.

Omitting perm_templ on create assigns the least-privileged non-superuser template, whoever the caller is; it no longer falls back to template id 1. If no such template exists, the request fails rather than granting administrator rights.

Permission template and groups on reads (v4.5.0+)

User reads carry the assigned permission template by ID and by name, plus the groups the user belongs to:

{
  "user_id": 5,
  "username": "svc-acme",
  "perm_templ": 2,
  "perm_templ_name": "Zone Manager",
  "groups": [{ "id": 3, "name": "dns-operators" }]
}

perm_templ_name is null when the stored template no longer exists or is a group template rather than a user template.

Assigning groups on create (v4.5.0+)

POST /users accepts an optional groups array holding integer group IDs, exact group names, or a mix of both:

{
  "username": "svc-acme",
  "password": "...",
  "perm_templ": 2,
  "groups": [3, "dns-operators"]
}

The response lists the memberships that were actually created, so a caller does not have to assume the request took full effect:

{ "user_id": 5, "groups": [{ "id": 3, "name": "dns-operators" }] }

Rules worth knowing before you script against it:

  • Sending groups requires user_is_ueberuser, because a group carries its own permission template. Callers without it get 403, even when they may otherwise create users.
  • Names are matched exactly, including case and accents. "Viewers" resolves, "viewers" does not. This is deliberate: MySQL's default collation would otherwise accept variants that PostgreSQL and SQLite reject.
  • Numeric strings are treated as names, not IDs. Send 3 to reference group 3; "3" is looked up as a group named 3 and will normally fail.
  • If any entry does not resolve to a group, the whole request returns 400 and no user is created.
  • Naming the same group more than once, whether by ID and by name or by repeating one value, still creates a single membership.
  • The array may hold up to 50 groups. A longer list returns 400 and creates nothing.

Membership changes after creation go through POST/DELETE /groups/{id}/members; PUT /users/{id} does not accept groups.

Groups (v4.2.0+)

Method Path Purpose
GET /groups List groups
POST /groups Create group
GET /groups/{id} Get group
PUT /groups/{id} Update group
DELETE /groups/{id} Delete group
GET /groups/{id}/members List group members
POST /groups/{id}/members Add member
DELETE /groups/{id}/members/{user_id} Remove member
GET /groups/{id}/zones List zones assigned to group
POST /groups/{id}/zones Assign zone to group
DELETE /groups/{id}/zones/{zone_id} Remove zone from group

Permission templates

Method Path Purpose
GET /permission-templates List templates
POST /permission-templates Create template
GET /permission-templates/{id} Get template
PUT /permission-templates/{id} Update template
DELETE /permission-templates/{id} Delete template
GET /permissions List available permission flags
GET /permissions/{id} Get permission flag details

Zone templates (v4.2.0+)

Method Path Purpose
GET /zone-templates List zone templates
POST /zone-templates Create zone template
GET /zone-templates/{id} Get zone template
PUT /zone-templates/{id} Update zone template
DELETE /zone-templates/{id} Delete zone template
GET /zone-templates/{id}/records List template records
POST /zone-templates/{id}/records Add template record
PUT /zone-templates/{template_id}/records/{id} Update template record
DELETE /zone-templates/{template_id}/records/{id} Delete template record

Health endpoints (v4.5.0+)

These sit outside the versioned API and outside the API key model. They take no credentials, are disabled by default, and return 404 while disabled. Paths are absolute, not relative to /api/v2.

Method Path Purpose
GET /api/health Readiness: database and PowerDNS API reachability. 200 healthy, 503 otherwise
GET /ping Liveness: returns ok and checks nothing else

They are not part of the OpenAPI specification, and they answer regardless of whether api.enabled is set. See Health Checks.

API v1 endpoints

Removed in 4.5.0. On 4.5.0 and later, every /api/v1 path answers 410 Gone and points at /api/v2.

On 4.2.x-4.4.x, v1 is still available under /api/v1. The endpoint surface is similar but the response envelope is less consistent and several v2 features (RRsets, bulk records, zone owners, groups, zone templates) are not available. See API Configuration for the v1 endpoint list, and migrate to v2 before upgrading to 4.5.0.

Pagination

List endpoints for zones and users accept page and per_page query parameters:

GET /api/v2/zones?page=1&per_page=50

Omitting per_page returns all results. page is only read when per_page is given, and per_page is capped at 10000.

Warning: The parameter is per_page, not limit. An unrecognised parameter is ignored rather than rejected, so ?limit=50 silently returns every row.

Paginated responses include a pagination block alongside data:

{
  "pagination": { "current_page": 1, "per_page": 50, "total": 150, "last_page": 3 }
}