DNS API
Built The API runs at https://dns.paasbox.com/v1. Tokens are handed out once the service opens, with the components’ first release. DNS names for your clusters explains the service; this page is the reference.
Base URL and compatibility
Section titled “Base URL and compatibility”https://dns.paasbox.com/v1The API serves the zone and record-set requests of the Hetzner Cloud API, with Hetzner’s request and answer bodies unchanged: {"zones": […], "meta": {"pagination": …}}, {"rrset": {…}}, {"action": {…}}. A Hetzner client works against it when you set its endpoint to this URL and its token to a PaaSbox DNS token. For the hcloud CLI and Hetzner’s Go client that is HCLOUD_ENDPOINT and HCLOUD_TOKEN.
There is one zone, paasbox.app. Your team writes below <team>.paasbox.app, and record names are relative to the zone: the record set api.prod.acme is api.prod.acme.paasbox.app. A wildcard name is sent as a literal *, for example *.apps.acme.
Authentication
Section titled “Authentication”Every request carries a team token as a bearer token:
Authorization: Bearer pbdns_…A token belongs to a team, carries the scope dns, and is shown once when it is created. Without a token, or with an unknown or revoked one, the answer is 401 unauthorized; with a token that lacks the dns scope, 403 forbidden. A team can hold up to five active tokens.
Endpoints
Section titled “Endpoints”| Method and path | What it does |
|---|---|
GET /zones | Lists the one zone, paasbox.app. ?name= with any other name returns an empty list. |
GET /zones/{zone} | The zone, by its ID or by its name. |
GET /zones/{zone}/rrsets | Your team’s record sets, and only those. |
POST /zones/{zone}/rrsets | Creates a record set: name, type, records (each with a value and an optional comment), an optional ttl and labels. |
GET /zones/{zone}/rrsets/{name}/{type} | One record set. |
DELETE /zones/{zone}/rrsets/{name}/{type} | Deletes a record set. |
POST /zones/{zone}/rrsets/{name}/{type}/actions/set_records | Replaces the records of a record set. |
POST /zones/{zone}/rrsets/{name}/{type}/actions/add_records | Adds records to a record set. |
POST /zones/{zone}/rrsets/{name}/{type}/actions/remove_records | Removes records from a record set. |
POST /zones/{zone}/rrsets/{name}/{type}/actions/change_ttl | Changes the TTL of a record set. |
GET /actions?id=…, GET /actions/{id} | The progress of a write, for clients that wait for it. Only zone actions are visible. |
Lists are paginated the way Hetzner paginates them: page and per_page (25 by default, at most 50), with meta.pagination in the answer.
Not served: creating, changing or deleting zones, zone files, zone and record-set protection, the other record-set actions, and every resource of the Hetzner Cloud API that is not DNS, such as servers and volumes. A method the API does not support on a path answers 405 method_not_allowed.
What a token may write
Section titled “What a token may write”- Names: your team’s own name (
acme, anArecord onacme.paasbox.appis allowed) and every name ending in.acme. Not the zone apex, not another team’s name, not a name outside the zone. Names must be plain DNS names; a name is checked before it reaches Hetzner. - Types:
A,AAAA,CNAMEandTXT.NSis refused everywhere. - Values: at most 50 records per record set, each value at most 4,096 characters; a TTL is empty or a non-negative integer.
A read of a name outside your team answers 404 not_found, not 403, so the zone’s contents cannot be listed through the API. Every write is recorded in the team’s audit log with the token, the name, the type and Hetzner’s answer.
Limits
Section titled “Limits”| Records per team | 200, counted across all your record sets |
| Records per record set | 50 |
| Active tokens per team | 5 |
| Requests | In progress 120 per minute per token, 600 per minute per team, 300 per minute per address before a token is checked |
Errors
Section titled “Errors”Errors have Hetzner’s shape, so a client’s error handling stays unchanged:
{"error": {"code": "resource_limit_exceeded", "message": "this team is limited to 200 DNS records on acme.paasbox.app"}}| Status | code | When |
|---|---|---|
| 400 | invalid_input | The body is not a JSON object, records is empty or malformed, or ttl is not an integer. |
| 401 | unauthorized | No token, or an unknown or revoked one. |
| 403 | forbidden | The token lacks the dns scope, or a write names the apex, another team’s name or an invalid name, or a type other than A, AAAA, CNAME and TXT. |
| 403 | resource_limit_exceeded | The write would take the team past 200 records. |
| 404 | not_found | An unknown zone, a name outside your team on read, or an unsupported record-set action. |
| 405 | method_not_allowed | The method is not supported on that path. |
| 429 | rate_limit_exceeded | In progress Over a request limit. Hetzner’s clients retry with a backoff. |
| 502 | upstream_unavailable | Hetzner’s API could not be reached. |
When Hetzner itself refuses a write, its answer is passed on unchanged, with its own status code and code.
Examples
Section titled “Examples”With curl, list your record sets and create an A record:
export PAASBOX_DNS_TOKEN=pbdns_…
curl -s https://dns.paasbox.com/v1/zones/paasbox.app/rrsets \ -H "Authorization: Bearer $PAASBOX_DNS_TOKEN"
curl -s -X POST https://dns.paasbox.com/v1/zones/paasbox.app/rrsets \ -H "Authorization: Bearer $PAASBOX_DNS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "api.prod.acme", "type": "A", "ttl": 300, "records": [{"value": "203.0.113.7"}]}'With the hcloud CLI, replace the records of a wildcard name:
export HCLOUD_ENDPOINT=https://dns.paasbox.com/v1export HCLOUD_TOKEN=pbdns_…
hcloud zone rrset set-records --record 203.0.113.8 paasbox.app '*.apps.acme' ADemo names
Section titled “Demo names”In progress On a branch, not released: short-lived names under demo.paasbox.app, each with a token that can write only below it.
| Method and path | Auth | What it does |
|---|---|---|
POST /demos | a team token, or no token and {"course_code": "…"} | Creates a demo name and its token. Body: name (optional), ttl in seconds (optional; 4 hours by default, at least 300, at most 7 days). The token is in this answer only. |
GET /demos | a team token | The team’s live demos, without tokens. |
GET /demos/{label} | the team’s token or the demo’s own | One demo. |
POST /demos/{label}/actions/extend | the same | {"ttl": seconds}: the expiry becomes now plus ttl, never later than 7 days after creation. |
DELETE /demos/{label} | the same | Deletes the demo’s records now and revokes its token. |
A team has at most 10 live demos, a demo at most 20 records. A demo token works with the zone endpoints above, confined to its own name; it cannot create or list demos.