Skip to content

API and Automation

The interface uses the same HTTP API that is open to automation. The base path is /api/v1.

Two ways in:

  • Session cookie — the interface’s way (POST /api/v1/auth/login).

  • API key as bearer token — the way for scripts and CI:

    Terminal window
    curl -H "Authorization: Bearer dnse_…" https://dns.example.com/api/v1/zones
KindReach
Project keyone project, with a project role (e.g. Editor)
Admin keyinstance-wide, limited by scopes

Admin scopes are users, teams, projects, and settings. An admin key without projects cannot access any project data.

Keys are shown once at creation; only a hash is stored. An expiry date is possible — 14 days beforehand, an email reminds the administrators, because keys do not renew themselves.

Creating: Project → Management → API Keys (project key) or Administration → API Keys (admin key).

Walkthrough: from empty system to managed zone

Section titled “Walkthrough: from empty system to managed zone”

The complete path via curl — with a project key holding the Editor role:

Terminal window
H='Authorization: Bearer dnse_YourKeyHere'
BASE=https://dns.example.com/api/v1

1. Register a provider account (Hetzner example, a single API token):

Terminal window
curl -s -X POST "$BASE/accounts/register" -H "$H" \
-H "Content-Type: application/json" \
-d '{
"projectId": "01…",
"provider": "hetzner",
"name": "Hetzner Produktion",
"credentials": { "api_token": "…" }
}'
# → 201 {"id":"01…","status":"ok",…}

Which credential fields a provider expects is told by GET /api/v1/providers — every integration describes its own fields.

2. View the account’s zones (onboarding list):

Terminal window
curl -s "$BASE/accounts/<accountId>/zones" -H "$H"
# → [{"name":"example.com"},{"name":"example.org"}]

3. Adopt a zone:

Terminal window
curl -s -X POST "$BASE/zones/adopt" -H "$H" \
-H "Content-Type: application/json" \
-d '{"accountId": "<accountId>", "zone": "example.com"}'
# → 201 {"id":"<zoneId>",…}

4. Write a record set — replaces the whole set identified by name and type:

Terminal window
curl -s -X POST "$BASE/zones/<zoneId>/record-sets/upsert" -H "$H" \
-H "Content-Type: application/json" \
-d '{"name": "www", "type": "A", "ttl": 300,
"records": ["203.0.113.10", "203.0.113.11"]}'

5. Check and resolve drift:

Terminal window
curl -s "$BASE/zones/<zoneId>/drift" -H "$H"
# → [{"id":"<driftId>","kind":"MODIFIED","name":"www","type":"A",…}]
curl -s -X POST "$BASE/drift/<driftId>/resolve" -H "$H" \
-H "Content-Type: application/json" \
-d '{"action": "enforce"}' # or "adopt" / "ignore"

6. Search across providers:

Terminal window
curl -s "$BASE/inventory/search?q=203.0.113.10&type=A" -H "$H"
# → {"hits":[…],"truncated":false}
PurposeCall
Instance info (public, no auth)GET /api/v1/instance
Request a certificatePOST /api/v1/projects/{id}/certificates/request
Download a certificateGET /api/v1/certificates/{id}/download
Start a provider migrationPOST /api/v1/zones/{id}/migrate
Migrations of a projectGET /api/v1/projects/{id}/migrations
DynDNS update (router)GET /nic/update (Basic Auth with token)
DynDNS update (script)POST /api/v1/dyndns/update (bearer)

The complete list with all parameters and schemas is in the generated API reference (sidebar → DNS-Editor API). Alongside the specification maintained in the repo (api/openapi.yaml), every instance serves the runtime-generated version at /api/v1/openapi.json.

Errors come as JSON with an error field and a matching status:

StatusMeaning
400invalid input
401no valid authentication
403role or scope insufficient
404does not exist or lies outside your reach
409conflict (e.g. zone already managed)

That “foreign” and “nonexistent” look the same is intentional: the API does not reveal what exists beyond your own permissions.