API Spec
This API runs over HTTP, preferably HTTPS.
Design Goals
- Discovery endpoint
- Unified API Scheme for Daemons & Console. Think of the Console Server as a proxy for all your PowerDNS deployments.
- Have API documentation (this!) for other consumers
Data format
Input data format: JSON.
Output data formats: JSON.
The Accept: header determines the output format. An unknown value or
*/* will cause a 400 Bad Request.
All text is UTF-8 and HTTP headers will reflect this.
Data types:
- empty fields:
nullbut present - Regex: implementation defined
- Dates: ISO 8601
REST
- GET: List/Retrieve. Success reply:
200 OK - POST: Create. Success reply:
201 Created, with new object as body. - PUT: Update. Success reply:
200 OK, with modified object as body. For some operations,204 No Contentis returned instead (and the modified object is not given in the body). - DELETE: Delete. Success reply:
200 OK, no body.
not-so-REST
For interactions that do not directly map onto CRUD, we use these:
- GET: Query. Success reply:
200 OK - PUT: Action/Execute. Success reply:
200 OK
Action/Execute methods return a JSON body of this format:
{
"message": "result message"
}
Authentication
The PowerDNS daemons accept a static API Key, configured with the
api-key
option, which has to be sent in the X-API-Key header.
Note: Authoritative Server 3.4.0 and Recursor 3.6.0 and 3.6.1 use HTTP Basic Authentication instead.
Errors
Response code 4xx or 5xx, depending on the situation. Never return 2xx
for an error!
- Invalid JSON body from client:
400 Bad Request - JSON body from client not a hash:
400 Bad Request - Input validation failed:
422 Unprocessable Entity
Error responses have a JSON body of this format:
{
"error": "short error message",
"errors": [
{ ... },
]
}
Where errors is optional, and the contents are error-specific.
Common Error Causes
400 Bad Request
- The client body was not a JSON document, or it could not be parsed, or the root element of the JSON document was not a hash.
- The client did not send an
Accept:header, or it was set to*/*. - For requests that operate on a zone, the
zone_idURL part was invalid. To get a validzone_id, list the zones with the/api/v1/servers/:server_id/zonesendpoint.
URL: /api
Version discovery endpoint.
Allowed methods: GET
[
{
"url": "/api/v1",
"version": 1
}
]
URL: /api/v1
Allowed methods: GET
{
"server_url": "/api/v1/servers{/server}",
"api_features": []
}
TODO:
- Not yet implemented.
api_featuresservers_modifiableoauth
General Collections Interface
Collections generally support GET and POST with these meanings:
GET
Retrieve a list of all entries.
The special type and url fields are included in the response objects:
type: name of the resource typeurl: url to the object
Response format:
[
obj1
[, further objs]
]
Example:
[
{
"type": "AType",
"id": "anid",
"url": "/atype/anid",
"a_field": "a_value"
},
{
"type": "AType",
"id": "anotherid",
"url": "/atype/anotherid",
"a_field": "another_value"
}
]
POST
Create a new entry. The client has to supply the entry in the request body,
in JSON format. application/x-www-form-urlencoded data MUST NOT be sent.
Clients SHOULD not send the 'url' field.
Client body:
obj1
Example:
{
"type": "AType",
"id": "anewid",
"a_field": "anew_value"
}
Servers
TODO: further routes
server_resource
Example with server "localhost", which is the only server returned by
pdns_server or pdns_recursor.
pdnsmgrd and pdnscontrol MUST NOT return “localhost”, but SHOULD return other servers.
{
"type": "Server",
"id": "localhost",
"url": "/api/v1/servers/localhost",
"daemon_type": "recursor",
"version": "VERSION",
"config_url": "/api/v1/servers/localhost/config{/config_setting}",
"zones_url": "/api/v1/servers/localhost/zones{/zone}",
}
Note: On a pdns_server or pdns_recursor, the servers collection is read-only, and the only allowed returned server is read-only as well. On a pdnscontrol server, the servers collection is read-write, and the returned server resources are read-write as well. Write permissions may depend on the credentials you have supplied.
- daemon_type
May be one of
authoritative,recursor.
URL: /api/v1/servers
Collection access.
Allowed REST methods:
- pdns_server:
GET - pdns_recursor:
GET - pdnsmgrd:
GET - pdnscontrol:
GET,PUT,POST,DELETE
URL: /api/v1/servers/:server_id
Returns a single server_resource.
Config
config_setting_resource
{
"type": "ConfigSetting",
"name": "config_setting_name",
"value": "config_setting_value"
}
URL: /api/v1/servers/:server_id/config
Collection access.
Allowed REST methods: GET, POST
POST
Creates a new config setting. This is useful for creating configuration for new backends.
TODO: Not yet implemented.
URL: /api/v1/servers/:server_id/config/:config_setting_name
Allowed REST methods: GET, PUT
NOTE: only the Recursors allow_from configuration setting can be retrieved or modified.
Zones
Authoritative DNS Zones.
A Resource Record Set (below as "RRset") are all records for a given name and type.
Comments are per-RRset.
zone_collection
{
"id": "<id>",
"name": "<string>",
"type": "Zone",
"url": "/api/v1/servers/:server_id/zones/:id",
"kind": "<kind>",
"serial": <int>,
"notified_serial": <int>,
"masters": ["<ip>", ...],
"dnssec": <bool>,
"nsec3param": "<nsec3param record>",
"nsec3narrow": <bool>,
"presigned": <bool>,
"soa_edit": "<string>",
"soa_edit_api": "<string>",
"account": "<string>",
"nameservers": ["<string>", ...],
"servers": ["<string>", ...],
"recursion_desired": <bool>,
"rrsets": [<RRset>, ...],
}
Where RRset is defined as:
{
"name": "<string>",
"type": "<type>",
"ttl": <int>,
"records": [<Record>, ...],
"comments": [<Comment>, ...]
}
Where Record is defined as:
{
"content": "<string>",
"disabled": <bool>
}
Where Comment is defined as:
{
"content": "<string>",
"account": "<string>",
"modified_at": <int>
}
Parameters:
-
idOpaque zone id (string), assigned by the Server. Do not interpret. Guaranteed to be safe for embedding in URLs. -
nameZone name, always including the trailing dot. Example:example.org.Note: Before 4.0.0, zone names were taken/given without the trailing dot. -
kindAuthoritative:<kind>:Native,MasterorSlaveRecursor:<kind>:Native, orForwarded -
dnssecinferred frompresignedbeingtrueXOR presence of at least one cryptokey withactivebeingtrue.
Switching dnssec to true (from false) sets up DNSSEC signing
based on the other flags, this includes running the equivalent of
secure-zone and rectify-zone. This also applies to newly created
zones.
If presigned is true, no DNSSEC changes will be made to the zone
or cryptokeys.
Note: Authoritative only.
TODO: dnssec, nsec3narrow, nsec3param, presigned are not yet implemented.
-
soa_editMAY be set to change theSOA-EDITzone setting. See theSOA-EDITdocumentation for more information. Note: Authoritative only. -
soa_edit_apiMAY be set. If it is set, on changes to the contents of a zone made through the API, the SOA record will be edited according to the SOA-EDIT-API rules. (Which are the same as the SOA-EDIT-DNSUPDATE rules.) If not set during zone creation, a SOA-EDIT-API metadata record is created and set toDEFAULT. (If this record is removed from the backend, the default behaviour is to not do any SOA editing based on this setting. This is different from settingDEFAULT.) Note: Authoritative only. -
accountMAY be set. Its value is defined by local policy. Note: Authoritative only. -
notified_serial,serialMUST NOT be sent in client bodies. Note: Authoritative only. -
nameserversMAY be sent in client bodies during creation, and MUST NOT be sent by the server. Simple list of strings of nameserver names, including the trailing dot. Note: Before 4.0.0, names were taken without the trailing dot. Note: Authoritative only. Not required for slave zones. -
servers: list of forwarded-to servers, including port. Note: Recursor only. -
recursion_desired: forForwardedzones, if the RD bit should be set. Note: Authoritative only. -
rrsets: list of DNS records and comments in the zone. Note: Modifications are supported on Authoritative only.
Please see the description for PATCH for details on the fields in
RRset, Record and Comment.
Notes:
Turning on DNSSEC with custom keys: just create the zone with dnssec
set to false, and add keys using the cryptokeys REST interface. Have
at least one of them active set to true. TODO: not yet
implemented.
Changes made through the Zones API will always yield valid zone data, and the zone will be properly "rectified" (TODO: not yet implemented). If changes are made through other means (e.g. direct database access), this is not guaranteed to be true and clients SHOULD trigger rectify.
Backends might implement additional features (by coincidence or not). These things are not supported through the API.
When creating a slave zone, it is recommended to not set any of
nameservers, records.
URL: /api/v1/servers/:server_id/zones
Allowed REST methods: GET, POST
POST
Creates a new domain.
dnssec,nsec3narrow,presigned,nsec3param,active-keysare OPTIONAL.dnssec,nsec3narrow,presigneddefault tofalse.- The server MUST create a SOA record. The created SOA record SHOULD have
serial set to the value given as
serial(or 0 if missing), use the nameserver name, email, TTL values as specified in the PowerDNS configuration (default-soa-name,default-soa-mail, etc). These default values can be overridden by supplying a custom SOA record in the records list. Ifsoa_edit_apiis set, the SOA record is edited according to the SOA-EDIT-API rules before storing it. (Also applies to custom SOA records.)
TODO: dnssec, nsec3narrow, nsec3param, presigned are not yet implemented.
URL: /api/v1/servers/:server_id/zones/:zone_id
Allowed methods: GET, PUT, DELETE, PATCH.
GET
Returns zone information.
DELETE
Deletes this zone, all attached metadata and rrsets.
PATCH
Modifies present RRsets and comments.
Returns 204 No Content on success.
Note: Authoritative only.
Client body for PATCH:
{ "rrsets":
[
{
"name": <string>,
"type": <string>,
"ttl": <int>,
"changetype": <changetype>,
"records":
[
{
"content": <string>,
"disabled": <bool>,
"set-ptr": <bool>
}, ...
],
"comments":
[
{
"account": <string>,
"content": <string>,
"modified_at": <int>
}, ...
]
},
{ ... }
]
}
-
nameFull name of the RRset to modify. (Example:foo.example.org.) -
typeType of the RRset to modify. (Example:AAAA) -
ttlDNS TTL to apply to records replaced, in seconds. MUST NOT be included whenchangetypeis set toDELETE. -
changetypeMust beREPLACEorDELETE. WithDELETE, all existing RRs matchingnameandtypewill be deleted, including all comments. WithREPLACE: whenrecordsis present, all existing RRs matchingnameandtypewill be deleted, and then new records given inrecordswill be created. If no records are left, any existing comments will be deleted as well. Whencommentsis present, all existing comments for the RRs matchingnameandtypewill be deleted, and then new comments given incommentswill be created. -
recordsList of new records (replacing the old ones). Must be empty whenchangetypeis set toDELETE. An empty list results in deletion of all records (and comments). A record consists of these fields: content: the record content. Must confirm to the DNS content rules for the specifiedtype. (PowerDNS hint: includes the backend'spriorityfield.)disabled: if this record will be hidden from DNS. (true: hidden, false: visible (the default)).-
set-ptr: If set to true, the server will find the matching reverse zone and create aPTRthere. ExistingPTRrecords are replaced. If no matching reverse Zone, an error is thrown. Only valid in client bodies, only valid forAandAAAAtypes. Not returned by the server. Only valid for the Authoritative server. -
commentsList of new comments (replacing the old ones). Must be empty whenchangetypeis set toDELETE. An empty list results in deletion of all comments.modified_atis optional and defaults to the current server time.accountis a field with user-defined meaning.
PUT
Modifies basic zone data (metadata).
Allowed fields in client body: all except id and url.
Returns 204 No Content on success.
Changing name renames the zone, as expected.
URL: /api/v1/servers/:server_id/zones/:zone_id/notify
Allowed methods: PUT
Send a DNS NOTIFY to all slaves.
Fails when zone kind is not Master or Slave, or master and slave are
disabled in pdns configuration. Only works for Slave if renotify is on.
Not supported for recursors.
Clients MUST NOT send a body.
URL: /api/v1/servers/:server_id/zones/:zone_id/axfr-retrieve
Allowed methods: PUT
Retrieves the zone from the master.
Fails when zone kind is not Slave, or slave is disabled in PowerDNS.
configuration.
Not supported for recursors.
Note: Added in 3.4.2
URL: /api/v1/servers/:server_id/zones/:zone_id/export
Allowed methods: GET
Returns the zone in AXFR format.
Not supported for recursors.
URL: /api/v1/servers/:server_id/zones/:zone_id/check
Allowed methods: GET
Verify zone contents/configuration.
Return format:
{
"zone": "<zone_name>",
"errors": ["error message1", ...],
"warnings": ["warning message1", ...]
}
TODO: Not yet implemented.
Zone Metadata
Note: Available since PowerDNS Authoritative Server 4.1.0.
zone_metadata_resource
{
"type": "Metadata",
"kind": <metadata_kind>,
"metadata": [
"value1",
...
]
}
Parameters:
kind: valid values for <metadata_kind> are specified in
the domainmetadata documentation.
metadata: an array with all values for this metadata kind.
Clients MUST NOT modify NSEC3PARAM, NSEC3NARROW, PRESIGNED and
LUA-AXFR-SCRIPT through this interface. The server rejects updates to
these metadata. Modifications to custom metadata kinds starting with X- is allowed as well.
URL: /api/v1/servers/:server_id/zones/:zone_name/metadata
Collection access.
Allowed methods: GET, POST
GET
Returns all metadata entries for the zone.
POST
Creates a set of metadata entries of given kind for the zone.
- existing metadata entries for the zone with the same kind are not overwritten.
URL: /api/v1/servers/:server_id/zones/:zone_name/metadata/:metadata_kind
Allowed methods: GET, PUT, DELETE
GET
Returns all metadata entries of a given kind for the zone.
DELETE
Deletes all metadata entries of a given kind for the zone.
PUT
Modifies the metadata entries of a given kind for the zone.
This returns 200 OK on success.
Cryptokeys
cryptokey_resource
{
"type": "Cryptokey",
"id": <int>,
"active": <bool>,
"keytype": <keytype>,
"dnskey": <string>,
"privatekey": <string>,
"ds": [ <ds>,
<ds>,
.... ]
}
Parameters:
id: read-only.
keytype: <keytype> is one of the following: ksk, zsk, csk.
dnskey: the DNSKEY for this key
ds: an array with all DSes for this key
privatekey: private key data (in ISC format).
URL: /api/v1/servers/:server_id/zones/:zone_name/cryptokeys
Allowed methods: GET, POST
GET
Returns all public data about cryptokeys, but not privatekey.
POST
This method adds a new key to a zone. The key can either be generated or imported by supplying the content parameter.
Parameters:
content: "\" <string>(The format used is compatible with BIND and NSD/LDNS)keytype: "ksk|zsk"<string>active: "true|false"<value>(If not set the key will not be active by default)
If content == null, the server generates a new key. In this case, the
following additional fields MAY be supplied:
bits: number of bits<int>algo:<algo>(Default: 13/ECDSA)
Where <algo> is one of the supported key algorithms in lowercase OR the
numeric id, see the list.
Response:
-
422 Unprocessable Entity:-
keytype is not ksk|zsk:
-
{"error" : "Invalid keytype 'keytype'"}- The "algo" is not supported:
-
{"error" : "Unknown algorithm: 'algo'"}- Algo <= 10 and the
bitsparameter is not set:
- Algo <= 10 and the
-
{"error" : "Creating an algorithm 'algo' key requires the size (in bits) to be passed."}- The provided bit size is not supported by the selected algorithm:
-
{"error" : "The algorithm does not support the given bit size."}- The
bitsparameter is not a positive integer value:
- The
-
{"error" : "'bits' must be a positive integer value"}- If the server can not guess the key size:
-
{"error" : "Can not guess key size for algorithm"}- The key-creation failed:
-
{"error" : "Adding key failed, perhaps DNSSEC not enabled in configuration?"}- The key in
contenthas the wrong format:
- The key in
-
{"error" : "Key could not be parsed. Make sure your key format is correct."}
-
-
-
201 Created:-
Everything was fine:
- Returns all public data about the new cryptokey. Look at cryptokey_resource.
-
URL: /api/v1/servers/:server_id/zones/:zone_name/cryptokeys/:cryptokey_id
Allowed methods: GET, PUT, DELETE
GET
Returns all public data about cryptokeys, including privatekey.
PUT
This method de/activates a key from zone_name specified by cryptokey_id.
Parameters:
active: "true|false"<value>
Responses:
204 No Content: The key withcryptokey_idis de/activated.422 Unprocessable Entity: The backend returns false on de/activation. An error occurred.{"error": "Could not de/activate Key: :cryptokey_id in Zone: :zone_name"}
DELETE
This method deletes a key from zone_name specified by cryptokey_id.
Responses:
200 OK: The Key is gone.422 Unprocessable Entity: The backend failed to remove the key.{"error": Could not DELETE :cryptokey_id"}
Data searching
URL: /api/v1/servers/localhost/search-data?q=:search_term&max=:max_results
Note: Authoritative only.
Allowed methods: GET
GET
Search the data inside PowerDNS for :search_term and return at most
:max_results. This includes zones, records and comments.
The * character can be used in :search_term as a wildcard character and the ? character can be used as a wildcard for a single character.
Response body is an array of one or more of the following objects:
For a zone:
{
"name": "<zonename>",
"object_type": "zone",
"zone_id": "<zoneid>"
}
For a record:
{
"content": "<content>",
"disabled": <bool>,
"name": "<name>",
"object_type": "record",
"ttl": <ttl>,
"type": "<type>",
"zone": "<zonename>,
"zone_id": "<zoneid>"
}
For a comment:
{
"object_type": "comment",
"name": "<name>",
"content": "<content>"
"zone": "<zonename>,
"zone_id": "<zoneid>"
}
Cache Access
TODO: Not yet implemented: Peek at the cache, clear the cache, possibly read cache?
URL: /api/v1/servers/:server_id/cache/flush?domain=:domain
Allowed methods: PUT (Execute)
PUT (Execute)
Flush the cache for a given domain name :domain. Response body:
{
"count": 10,
"result": "Flushed cache."
}
Implementation detail: On Authoritative servers, this clears the packet cache. On Recursors, this clears the positive, negative and packet cache.
Logging & Statistics
URL: /api/v1/servers/:server_id/search-log?q=:search_term
Allowed methods: GET (Query)
GET (Query)
Query the log, filtered by :search_term (query parameter). Response body:
[
"<log_line>",
...
]
URL: /api/v1/servers/:server_id/statistics
Allowed methods: GET (Query)
GET (Query)
Query PowerDNS internal statistics. Response body:
[
{
"type": "StatisticItem",
"name": "<name>",
"value": "<value>"
},
...
]
The statistic entries are dependent on the daemon type. Values are returned as strings.
URL: /api/v1/servers/:server_id/trace
TODO: Not yet implemented.
PUT (Configure)
Configure query tracing.
Client body:
{
"domains": "<regex_string>"
}
Set domains to null to turn off tracing.
GET (Query)
Retrieve query tracing log and current config. Response body:
{
"domains": "<Regex>",
log: [
"<log_line>",
...
]
}
URL: /api/v1/servers/:server_id/failures
TODO: Not yet implemented.
PUT
Configure query failure logging.
Client body:
{
"top-domains": <int>,
"domains": "<Regex>",
}
Parameters:
top-domains are the number of top resolved domains that are
automatically monitored for failures.
domains is a Regex of domains that are additionally monitored for
resolve failures.
GET
Retrieve query failure logging and current config.
Response body:
{
"top-domains": <int>,
"domains": "<Regex>",
"log": [
{
"first_occurred": <timestamp>,
"domain": "<full domain>",
"qtype": "<qtype>",
"failure": <failure_code>,
"failed_parent": "<full parent domain>",
"details": "<log message>",
"queried_servers": [
{
"name": <name>,
"address": <address>
}, ...
]
},
...
]
}
Parameters:
failed_parent is generally OPTIONAL.
Where <failure_code> is one of these:
-
dnssec-validation-failedDNSSEC Validation failed for this domain.
-
dnssec-parent-validation-failedDNSSEC Validation failed for one of the parent domains. Response MUST contain failed_parent.
-
nxdomainThis domain was not present on the authoritative nameservers.
-
nodata -
all-servers-unreachableAll auth nameservers that have been tried did not respond.
-
parent-unresolvableResponse MUST contain
failed_parent. -
refusedAll auth nameservers that have been tried responded with REFUSED.
-
servfailAll auth nameservers that have been tried responded with SERVFAIL.
-
TODO: further failures
Data Overrides
TODO: Not yet implemented.
override_type
created is filled by the Server.
{
"type": "Override",
"id": <int>,
"override": "ignore-dnssec",
"domain": "nl",
"until": <timestamp>,
"created": <timestamp>
}
{
"type": "Override",
"id": <int>,
"override": "replace",
"domain": "www.cnn.com.",
"rrtype": "AAAA",
"values": ["203.0.113.4", "203.0.113..2"],
"until": <timestamp>,
"created": <timestamp>
}
TODO: what about validation here?
{
"type": "Override",
"id": <int>,
"override": "purge",
"domain": "example.net.",
"created": <timestamp>
}
Clears recursively all cached data ("plain" DNS + DNSSEC)
TODO: should this be stored? (for history)
URL: /api/v1/servers/:server_id/overrides
TODO: Not yet implemented.
Collection access.
Allowed Methods: GET, POST
URL: /api/v1/servers/:server_id/overrides/:override_id
TODO: Not yet implemented.
Allowed methods: GET, PUT, DELETE