End-user Zone API
A focused reference for managing DNS zones and records over HTTP. Every UI action is also a JSON API call — with side-by-side cURL and PHP examples for every endpoint, ready to drop into your shell scripts, deploy pipelines, or dashboards.
1.Generating an API token UI walkthrough
Every API call below carries an X-API-Key header. You generate that key once from the web UI and store it somewhere safe (a password manager, CI secret store, etc.) — the manager only displays it at creation time.
Step-by-step
- Open the web UI in your browser at
http://<your-host>:8084/and log in with your admin username and password. - In the left sidebar, expand System and click API Tokens. The page header reads “API Tokens — Named tokens for programmatic access.”
- Click the orange + New token button in the top right.
- In the modal that appears, type a descriptive name in Token name (e.g.
deploy-script,monitoring-bot,terraform). The name only identifies where the token is used — it has no effect on permissions. - Click Create token. The modal flips to show the generated token (a string that begins with
bsd_…) along with a Copy button. - Copy the token immediately and paste it into your secret store. Once you close the dialog the plaintext value is gone forever — only its SHA-256 hash is kept on the server.
- The new token now appears in the table on the API Tokens page, showing its Created timestamp, Last used timestamp (updates on every authenticated request), and an Active status chip.
API_KEY value written into /etc/backendside-dns-manager/config.env at install time also works as an authentication header. Named tokens are preferred because they can be revoked individually and their last-used timestamp is tracked, but the static key remains as a bootstrap option.
Using your token
Send the token on every request as an X-API-Key HTTP header. That single header is the only authentication step for any of the endpoints documented below.
# Quick test: list zones
curl -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/zones
<?php
$host = 'http://your-host:8084';
$key = 'bsd_your_token_here';
$ch = curl_init("$host/api/v1/zones");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Accept: application/json",
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("API error $status: $body");
}
$zones = json_decode($body, true);
print_r($zones);
Conventions & errors
- All request and response bodies are
application/json. - Timestamps are ISO 8601 / RFC 3339, UTC.
- Successful responses use
200or201. Validation failures use400, missing items404, conflicts409, server errors500. - Errors return a JSON body:
{ "error": "...", "detail": "...", "code": "..." }(thecodefield is machine-readable — see common values below). - BIND reload warnings. Any mutation that touches the DNS server will return its normal success body plus an extra
"warning"string when the underlyingrndc reloadfails. The data write succeeded — DNS just hasn't picked it up. Surface the warning to your user; don't treat it as an error.
| Code | Meaning |
|---|---|
AUTH_REQUIRED | No X-API-Key header was sent. |
INVALID_API_KEY | The token is wrong, revoked, or unknown. |
RATE_LIMITED | Per-IP rate limit exceeded — slow down or whitelist your IP. |
ZONE_NOT_FOUND | The named zone is not managed by this server. |
ZONE_ALREADY_EXISTS | You tried to create a zone that already exists. |
ZONE_INVALID_NAME | The zone name failed validation. |
RECORD_NOT_FOUND | The record (or deleted-record) ID doesn't exist in the zone. |
BIND_RELOAD_FAILED | The data write succeeded but the DNS reload didn't. |
2.Zones
A zone is a managed DNS zone (e.g. example.com). The zone name is also its ID in URLs.
GET /api/v1/zones
List zones with optional pagination and substring search.
| Query param | Default | Description |
|---|---|---|
page | 1 | 1-based page number. |
limit | 50 | Results per page (max 500). |
q | — | Substring match on the zone name. |
Response 200:
{
"data": [
{ "id": "example.com", "name": "example.com",
"type": "master", "status": "active", "dnssec": false }
],
"meta": { "page": 1, "limit": 50, "total": 340 }
}
curl -H "X-API-Key: $KEY" \
"http://$HOST:8084/api/v1/zones?page=1&limit=50&q=example"
$ch = curl_init("$host/api/v1/zones?page=1&limit=50&q=example");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($resp['data'] as $z) {
echo $z['name'] . PHP_EOL;
}
POST /api/v1/zones
Create a new zone. Generates a fresh zone file with an SOA record and a default NS placeholder if none is supplied.
Request body:
{
"name": "example.com",
"type": "master",
"description": "Marketing site",
"nameserver": "ns1.example.com",
"admin_email": "[email protected]",
"default_ttl": 3600
}
Only name is required. type defaults to "master" and default_ttl defaults to 3600.
Validation rules (the server returns 400 on violation):
- Lowercase letters, digits,
-,_only. - Each label is 1–63 characters; the full name must not exceed 253.
- Must contain at least one dot.
- No leading or trailing hyphen on any label.
Response 201: the created zone object (or with "warning" on a reload failure).
curl -X POST http://$HOST:8084/api/v1/zones \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"name":"example.com",
"nameserver":"ns1.example.com",
"admin_email":"[email protected]",
"default_ttl":3600
}'
$payload = json_encode([
'name' => 'example.com',
'nameserver' => 'ns1.example.com',
'admin_email' => '[email protected]',
'default_ttl' => 3600,
]);
$ch = curl_init("$host/api/v1/zones");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 201) {
throw new RuntimeException("Create failed: $resp");
}
$zone = json_decode($resp, true)['zone'];
GET /api/v1/zones/:id
Fetch a single zone with all its records.
Response 200:
{
"id": "example.com", "name": "example.com",
"type": "master", "status": "active",
"nameserver": "ns1.example.com.",
"admin_email": "hostmaster.example.com.",
"default_ttl": 3600,
"records": [
{ "id": "1714686702123456000", "zone_id": "example.com",
"name": "@", "type": "A", "value": "192.0.2.1", "ttl": 300 }
]
}
Errors: 404 — zone not found.
curl -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/zones/example.com
$zone = 'example.com';
$ch = curl_init("$host/api/v1/zones/" . urlencode($zone));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($body['records'] as $r) {
printf("%-20s %-6s %s\n", $r['name'], $r['type'], $r['value']);
}
PUT /api/v1/zones/:id
Update zone metadata (description, primary NS, admin email, default TTL). Records are preserved and the SOA serial bumps automatically.
Request body:
{
"description": "Updated description",
"nameserver": "ns2.example.com",
"admin_email": "[email protected]",
"default_ttl": 7200
}
Response 200: { "message": "zone updated", "warning": "..." } (warning optional).
curl -X PUT http://$HOST:8084/api/v1/zones/example.com \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"default_ttl":7200}'
$ch = curl_init("$host/api/v1/zones/example.com");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => json_encode(['default_ttl' => 7200]),
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE /api/v1/zones/:id
Permanently delete a zone (file plus its zones.conf entry).
curl -X DELETE -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/zones/example.com
$ch = curl_init("$host/api/v1/zones/example.com");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) { /* handle error */ }
GET /api/v1/zones/:id/export
Stream the raw .zone file as text/plain. Useful for backups, manual edits in an external editor, or moving the zone to another nameserver. The response sets Content-Disposition: attachment; filename="<zone>.zone".
curl -H "X-API-Key: $KEY" -O -J \
http://$HOST:8084/api/v1/zones/example.com/export
$ch = curl_init("$host/api/v1/zones/example.com/export");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$zoneText = curl_exec($ch);
curl_close($ch);
file_put_contents('example.com.zone', $zoneText);
3.Records
A record belongs to a zone. The server assigns each record an opaque string ID at creation time. Supported types: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA, PTR.
GET /api/v1/records
List records. With no query string, returns every record in every zone. Filters compose with logical AND and are case-insensitive.
| Query param | Description |
|---|---|
zone=<name> | Limit to a single zone. |
type=<TYPE> | Filter by record type, e.g. A, MX, TXT. |
name=<substr> | Substring match on the record name. |
q=<substr> | Substring match across both name and value. |
# All A records in one zone
curl -H "X-API-Key: $KEY" \
"http://$HOST:8084/api/v1/records?zone=example.com&type=A"
$qs = http_build_query(['zone' => 'example.com', 'type' => 'A']);
$ch = curl_init("$host/api/v1/records?$qs");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$records = json_decode(curl_exec($ch), true);
curl_close($ch);
POST /api/v1/records
Create a record in an existing zone.
| Field | Required | Notes |
|---|---|---|
zone_id | yes | The owning zone (must already exist). |
name | yes | @ for the apex, * or *.foo for wildcards, otherwise standard label rules. |
type | yes | One of A, AAAA, CNAME, MX, NS, TXT, SRV, CAA, PTR. |
value | yes | Validated per type — see notes below. |
ttl | no | Defaults to 3600. |
priority | MX/SRV only | Required for MX and SRV. |
weight, port | SRV only | Required for SRV. |
Per-type value validation:
A— must parse as IPv4.AAAA— must parse as IPv6.CNAME,NS,PTR,MX,SRV— DNS hostname; trailing dot optional.TXT— anything non-empty without NUL bytes.CAA— length ≤ 1024 (no further parsing).
curl -X POST http://$HOST:8084/api/v1/records \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"zone_id":"example.com",
"name":"www",
"type":"A",
"value":"192.0.2.10",
"ttl":300
}'
function createRecord(string $host, string $key, array $rec): array {
$ch = curl_init("$host/api/v1/records");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($rec),
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 201) {
throw new RuntimeException("Create failed ($status): $body");
}
return json_decode($body, true);
}
$row = createRecord($host, $key, [
'zone_id' => 'example.com',
'name' => 'www',
'type' => 'A',
'value' => '192.0.2.10',
'ttl' => 300,
]);
echo "Created record id: {$row['id']}\n";
PUT /api/v1/records/:id?zone=<name>
Replace a record. The body shape matches POST minus the zone_id (it goes in the query string instead). All fields are required — this is a full replacement, not a patch.
curl -X PUT \
"http://$HOST:8084/api/v1/records/1714686702123456000?zone=example.com" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"name":"www","type":"A","value":"192.0.2.99","ttl":600}'
$id = '1714686702123456000';
$zone = 'example.com';
$url = "$host/api/v1/records/$id?" . http_build_query(['zone' => $zone]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => json_encode([
'name' => 'www',
'type' => 'A',
'value' => '192.0.2.99',
'ttl' => 600,
]),
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE /api/v1/records/:id?zone=<name>
Delete a single record. The zone query parameter is required. The deleted entry is preserved in the deleted-records sidecar for 48 hours so you can restore it.
curl -X DELETE -H "X-API-Key: $KEY" \
"http://$HOST:8084/api/v1/records/1714686702123456000?zone=example.com"
$id = '1714686702123456000';
$zone = 'example.com';
$url = "$host/api/v1/records/$id?" . http_build_query(['zone' => $zone]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
curl_exec($ch);
curl_close($ch);
POST /api/v1/records/bulk
Create many records in one zone with a single BIND reload at the end. Invalid individual records are skipped (non-fatal) — if every record is invalid the request returns 400.
Response 201:
{
"created": 3,
"records": [ { /* DNSRecord */ }, ... ],
"skipped": [],
"warning": "..."
}
curl -X POST http://$HOST:8084/api/v1/records/bulk \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"zone_id":"example.com",
"records":[
{"name":"@", "type":"A", "value":"192.0.2.1", "ttl":3600},
{"name":"www", "type":"CNAME", "value":"@", "ttl":3600},
{"name":"@", "type":"MX", "value":"mail", "ttl":3600, "priority":10}
]
}'
$payload = json_encode([
'zone_id' => 'example.com',
'records' => [
['name' => '@', 'type' => 'A', 'value' => '192.0.2.1', 'ttl' => 3600],
['name' => 'www', 'type' => 'CNAME', 'value' => '@', 'ttl' => 3600],
['name' => '@', 'type' => 'MX', 'value' => 'mail', 'ttl' => 3600, 'priority' => 10],
],
]);
$ch = curl_init("$host/api/v1/records/bulk");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Created: {$resp['created']}\n";
if (!empty($resp['skipped'])) {
foreach ($resp['skipped'] as $reason) {
echo "Skipped: $reason\n";
}
}
POST /api/v1/records/bulk-delete
Delete many records in one zone with a single BIND reload at the end.
curl -X POST http://$HOST:8084/api/v1/records/bulk-delete \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"zone":"example.com",
"ids":["1714686702123456000","1714686703987654321"]
}'
$payload = json_encode([
'zone' => 'example.com',
'ids' => ['1714686702123456000', '1714686703987654321'],
]);
$ch = curl_init("$host/api/v1/records/bulk-delete");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Deleted: {$resp['deleted']}\n";
4.Restore & import
GET /api/v1/zones/:id/deleted-records
List recoverable records for a zone. Records remain available for 48 hours after deletion, capped at the 50 most recent entries. Older or excess entries are pruned automatically.
Response 200:
[
{
"id": "1714686702123456000",
"name": "www",
"type": "A",
"value": "192.0.2.10",
"ttl": 3600,
"deleted_at": "2026-05-02T03:11:42Z",
"deleted_by": "admin"
}
]
curl -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/zones/example.com/deleted-records
$ch = curl_init("$host/api/v1/zones/example.com/deleted-records");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$deleted = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($deleted as $d) {
echo "{$d['deleted_at']} {$d['name']} {$d['type']} {$d['value']}\n";
}
POST /api/v1/zones/:id/deleted-records/:rid/restore
Re-insert a deleted record back into the zone file and reload BIND. The restored record gets a fresh created_at / updated_at and is removed from the deleted-records list on success.
curl -X POST -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/zones/example.com/deleted-records/1714686702123456000/restore
$zone = 'example.com';
$rid = '1714686702123456000';
$url = "$host/api/v1/zones/$zone/deleted-records/$rid/restore";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($resp['record']);
POST /api/v1/admin/import-text
Import an existing BIND zone file by submitting it as raw text. The server validates it with named-checkzone before writing to disk and reloading BIND. Use this when migrating a zone in from another nameserver.
Request body:
{
"name": "example.com",
"content": "$TTL 3600\n@ IN SOA ns1.example.com. hostmaster.example.com. (\n 2026050201 3600 600 1209600 3600 )\n IN NS ns1.example.com.\nwww IN A 192.0.2.10\n"
}
Response 200: { "zone": "example.com", "message": "zone imported", "warning": "..." } (warning optional).
Errors:
400—invalid zone filewith thenamed-checkzoneoutput indetail.500— write or reload failure.
ZONE_TEXT=$(cat example.com.zone)
curl -X POST http://$HOST:8084/api/v1/admin/import-text \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d "$(jq -n --arg n example.com --arg c "$ZONE_TEXT" \
'{name:$n, content:$c}')"
$content = file_get_contents('example.com.zone');
$payload = json_encode([
'name' => 'example.com',
'content' => $content,
]);
$ch = curl_init("$host/api/v1/admin/import-text");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 400) {
$err = json_decode($body, true);
throw new RuntimeException("Invalid zone: " . $err['detail']);
}
5.Zone templates
Reusable record sets to apply when you create a zone. Four templates ship built-in (Basic Web, Email, Minimal, Full Stack); custom templates can be added from the UI.
GET /api/v1/templates
List all templates. Combine with the bulk-create endpoint to apply a template to a freshly-created zone — see the Apply a template pattern below.
Response 200:
[
{
"id": "basic-web",
"name": "Basic Web",
"description": "A record at apex + www CNAME",
"builtin": true,
"records": [
{ "name": "@", "type": "A", "value": "0.0.0.0", "ttl": 3600 },
{ "name": "www", "type": "CNAME", "value": "@", "ttl": 3600 }
]
}
]
# 1. Create the zone
curl -X POST http://$HOST:8084/api/v1/zones \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"name":"example.com"}'
# 2. Pull the template's records
RECORDS=$(curl -s -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/templates \
| jq '[.[] | select(.id=="basic-web") | .records[]]')
# 3. Bulk-create them in the zone
curl -X POST http://$HOST:8084/api/v1/records/bulk \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d "{\"zone_id\":\"example.com\",\"records\":$RECORDS}"
// 1. Create the zone
createZone($host, $key, ['name' => 'example.com']);
// 2. Fetch templates and pick one
$ch = curl_init("$host/api/v1/templates");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$templates = json_decode(curl_exec($ch), true);
curl_close($ch);
$basicWeb = null;
foreach ($templates as $t) {
if ($t['id'] === 'basic-web') { $basicWeb = $t; break; }
}
// 3. Bulk-apply its records
$payload = json_encode([
'zone_id' => 'example.com',
'records' => $basicWeb['records'],
]);
$ch = curl_init("$host/api/v1/records/bulk");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
6.DNSSEC
Three endpoints wrap BIND's built-in dnssec-policy machinery. Enabling signing flips a flag and lets BIND auto-generate the KSK and ZSK a few seconds later.
GET /api/v1/dnssec/:zone
Current signing state. The endpoint always returns 200 when the zone exists — check the enabled field rather than the status code.
Response 200 — signed and ready:
{
"zone": "example.com",
"enabled": true,
"policy": "default",
"key_dir": "/var/cache/bind",
"keys": [ /* DNSKEYs */ ],
"ds_records": [
{
"tag": 41284, "algorithm": 13, "digest_algorithm": 2,
"digest": "AB12CD34...",
"display": "41284 13 2 AB12CD34..."
}
]
}
curl -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/dnssec/example.com
$ch = curl_init("$host/api/v1/dnssec/example.com");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$state = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($state['enabled'] && !empty($state['ds_records'])) {
echo "DS to publish at registrar:\n";
foreach ($state['ds_records'] as $ds) {
echo " example.com. IN DS {$ds['display']}\n";
}
}
POST /api/v1/dnssec/:zone
Enable signing for the zone. No body required. The flag is saved immediately; BIND generates keys within a few seconds and starts serving signed responses.
GET /api/v1/dnssec/:zone until ds_records is populated, then copy each entry's display string into your domain registrar's DNSSEC settings. Without that step, validating resolvers will not see the chain of trust.
curl -X POST -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/dnssec/example.com
$ch = curl_init("$host/api/v1/dnssec/example.com");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $resp['message'] . PHP_EOL;
DELETE /api/v1/dnssec/:zone
Disable signing. BIND stops signing on reload but the existing key files remain in /var/cache/bind — clean them up by hand if desired.
SERVFAIL responses for validating resolvers. Coordinate the order: pull the DS first, wait for the parent's TTL, then disable signing.
curl -X DELETE -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/dnssec/example.com
$ch = curl_init("$host/api/v1/dnssec/example.com");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
curl_exec($ch);
curl_close($ch);
7.Tools
POST /api/v1/admin/lookup
Run a dig query from the manager host so you can verify your changes are visible from the same machine BIND is running on. Useful after creating or updating a record.
Request body:
{
"name": "www.example.com",
"type": "A",
"server": "127.0.0.1"
}
| Field | Default | Notes |
|---|---|---|
name | required | The DNS name to query. |
type | A | One of A, AAAA, MX, TXT, NS, CNAME, SOA, PTR, CAA, SRV, ANY (case-insensitive). |
server | 127.0.0.1 | Resolver to query, passed as @<server>. |
Response is always 200 with { "output": "...", "error": false }. NXDOMAIN, SERVFAIL, etc. come back with error: true and the dig output in output — display it to the user as-is. Queries are hard-capped at 10 seconds.
curl -X POST http://$HOST:8084/api/v1/admin/lookup \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"name":"www.example.com","type":"A"}'
$payload = json_encode([
'name' => 'www.example.com',
'type' => 'A',
]);
$ch = curl_init("$host/api/v1/admin/lookup");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
"X-API-Key: $key",
"Content-Type: application/json",
],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $resp['output'];
POST /api/v1/bind/reload
Force a BIND reload. Every record/zone mutation already triggers an automatic reload, but call this if a previous request returned a warning field (the data write succeeded but the reload failed) and you've corrected the underlying issue.
The endpoint runs named-checkconf first and refuses to reload if the BIND configuration is invalid — so a bad edit returns 500 with the error in detail while BIND keeps serving the previous (working) config.
curl -X POST -H "X-API-Key: $KEY" \
http://$HOST:8084/api/v1/bind/reload
$ch = curl_init("$host/api/v1/bind/reload");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
$err = json_decode($body, true);
throw new RuntimeException("Reload failed: " . ($err['detail'] ?? $body));
}