{"openapi":"3.1.0","info":{"title":"PawDNS REST API","version":"1.0.0","summary":"Forward DNS, reverse DNS (PTR), DNSSEC and DynDNS for PawHost customers and resellers.","contact":{"name":"PawHost","url":"https://manage.pawdns.de","email":"info@pawhost.de"},"license":{"name":"Proprietary","identifier":"LicenseRef-PawHost-Proprietary"},"description":"PawDns is PawHost's central DNS manager. Its database is the source of truth; every change\nis written there first and then **published** asynchronously to the authoritative\nnameservers (`ns1.pawdns.de`, `ns2.pawdns.com`, `ns3.pawdns.eu`). This API is the contract\nfor the customer panel, reseller panels and the WHMCS modules (DESIGN.md §9).\n\nThis document is **contract-first**: the backend implements it, contract tests enforce it,\nand you can build a client today against a mock server (`docs/api/README.md`).\n\n## Authentication\n\nEvery request (except `/nic/update`) carries a **service token**:\n\n```\nAuthorization: Bearer pdns_live_8kQ2vR7xYc1Ue0Lb3MzN5sTq\n```\n\nTokens belong to an **API client**, and every API client belongs to a **tenant**\n(platform, reseller or customer, DESIGN.md §7.3). A token can only ever see and change\nobjects in its own tenant's **subtree**.\n\n| Token owner | Typical client | Sees |\n|---|---|---|\n| Platform (PawHost) | customer panel, WHMCS modules | everything except protected names; normally used with `X-Act-As-Customer` |\n| Reseller | reseller panel, reseller WHMCS | the reseller and all its (sub-)customers |\n| Customer | a customer's own automation (later, see README \"Open questions\") | the customer's own objects |\n\nSandbox tokens start with `pdns_test_` and act on a **sandbox tenant** whose backend is\nfake: everything behaves like production (including publication states), but nothing\nreaches real nameservers.\n\n### Scopes\n\nA token carries a set of scopes. Each operation lists the scope it needs in its `security`\nrequirement. A missing scope gives `403` with code `insufficient_scope`.\n\n| Scope | Grants |\n|---|---|\n| `tenant:read` | `GET /me` |\n| `zones:read` | list/get zones, export, list change sets, templates |\n| `zones:write` | create/delete zones, zone settings, import, apply templates |\n| `records:read` | list/get records, change sets |\n| `records:write` | create/update/delete records, change sets, retry publication |\n| `dnssec:read` | DNSSEC status, DS/DNSKEY |\n| `dnssec:write` | enable/disable DNSSEC, trigger a chain check |\n| `rdns:read` | `GET /ips`, `GET /prefixes`, read PTRs |\n| `rdns:write` | set/reset PTRs, trigger delegation checks |\n| `dyndns:read` | list/get DynDNS hosts |\n| `dyndns:write` | create/delete DynDNS hosts, rotate tokens |\n| `audit:read` | the audit log |\n| `webhooks:manage` | register/list/change/delete webhooks |\n| `act_as` | use the `X-Act-As-Customer` header |\n\n`*:write` does **not** imply `*:read`; give both.\n\n### Acting for a customer (`X-Act-As-Customer`)\n\nA platform or reseller token with the `act_as` scope may send\n\n```\nX-Act-As-Customer: ten_4821        # PawDns tenant id\nX-Act-As-Customer: whmcs:1234      # direct PawHost customer by WHMCS client id\n```\n\nPawDns then evaluates the request **exactly as if that customer made it**: same ownership\nrules, same quotas, same features, and the audit log records both the API client and the\ncustomer. The header only names *who* acts; *what* that customer owns is always looked up\nby PawDns (DESIGN.md §7, \"never trust the request\").\n\n- The target must be a tenant of type `customer` inside the token's subtree. Otherwise\n  `403 act_as_not_allowed` (outside the subtree, or not a customer) or\n  `404 act_as_unknown` (no such tenant / WHMCS client).\n- Without the header the **acting tenant** is the token's own tenant.\n- A reseller token can therefore act for its own customers, but never for PawHost's\n  direct customers or another reseller's customers.\n- **First contact by WHMCS client id (auto-link).** When a **live platform token**\n  sends `whmcs:<id>` and no PawDns customer is linked to that WHMCS client yet, PawDns\n  reads the client from WHMCS (read-only) and creates its customer tenant directly below\n  PawHost, linked to that one client and audit-logged (`tenant.created`,\n  `tenant.whmcs_linked`). The request then proceeds as that customer. It happens once:\n  later requests (and parallel ones) use the same tenant. Only that client is linked,\n  never others. Unknown and closed WHMCS clients stay `404 act_as_unknown`. A token tied\n  to a user additionally needs the user permission `tenants.whmcs_link`; reseller and\n  sandbox tokens never auto-link (they get `404 act_as_unknown` as before).\n\n## Conventions\n\n- **Versioning.** All paths are under `/api/v1`. Additive changes (new fields, new enum\n  values, new endpoints) happen without a version bump; clients **must ignore unknown\n  fields and tolerate unknown enum values**. Breaking changes get `/api/v2`. Deprecated\n  operations announce themselves with `Deprecation` and `Sunset` headers at least six\n  months ahead.\n- **JSON.** Requests and responses are `application/json` (UTF-8). Timestamps are\n  RFC 3339 in UTC. IDs are opaque strings with a type prefix (`rec_…`, `chg_…`); do not\n  parse them. Zones are addressed by **name** (`/zones/example.com`), IDN zones by their\n  A-label (`xn--…`).\n- **Names.** DNS names are returned in lower case and without a trailing dot. Input may\n  carry a trailing dot on target host names; it is stripped. U-labels are converted to\n  A-labels.\n- **Lists** return `{\"data\": [...], \"pagination\": {\"next_cursor\": \"...\", \"has_more\": true}}`.\n  Pass `next_cursor` as `?cursor=` to get the next page. Cursors are opaque and expire\n  after 24 h. `limit` defaults to 50 (max 500). A `Link: <…>; rel=\"next\"` header is\n  set as well. Lists have no total count, except where documented (`/zones` returns\n  `pagination.total` because it is cheap there).\n- **Single objects** are returned unwrapped.\n- **Errors** are RFC 7807 `application/problem+json` with a stable machine-readable\n  `code` (see `Problem`). `title` and `detail` are localised: send\n  `Accept-Language: de` or `en` (default `en`); the response carries `Content-Language`.\n  Branch on `code`, never on `title`/`detail`.\n- **Idempotency.** Every `POST` and `PATCH` accepts an `Idempotency-Key` header (any\n  unique string up to 255 chars, a UUID is recommended). PawDns stores the first response\n  for 24 h per API client + acting tenant + key and replays it for retries\n  (`Idempotent-Replayed: true`). Reusing a key with a different body gives\n  `422 idempotency_key_reused`; a retry while the first request is still running gives\n  `409 idempotency_in_progress`.\n- **Optimistic concurrency.** Zones and records return an `ETag`. Send it back as\n  `If-Match` on `PATCH`/`DELETE`; on mismatch PawDns answers `412 precondition_failed`\n  and changes nothing. `If-Match` is optional but **the panel should always send it**.\n  `If-None-Match` on `GET` gives `304` when nothing changed. Change set items carry an\n  optional `if_match` per record.\n- **Rate limits** are per API client and, when acting for a customer, additionally per\n  customer (tenant limit `api_rate_limit_per_minute`). Every response carries\n  `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds until the\n  window resets). Over the limit: `429 rate_limited` with `Retry-After`.\n- **Request ids.** Every response has `X-Request-Id`; quote it in support requests. It is\n  also in every problem body (`request_id`).\n\n## Publication\n\nA write succeeds as soon as it is stored in PawDns. Publication to the nameservers happens\nin the background, so every write produces a **change set** with a publication state:\n\n| State | Meaning | Panel wording |\n|---|---|---|\n| `pending` | stored, waiting to be pushed to the backend | \"Wird veröffentlicht …\" / \"Publishing …\" |\n| `pushed` | the backend accepted it; it appears on the nameservers within ~15 minutes | \"An Nameserver übertragen\" / \"Sent to nameservers\" |\n| `live` | PawDns saw the new data on **all** authoritative nameservers | \"Live\" |\n| `failed` | the backend rejected it or retries are exhausted; PawDns keeps retrying via reconcile, a manual retry is possible | \"Fehlgeschlagen\" / \"Failed\" |\n\nIf a pushed change is still not visible on all nameservers after 60 minutes, the change set\nstays `pushed` and gets `error.code: verification_timeout` (the backend did accept it).\n\nPoll `GET /zones/{zone}/changesets/{changeset}` or subscribe to the\n`record.published` / `record.failed` webhooks. Every record and zone also carries a\n`publication` summary.\n"},"servers":[{"url":"https://sandbox.pawdns.de/api/v1","description":"Sandbox (same host; use a `pdns_test_…` token, fake backend, nothing reaches real nameservers)"},{"url":"https://manage.pawdns.de/api/v1","description":"Production (`pdns_live_…` token)"}],"security":[{"serviceToken":[]}],"tags":[{"name":"Tenant","description":"The acting tenant, its limits, usage, branding and features. Read-only."},{"name":"Zones","description":"Forward zones (and reverse zones of customer-owned prefixes). Create within quota, settings, import/export."},{"name":"Records","description":"Resource records. Supported types: A, AAAA, CNAME, MX, TXT (incl. long TXT), SRV, CAA,\nTLSA, NS (delegations only) and PTR (reverse zones only). The apex NS set and the SOA are\nmanaged by PawDns and are read-only.\n"},{"name":"Change sets","description":"Atomic multi-record changes and the publication state of every change."},{"name":"DNSSEC","description":"Automatic DNSSEC following the per-zone state machine (DESIGN.md §9a)."},{"name":"Reverse DNS","description":"The IPs and prefixes the customer owns, and their PTR records."},{"name":"DynDNS","description":"DynDNS hosts and their tokens. Devices update via the dyndns2-compatible\n`GET /nic/update` endpoint, which lives **outside** `/api/v1`.\n"},{"name":"Templates","description":"Record templates (mail, hosting, …) that can be applied to a zone."},{"name":"Audit","description":"The acting tenant's own audit log."},{"name":"Webhooks","description":"Webhook endpoints receive signed `POST` requests for the events listed under\n**Webhooks** (`zone.changed`, `record.published`, `record.failed`,\n`dnssec.state_changed`, `quota.warning`).\n\n**Ownership.** A webhook belongs to the tenant it was created for. Created without\n`X-Act-As-Customer`, it receives the events of the token's whole subtree (one webhook\nfor the whole panel); created with the header, only that customer's events. Every\nevent carries `tenant.id` so receivers can route it.\n\n**Signature.** Each delivery has the header\n`PawDns-Signature: t=<unix seconds>,v1=<hex>`, where\n`v1 = hex(HMAC-SHA256(secret, \"<t>.<raw request body>\"))`. Verify with a constant-time\ncomparison and reject deliveries whose `t` is more than 5 minutes off. During secret\nrotation, deliveries carry two `v1=` values (old and new secret) for 24 h.\n\n**Delivery and retries.** A delivery succeeds on any `2xx` within 10 s. Otherwise it is\nretried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h (8 attempts in total).\nAfter 3 days of failing deliveries the webhook is set to `disabled` and the tenant's\nsupport contact is e-mailed. Deliveries are **at least once** and **not ordered**: use\n`PawDns-Delivery` / the event `id` for de-duplication and `occurred_at` for ordering.\n"}],"paths":{"/me":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getMe","tags":["Tenant"],"summary":"Get the acting tenant","description":"Returns the **acting tenant** (the customer named in `X-Act-As-Customer`, or the\ntoken's own tenant), its limits and current usage, its branding (for white-label\npanels), the features it may use, and information about the calling token.\n\nUse `features` to show or hide panel areas, and `limits`/`usage` for quota bars.\n","security":[{"serviceToken":["tenant:read"]}],"responses":{"200":{"description":"The acting tenant.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"examples":{"actingCustomer":{"$ref":"#/components/examples/MeCustomer"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/stats":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getStats","tags":["Tenant"],"summary":"Get dashboard numbers of the acting tenant","description":"Aggregated counts for the **acting tenant's subtree** (the customer named in\n`X-Act-As-Customer`, or the token's tenant): zones, records by type, IP space with PTR\ncounts, DynDNS hosts, webhooks and API tokens. The same numbers as the PawDNS dashboard.\n\nComputed from PawDNS's own data only and cached for about 60 seconds, so values can lag\nchanges by up to a minute (`generated_at`). A platform token gets platform-wide numbers\n(`scope: platform`; the API sandbox is never included). Only with `scope: platform`:\n`ip.ipv6` (IPv6 networks, prefixes and addresses handed out per source, IPv6 PTRs; no\naddress percentages) and `hosttech_quota` (the last zone quota reading, refreshed every\n15 minutes; never a live call).\n","security":[{"serviceToken":["tenant:read"]}],"responses":{"200":{"description":"The numbers of the acting subtree.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stats"},"examples":{"reseller":{"$ref":"#/components/examples/StatsReseller"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listZones","tags":["Zones"],"summary":"List and search zones","description":"Lists the zones of the acting tenant's subtree, ordered by name. Protected names\n(PawHost's own domains) are never listed. Reverse zones appear only for\n**customer-owned prefixes** (DESIGN.md §7.2); PTRs in PawHost address space are\nmanaged via `/ptr/{ip}` instead.\n","security":[{"serviceToken":["zones:read"]}],"parameters":[{"name":"q","in":"query","description":"Case-insensitive substring match on the zone name (A-label or U-label).","schema":{"type":"string","maxLength":253},"example":"example"},{"name":"kind","in":"query","schema":{"$ref":"#/components/schemas/ZoneKind"}},{"name":"dnssec_state","in":"query","description":"Filter by DNSSEC state; repeat for several.","schema":{"type":"array","items":{"$ref":"#/components/schemas/DnssecState"}},"style":"form","explode":true},{"name":"publication_state","in":"query","description":"Only zones whose aggregated publication state matches.","schema":{"$ref":"#/components/schemas/ZonePublicationState"}},{"name":"tenant_id","in":"query","description":"Reseller/platform tokens without `X-Act-As-Customer` only — restrict to one tenant of the subtree.","schema":{"type":"string"},"example":"ten_4821"},{"name":"sort","in":"query","schema":{"type":"string","enum":["name","-name","created_at","-created_at"],"default":"name"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of zones.","headers":{"Link":{"$ref":"#/components/headers/Link"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneList"},"examples":{"page":{"$ref":"#/components/examples/ZoneListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"createZone","tags":["Zones"],"summary":"Create a zone","description":"Creates a **forward** zone for the acting tenant. Checks, in this order:\n\n1. the name is a valid registrable domain or subdomain (`validation_failed`)\n2. it is not a protected name or below one (`zone_protected`)\n3. no tenant owns it already (`zone_exists`) and it does not overlap an existing zone (`zone_overlap`)\n4. the tenant's `max_zones` quota (`quota_exceeded`)\n5. the backend still has capacity (`backend_capacity_exhausted`, DESIGN.md §7.2)\n\nReverse zones cannot be created here (`reverse_zone_managed`); they are created when\na prefix is assigned.\n\nThe zone gets the apex NS set and SOA automatically. Optionally a template is applied\nand initial records are created in the same atomic change set. The zone starts in\nstatus `activating` until the nameservers answer for it.\n","security":[{"serviceToken":["zones:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneCreate"},"examples":{"minimal":{"summary":"Empty zone with defaults","value":{"name":"example.com"}},"withTemplate":{"summary":"Zone with the mail template and a web server address","value":{"name":"example.com","default_ttl":3600,"dnssec":true,"template":{"template_id":"tpl_mail_pawhost","variables":{"mail_host":"mail.example.net"}},"records":[{"type":"A","name":"@","data":{"address":"192.0.2.10"}},{"type":"CNAME","name":"www","data":{"target":"example.com"}}]}}}}}},"responses":{"201":{"description":"Zone created. Publication runs in the background.","headers":{"Location":{"$ref":"#/components/headers/Location"},"ETag":{"$ref":"#/components/headers/ETag"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"},"examples":{"created":{"$ref":"#/components/examples/ZoneActivating"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`zone_protected`, `feature_disabled`, `insufficient_scope`, `tenant_suspended`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"protected":{"$ref":"#/components/examples/ProblemZoneProtected"}}}}},"404":{"$ref":"#/components/responses/ActAsUnknown"},"409":{"description":"`zone_exists`, `zone_overlap`, `quota_exceeded`, `idempotency_in_progress`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"exists":{"$ref":"#/components/examples/ProblemZoneExists"},"quota":{"$ref":"#/components/examples/ProblemQuotaExceeded"}}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/zones/{zone}":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getZone","tags":["Zones"],"summary":"Get a zone","description":"Zone details including settings, nameserver delegation, DNSSEC summary and publication summary.","security":[{"serviceToken":["zones:read"]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The zone.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"},"examples":{"active":{"$ref":"#/components/examples/ZoneActive"}}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"operationId":"updateZone","tags":["Zones"],"summary":"Change zone settings","description":"Changes zone-level settings. Only the fields sent are changed. Changing\n`default_ttl` affects every record whose `ttl` is `null` and is published like a\nrecord change.\n","security":[{"serviceToken":["zones:write"]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneUpdate"},"examples":{"ttl":{"summary":"Lower the default TTL before a migration","value":{"default_ttl":300}},"soa":{"summary":"Change the SOA contact","value":{"soa_email":"hostmaster@example.com"}}}}}},"responses":{"200":{"description":"Updated zone.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"},"examples":{"updated":{"$ref":"#/components/examples/ZoneActive"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"deleteZone","tags":["Zones"],"summary":"Delete a zone","description":"Deletes the zone and all its records. This removes the zone from the nameservers and\n**cannot be undone**; PawDns keeps a BIND export of the last state for 30 days\n(support can restore it).\n\n- `confirm` must repeat the zone name, as a guard against accidental deletes.\n- If DNSSEC is active and the DS is managed by PawDns (registrar API), PawDns removes\n  the DS first and deletes the zone only after the DS is gone from the parent, so the\n  domain never goes bogus. The zone shows status `deleting` until then.\n- If the DS was added by the customer at a foreign registrar (`mode: assisted`) and is\n  still visible at the parent, the request is refused with `409 dnssec_ds_present`\n  unless `force=true`.\n- DynDNS hosts in the zone are deleted too.\n","security":[{"serviceToken":["zones:write"]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"},{"name":"confirm","in":"query","required":true,"description":"Must equal the zone name.","schema":{"type":"string"},"example":"example.com"},{"name":"force","in":"query","description":"Delete even though a customer-managed DS is still published at the parent.","schema":{"type":"boolean","default":false}}],"responses":{"202":{"description":"Deletion accepted. The zone disappears once the backend confirmed it (`zone.changed` webhook with `change: deleted`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"},"examples":{"deleting":{"$ref":"#/components/examples/ZoneDeleting"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`dnssec_ds_present` or `conflict` (deletion already running).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"dsPresent":{"$ref":"#/components/examples/ProblemDsPresent"}}}}},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/import":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"importZone","tags":["Zones"],"summary":"Import records from a BIND zone file","description":"Parses a BIND zone file (pasted as JSON or uploaded as `multipart/form-data`) and\napplies it as **one atomic change set**.\n\n- `mode: merge` (default) adds records that do not exist yet and leaves the rest alone.\n  `mode: replace` makes the zone match the file exactly (records missing in the file\n  are deleted).\n- `dry_run: true` returns the diff without changing anything. The panel should always\n  show a dry run first.\n- `$ORIGIN`, `$TTL`, relative names, `@`, multi-line `( … )` records and quoted\n  multi-string TXT are supported. Owner names outside the zone are errors.\n- Ignored (reported in `skipped`): SOA, apex NS (managed by PawDns), DNSKEY/RRSIG/NSEC*\n  /DS (DNSSEC is handled by PawDns), and unsupported types.\n- All records are validated like a change set; any error rejects the whole import\n  with `422` and per-line errors.\n- Max file size 1 MiB.\n","security":[{"serviceToken":["zones:write","records:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneImportRequest"},"examples":{"paste":{"summary":"Pasted zone file, dry run","value":{"mode":"merge","dry_run":true,"zonefile":"$ORIGIN example.com.\n$TTL 3600\n@       IN SOA  ns1.example.net. hostmaster.example.com. 2026101101 7200 3600 1209600 3600\n@       IN NS   ns1.example.net.\n@       IN A    192.0.2.10\nwww     IN CNAME example.com.\n@       IN MX   10 mail.example.com.\nmail    IN A    192.0.2.25\n@       IN TXT  \"v=spf1 mx -all\"\n"}}}},"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ZoneImportUpload"},"examples":{"upload":{"summary":"Uploaded file","value":{"mode":"replace","dry_run":false,"file":"example.com.zone"}}}}}},"responses":{"200":{"description":"Dry run result (nothing changed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneImportResult"},"examples":{"dryRun":{"$ref":"#/components/examples/ImportDryRun"}}}}},"201":{"description":"Import applied as a change set.","headers":{"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneImportResult"},"examples":{"applied":{"$ref":"#/components/examples/ImportApplied"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/export":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"exportZone","tags":["Zones"],"summary":"Export the zone as a BIND zone file","description":"Returns the zone in BIND presentation format (RFC 1035 §5), as stored in PawDns.\nNames are absolute (trailing dot). Long TXT values are split into 255-byte strings.\n`include_system=true` adds the SOA and apex NS records.\n","security":[{"serviceToken":["zones:read","records:read"]}],"parameters":[{"name":"include_system","in":"query","schema":{"type":"boolean","default":true}},{"name":"download","in":"query","description":"Set `Content-Disposition: attachment; filename=\"<zone>.zone\"`.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Zone file.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"text/dns":{"schema":{"type":"string"},"examples":{"zonefile":{"summary":"example.com","value":"; example.com exported by PawDns 2026-10-11T09:30:00Z\n$ORIGIN example.com.\n$TTL 3600\n@             3600 IN SOA   ns1.pawdns.de. hostmaster.example.com. 2026101104 7200 3600 1209600 3600\n@             3600 IN NS    ns1.pawdns.de.\n@             3600 IN NS    ns2.pawdns.com.\n@             3600 IN NS    ns3.pawdns.eu.\n@             3600 IN A     192.0.2.10\n@             3600 IN AAAA  2001:db8:10::10\n@             3600 IN MX    10 mail.example.com.\n@             3600 IN TXT   \"v=spf1 mx -all\"\n@             3600 IN CAA   0 issue \"letsencrypt.org\"\nwww           3600 IN CNAME example.com.\nmail          3600 IN A     192.0.2.25\n_dmarc        3600 IN TXT   \"v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com\"\n_sip._tcp     3600 IN SRV   10 5 5060 sip.example.com.\n_25._tcp.mail 3600 IN TLSA  3 1 1 0c72ac70b745ac19998811b131d662c9ac69dbdbe7cb23e5b514b56664c5d3d6\n"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/records":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listRecords","tags":["Records"],"summary":"List records of a zone","description":"Records ordered by name (apex first), then type, then content. System records (apex NS)\nare included with `system: true` unless `include_system=false`.\n","security":[{"serviceToken":["records:read"]}],"parameters":[{"name":"type","in":"query","description":"Filter by type; repeat for several (`?type=A&type=AAAA`).","schema":{"type":"array","items":{"$ref":"#/components/schemas/RecordType"}},"style":"form","explode":true},{"name":"name","in":"query","description":"Exact owner name, zone-relative (`@` for the apex).","schema":{"type":"string"},"example":"www"},{"name":"q","in":"query","description":"Case-insensitive substring search over name and presentation content.","schema":{"type":"string","maxLength":255},"example":"mail"},{"name":"publication_state","in":"query","schema":{"$ref":"#/components/schemas/PublicationState"}},{"name":"include_system","in":"query","schema":{"type":"boolean","default":true}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of records.","headers":{"Link":{"$ref":"#/components/headers/Link"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordList"},"examples":{"page":{"$ref":"#/components/examples/RecordListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"createRecord","tags":["Records"],"summary":"Create a record","description":"Creates one record. Equivalent to a change set with a single `create` item; the\nimplicit change set id is in `publication.changeset_id`. Validation rules are\ndocumented on each record type schema (`ARecord`, `CnameRecord`, …).\n","security":[{"serviceToken":["records:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordInput"},"examples":{"a":{"summary":"A record","value":{"type":"A","name":"www","ttl":3600,"data":{"address":"192.0.2.10"}}},"aaaa":{"summary":"AAAA record using the zone default TTL","value":{"type":"AAAA","name":"www","ttl":null,"data":{"address":"2001:db8:10::10"}}},"cname":{"summary":"CNAME","value":{"type":"CNAME","name":"shop","data":{"target":"shops.example.net"}}},"mx":{"summary":"MX","value":{"type":"MX","name":"@","data":{"preference":10,"exchange":"mail.example.com"}}},"txtLong":{"summary":"Long TXT (DKIM, > 255 bytes)","value":{"type":"TXT","name":"default._domainkey","data":{"text":"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwq3cZ0W2y6Jq1v8m4Ea7pP1h3c2Qy0p9g8vH6sX5Zt3Lr7Kq2Nw4Uj9Bf6Ym1Dc0Tg8Sx3Ve5Ra7Qp2Wn9Lk4Hb6Gc1Fd3Mj0Pz8Ky5Ix2Ow7Tv4Ru1Qs9Ep6Ah3Jg0Ni8Lm5Kc2Bb7Va4Uz1Yx9Wt6Sr3Qo0Pn8Mk5Lj2Ih9Gf6Ed3Dc0Ba7Zy4Xw1Vu8Ts5Rq2Po9Nm6Lk3Ji0Hg7Fe4Dc1Ba8Zy5Xw2Vu9Ts6Rq3Po0Nm7Lk4Ji1Hg8Fe5Dc2Ba9QIDAQAB"}}},"srv":{"summary":"SRV","value":{"type":"SRV","name":"_sip._tcp","data":{"priority":10,"weight":5,"port":5060,"target":"sip.example.com"}}},"caa":{"summary":"CAA","value":{"type":"CAA","name":"@","data":{"flags":0,"tag":"issue","value":"letsencrypt.org"}}},"tlsa":{"summary":"TLSA (DANE for SMTP)","value":{"type":"TLSA","name":"_25._tcp.mail","data":{"usage":3,"selector":1,"matching_type":1,"certificate_data":"0c72ac70b745ac19998811b131d662c9ac69dbdbe7cb23e5b514b56664c5d3d6"}}},"ns":{"summary":"NS delegation of a subdomain","value":{"type":"NS","name":"lab","data":{"nameserver":"ns1.example.net"}}}}}}},"responses":{"201":{"description":"Record created; publication pending.","headers":{"Location":{"$ref":"#/components/headers/Location"},"ETag":{"$ref":"#/components/headers/ETag"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"},"examples":{"created":{"$ref":"#/components/examples/RecordAPending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/records/{record}":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/RecordId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getRecord","tags":["Records"],"summary":"Get a record","security":[{"serviceToken":["records:read"]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The record.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"},"examples":{"live":{"$ref":"#/components/examples/RecordMxLive"}}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"operationId":"updateRecord","tags":["Records"],"summary":"Update a record","description":"Changes name, TTL, comment and/or data. The `type` cannot change (delete and create\ninstead). `data` must be the **complete** data object of the record's type.\nSystem records give `403 record_readonly`.\n","security":[{"serviceToken":["records:write"]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPatch"},"examples":{"ttl":{"summary":"Change only the TTL","value":{"ttl":600}},"data":{"summary":"Point the MX at another host","value":{"data":{"preference":10,"exchange":"mx1.example.net"},"comment":"moved to new mail provider"}}}}}},"responses":{"200":{"description":"Record updated; publication pending.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"},"examples":{"updated":{"$ref":"#/components/examples/RecordMxPending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"deleteRecord","tags":["Records"],"summary":"Delete a record","description":"Deletes the record. The response is the implicit change set, so the panel can follow\nthe removal from the nameservers. System records give `403 record_readonly`.\n","security":[{"serviceToken":["records:write"]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"202":{"description":"Deleted in PawDns; removal from the nameservers pending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSet"},"examples":{"deleted":{"$ref":"#/components/examples/ChangeSetDeletePending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/changesets":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listChangeSets","tags":["Change sets"],"summary":"List change sets of a zone","description":"Newest first. Includes the implicit change sets of single-record writes, imports,\ntemplate applications and zone setting changes. Use `state=pending&state=pushed` to\nshow \"changes not yet live\".\n","security":[{"serviceToken":["records:read"]}],"parameters":[{"name":"state","in":"query","schema":{"type":"array","items":{"$ref":"#/components/schemas/PublicationState"}},"style":"form","explode":true},{"name":"since","in":"query","description":"Only change sets created at or after this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of change sets (without `results` details).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSetList"},"examples":{"page":{"$ref":"#/components/examples/ChangeSetListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"createChangeSet","tags":["Change sets"],"summary":"Apply an atomic change set","description":"Applies up to 1000 creates, updates, deletes and RRset replacements to one zone,\n**all or nothing**.\n\n- Items are validated against the zone state **after** all items are applied, so a\n  change set can, for example, delete an A record and create a CNAME at the same name.\n- Any invalid item rejects the whole change set with `422 validation_failed`; each\n  error carries the item `index` and a JSON `pointer` into the request body.\n- `if_match` per item (the record's ETag) and `zone_if_match` (the zone ETag) give\n  optimistic concurrency; a mismatch gives `412 precondition_failed` with the\n  offending items in `errors`.\n- `dry_run: true` validates and returns the diff and warnings without storing\n  anything (`200`).\n- On success the change set is stored as one unit and published as one unit\n  (`201`, state `pending`).\n","security":[{"serviceToken":["records:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSetRequest"},"examples":{"moveWebsite":{"summary":"Move the website to a new server (replace A/AAAA, add CAA, drop old record)","value":{"comment":"Umzug auf neuen Webserver","zone_if_match":"\"z-4f1c2a-17\"","changes":[{"op":"replace_rrset","name":"@","type":"A","ttl":300,"records":[{"address":"192.0.2.20"},{"address":"192.0.2.21"}]},{"op":"update","id":"rec_9mWq3T","if_match":"\"r-9mWq3T-4\"","record":{"data":{"address":"2001:db8:10::20"}}},{"op":"create","record":{"type":"CAA","name":"@","data":{"flags":0,"tag":"issue","value":"letsencrypt.org"}}},{"op":"delete","id":"rec_2Pz8Kd"}]}},"swapToCname":{"summary":"Replace an A record by a CNAME in one step","value":{"changes":[{"op":"delete","id":"rec_7Hq2Lm"},{"op":"create","record":{"type":"CNAME","name":"www","data":{"target":"example.com"}}}]}},"dryRun":{"summary":"Validate only","value":{"dry_run":true,"changes":[{"op":"create","record":{"type":"CNAME","name":"@","data":{"target":"example.net"}}}]}}}}}},"responses":{"200":{"description":"Dry run result. Nothing was stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSetDryRun"},"examples":{"dryRun":{"$ref":"#/components/examples/ChangeSetDryRunResult"}}}}},"201":{"description":"Change set stored; publication pending.","headers":{"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSet"},"examples":{"pending":{"$ref":"#/components/examples/ChangeSetPending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"`validation_failed` with one entry per invalid item in `errors`, or\n`idempotency_key_reused`.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"perItem":{"$ref":"#/components/examples/ProblemChangeSetValidation"}}}}},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/changesets/{changeset}":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ChangeSetId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getChangeSet","tags":["Change sets"],"summary":"Get a change set and its publication state","description":"Poll this to show \"live at the nameservers\". Recommended polling: every 5 s while\n`pending`, every 30 s while `pushed`; stop at `live` or `failed`. Prefer the\n`record.published` / `record.failed` webhooks where possible.\n","security":[{"serviceToken":["records:read"]}],"responses":{"200":{"description":"The change set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSet"},"examples":{"pushed":{"$ref":"#/components/examples/ChangeSetPushed"},"live":{"$ref":"#/components/examples/ChangeSetLive"},"failed":{"$ref":"#/components/examples/ChangeSetFailed"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/changesets/{changeset}/retry":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ChangeSetId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"retryChangeSet","tags":["Change sets"],"summary":"Retry publishing a failed change set","description":"Requeues a `failed` change set for publication. The PawDns data is not changed (it\nalready is the source of truth); only the push to the nameservers is retried. Only\nallowed in state `failed` (`409 conflict` otherwise).\n","security":[{"serviceToken":["records:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"202":{"description":"Requeued; state is `pending` again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSet"},"examples":{"requeued":{"$ref":"#/components/examples/ChangeSetPending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/apply-template":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"applyTemplate","tags":["Templates"],"summary":"Apply a template to a zone","description":"Renders the template with the given variables and applies the result as one atomic\nchange set.\n\n- `on_conflict: skip` (default) leaves existing records with the same name and type\n  alone; `replace` replaces those RRsets with the template's.\n- CNAME conflicts (a template record at a name that has a CNAME, or vice versa) are\n  validation errors unless `on_conflict: replace`.\n- `dry_run: true` returns the diff only.\n","security":[{"serviceToken":["zones:write","records:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateApplyRequest"},"examples":{"mail":{"summary":"PawHost mail template","value":{"template_id":"tpl_mail_pawhost","variables":{"mail_host":"mail.example.net"},"on_conflict":"skip","dry_run":false}}}}}},"responses":{"200":{"description":"Dry run result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSetDryRun"},"examples":{"dryRun":{"$ref":"#/components/examples/ChangeSetDryRunResult"}}}}},"201":{"description":"Template applied as a change set.","headers":{"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSet"},"examples":{"applied":{"$ref":"#/components/examples/ChangeSetPending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/dnssec":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getDnssec","tags":["DNSSEC"],"summary":"Get DNSSEC status","description":"Current state in the DNSSEC state machine (DESIGN.md §9a), the DNSKEYs published at\nour nameservers, the DS records to be placed at the parent, the DS records actually\nseen at the parent, and the result of PawDns's own chain validation.\n\nIn `mode: assisted` (domain at a foreign registrar) the state stops at `awaiting_ds`\nuntil the customer has added the DS at their registrar. Show `ds` and the\n`next_action` hint in the panel.\n","security":[{"serviceToken":["dnssec:read"]}],"responses":{"200":{"description":"DNSSEC status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dnssec"},"examples":{"active":{"$ref":"#/components/examples/DnssecActive"},"awaitingDs":{"$ref":"#/components/examples/DnssecAwaitingDs"},"off":{"$ref":"#/components/examples/DnssecOff"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/zones/{zone}/dnssec/enable":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"enableDnssec","tags":["DNSSEC"],"summary":"Enable DNSSEC","description":"Starts the state machine `off → signing → keys_published → ds_submitted (or\nawaiting_ds) → ds_published → validated → active`. Each transition fires\n`dnssec.state_changed`. Typical duration: 30–90 minutes (backend publication delay\nplus parent TTLs).\n\n`mode` defaults to `automatic` when the domain is registered through PawHost (DS is\nsubmitted via the registrar API) and to `assisted` otherwise. Requesting `automatic`\nfor a domain PawDns cannot submit a DS for gives `422 validation_failed`.\n\nRefused with `409 dnssec_not_delegated` when the domain's NS do not point at our\nnameservers, and `409 dnssec_transition_in_progress` while a transition is running.\nCalling it on a zone that is already enabling or active returns the current status\n(`200`).\n","security":[{"serviceToken":["dnssec:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnssecEnableRequest"},"examples":{"auto":{"summary":"Let PawDns decide","value":[]},"assisted":{"summary":"Force assisted mode","value":{"mode":"assisted"}}}}}},"responses":{"200":{"description":"Already enabled or enabling; current status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dnssec"},"examples":{"active":{"$ref":"#/components/examples/DnssecActive"}}}}},"202":{"description":"Enabling started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dnssec"},"examples":{"signing":{"$ref":"#/components/examples/DnssecSigning"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`dnssec_not_delegated` or `dnssec_transition_in_progress`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notDelegated":{"$ref":"#/components/examples/ProblemDnssecNotDelegated"}}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/zones/{zone}/dnssec/disable":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"disableDnssec","tags":["DNSSEC"],"summary":"Disable DNSSEC safely","description":"Runs the safe rollback: **DS removed at the parent first** (`ds_removing`), then,\nonce the DS is gone from the parent and its TTL has expired, signing is switched off\n(`unsigning → off`). The zone never goes bogus.\n\nIn `mode: assisted` the customer must remove the DS at their registrar; the state\nstays `ds_removing` with `next_action: remove_ds_at_registrar` until PawDns no longer\nsees the DS at the parent.\n","security":[{"serviceToken":["dnssec:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"202":{"description":"Disabling started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dnssec"},"examples":{"removing":{"$ref":"#/components/examples/DnssecDsRemoving"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/zones/{zone}/dnssec/check":{"parameters":[{"$ref":"#/components/parameters/ZoneName"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"checkDnssec","tags":["DNSSEC"],"summary":"Re-check the DNSSEC chain now","description":"Runs PawDns's chain check immediately (DS at the parent vs. DNSKEY at our nameservers,\nplus validation through a validating resolver) instead of waiting for the next\nscheduled check. Useful after a customer added the DS in assisted mode. Limited to one\ncheck per zone per minute (`429`).\n","security":[{"serviceToken":["dnssec:write"]}],"responses":{"200":{"description":"Check finished; updated status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dnssec"},"examples":{"active":{"$ref":"#/components/examples/DnssecActive"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/ips":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listIps","tags":["Reverse DNS"],"summary":"List the IP assignments the customer owns","description":"The addresses and small subnets assigned to the acting tenant from **PawHost address\nspace** (servers via TenantOS, VMs via OpenStack, extra IPs via WHMCS). For IPv6 an\nassignment is often a whole subnet (e.g. a /64 for a server); every address inside it\ncan get a PTR via `/ptr/{ip}`.\n\nThe assignment data is read-only here; it comes from the systems that assign IPs\n(DESIGN.md §7). Customer-owned prefixes (own PI/PA space) are listed under\n`/prefixes` instead.\n","security":[{"serviceToken":["rdns:read"]}],"parameters":[{"name":"family","in":"query","schema":{"type":"integer","enum":[4,6]}},{"name":"q","in":"query","description":"Substring match on IP, PTR target or server name.","schema":{"type":"string","maxLength":255},"example":"web01"},{"name":"within","in":"query","description":"Only assignments inside this CIDR.","schema":{"type":"string"},"example":"192.0.2.0/24"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of IP assignments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IpAssignmentList"},"examples":{"page":{"$ref":"#/components/examples/IpAssignmentListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/prefixes":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listPrefixes","tags":["Reverse DNS"],"summary":"List customer-owned prefixes","description":"The customer's **own** IPv4/IPv6 prefixes (PI/PA, BGP customers) for which PawDns\nhosts reverse DNS (DESIGN.md §7.2), with the reverse zones PawDns created for them and\nthe live delegation status of each zone. Prefixes are attached by PawHost staff or the\ncustomer's reseller; there is no create endpoint for customers.\n","security":[{"serviceToken":["rdns:read"]}],"parameters":[{"name":"family","in":"query","schema":{"type":"integer","enum":[4,6]}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of prefixes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrefixList"},"examples":{"page":{"$ref":"#/components/examples/PrefixListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/prefixes/{prefix}":{"parameters":[{"$ref":"#/components/parameters/PrefixId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getPrefix","tags":["Reverse DNS"],"summary":"Get a customer-owned prefix","security":[{"serviceToken":["rdns:read"]}],"responses":{"200":{"description":"The prefix with its reverse zones and delegation status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Prefix"},"examples":{"v4":{"$ref":"#/components/examples/PrefixV4"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/prefixes/{prefix}/delegation-check":{"parameters":[{"$ref":"#/components/parameters/PrefixId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"checkPrefixDelegation","tags":["Reverse DNS"],"summary":"Re-check the reverse delegation now","description":"Queries the parent zones (RIPE's `in-addr.arpa` / `ip6.arpa` servers) for every reverse\nzone of the prefix and updates `delegation`. Use it after the customer changed their\nRIPE `domain:` objects. Limited to one check per prefix per minute.\n","security":[{"serviceToken":["rdns:write"]}],"responses":{"200":{"description":"Check finished; updated prefix.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Prefix"},"examples":{"v4":{"$ref":"#/components/examples/PrefixV4"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listPtrs","tags":["Reverse DNS"],"summary":"List PTRs inside an owned range","description":"Lists the PTRs inside `within`, which must be inside an IP assignment or prefix of the\nacting tenant. For IPv4 every address of the range is listed (with `target: null` when\nunset, so the panel can render a full /24 grid); for IPv6 only addresses that have a\nPTR (set or default) are listed.\n","security":[{"serviceToken":["rdns:read"]}],"parameters":[{"name":"within","in":"query","required":true,"description":"CIDR inside an owned assignment or prefix. IPv4 at most a /22, IPv6 any size.","schema":{"type":"string"},"example":"198.51.100.0/24"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of PTRs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrList"},"examples":{"page":{"$ref":"#/components/examples/PtrListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr/{ip}":{"parameters":[{"$ref":"#/components/parameters/Ip"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getPtr","tags":["Reverse DNS"],"summary":"Get the PTR of an IP","description":"Any address inside an owned assignment or prefix. Otherwise `403 not_owner`.","security":[{"serviceToken":["rdns:read"]}],"responses":{"200":{"description":"The PTR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ptr"},"examples":{"custom":{"$ref":"#/components/examples/PtrCustom"},"default":{"$ref":"#/components/examples/PtrDefault"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`not_owner`: the IP is not assigned to the acting tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notOwner":{"$ref":"#/components/examples/ProblemNotOwner"}}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"put":{"operationId":"setPtr","tags":["Reverse DNS"],"summary":"Set the PTR of an IP","description":"Sets the PTR target. The target must be a valid host name (no IP, no wildcard). PawDns\nadditionally checks forward confirmation (does the target resolve back to this IP?)\nand reports the result in `forward_confirmed` and as a warning, but does not refuse\na non-confirming PTR. For a PTR in a customer-owned prefix the reverse zone is created\nat the backend on first use if needed.\n","security":[{"serviceToken":["rdns:write"]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrUpdate"},"examples":{"mail":{"summary":"Mail server PTR","value":{"target":"mail.example.com","ttl":3600}}}}}},"responses":{"200":{"description":"PTR stored; publication pending.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ptr"},"examples":{"pending":{"$ref":"#/components/examples/PtrPending"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`not_owner`, `insufficient_scope`, `tenant_suspended`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notOwner":{"$ref":"#/components/examples/ProblemNotOwner"}}}}},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"resetPtr","tags":["Reverse DNS"],"summary":"Reset the PTR of an IP","description":"In PawHost address space: resets the PTR to the default of its block (e.g.\n`192-0-2-10.pawhost.de` or `vps10452.cloud.yoursrv.de`, see `default_target`). Without a\ndefault (none configured for the block, or a customer-owned prefix): removes the PTR.\n","security":[{"serviceToken":["rdns:write"]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"200":{"description":"PTR reset; publication pending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ptr"},"examples":{"default":{"$ref":"#/components/examples/PtrDefault"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`not_owner`, `insufficient_scope`, `tenant_suspended`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notOwner":{"$ref":"#/components/examples/ProblemNotOwner"}}}}},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr/{ip}/archive":{"parameters":[{"$ref":"#/components/parameters/Ip"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listPtrArchive","tags":["Reverse DNS"],"summary":"Archived rDNS of an IP (release handling)","description":"When an IP is released (its VM, server or service is gone or cancelled), PawDns resets\nits custom PTR to the default after a grace period and archives the previous state first.\nThis lists those archive entries for one IP, newest first. Only entries whose owner at\narchive time is the acting tenant or one of its customers are listed (the platform sees\nall). `restore_available` is `true` when the archived customer uses the IP again; nothing\nis ever restored automatically, use `POST /ptr/{ip}/restore`. Additional endpoint; needs\nthe permission `rdns.archive.view` for tokens tied to a user.\n","security":[{"serviceToken":["rdns:read"]}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of archive entries (possibly empty).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrArchiveList"},"examples":{"page":{"$ref":"#/components/examples/PtrArchiveListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr/{ip}/restore":{"parameters":[{"$ref":"#/components/parameters/Ip"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"restorePtr","tags":["Reverse DNS"],"summary":"Restore archived rDNS onto an IP","description":"Writes the PTR of an archive entry (`archive_id`, from `GET /ptr/{ip}/archive`) onto\n`{ip}`: the archived IP itself or another IP the customer owns now. `{ip}` must be owned\nby the acting tenant (`403 not_owner`) and the archive entry must be visible to it\n(`404 not_found`). With `dry_run: true` nothing is written and the response is the\npreview. The PTR goes through the normal write path (publication pending, like\n`PUT /ptr/{ip}`). Descriptions and owners are restored in the admin UI only. Additional\nendpoint; needs the permission `rdns.restore` for tokens tied to a user.\n","security":[{"serviceToken":["rdns:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrRestoreRequest"},"examples":{"preview":{"summary":"Preview only","value":{"archive_id":"arc_12","dry_run":true}},"restore":{"summary":"Restore","value":{"archive_id":"arc_12"}}}}}},"responses":{"200":{"description":"Restored (or the preview with `dry_run`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrRestore"},"examples":{"restored":{"$ref":"#/components/examples/PtrRestoreDone"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`not_owner`, `insufficient_scope`, `tenant_suspended`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notOwner":{"$ref":"#/components/examples/ProblemNotOwner"}}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr-defaults":{"parameters":[{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listPtrDefaults","tags":["Reverse DNS"],"summary":"List the default rDNS templates per block","description":"Default PTR templates per block (`ptr_defaults`): what a reset (`DELETE /ptr/{ip}`, release\nhandling, \"apply default\") writes. The longest matching prefix wins; an entry with\n`no_default: true` carves a block out of a larger template. Ordered by address.\n**Platform only:** needs a token of the PawHost platform tenant that does not act for a\ncustomer; for everyone else the resource does not exist (`404 not_found`). A token tied to\na user also needs the permission `ipam.release.manage`. Config only: nothing here writes DNS.\n","security":[{"serviceToken":["rdns:read"]}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrDefaultList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"put":{"operationId":"setPtrDefault","tags":["Reverse DNS"],"summary":"Set or update the default rDNS template of a block","description":"Creates or replaces the entry of `prefix` (IPv4 /24 to /32, IPv6 any length). `template:\nnull` marks the block as \"no default\" (a reset there leaves the PTR alone). Placeholders:\n`{a}` `{b}` `{c}` `{d}` (IPv4 octets), `{ip-dashed}`, `{hex}` (32 IPv6 nibbles).\nAudit `rdns.default.set`.\n**Platform only:** needs a token of the PawHost platform tenant that does not act for a\ncustomer; for everyone else the resource does not exist (`404 not_found`). A token tied to\na user also needs the permission `ipam.release.manage`. Config only: nothing here writes DNS.\n","security":[{"serviceToken":["rdns:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrDefaultInput"},"examples":{"template":{"value":{"prefix":"192.0.2.0/24","template":"{a}.{b}.{c}.{d}.example.net","note":"TenantOS subnet"}},"noDefault":{"value":{"prefix":"192.0.2.40/29","template":null,"note":"OpenStack pool"}}}}}},"responses":{"200":{"description":"The entry was updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrDefault"}}}},"201":{"description":"The entry was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrDefault"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr-defaults/resolve":{"parameters":[{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"resolvePtrDefault","tags":["Reverse DNS"],"summary":"Which default applies to an IP","description":"Resolves the default PTR of one IP exactly as a reset would: customer-owned prefix → none;\nelse the longest matching entry (a \"no default\" entry ends the search); a template does not\ncross into a reseller's or customer's hand-set holding; without a template the OpenStack VM\ndefault while the VM exists.\n**Platform only:** needs a token of the PawHost platform tenant that does not act for a\ncustomer; for everyone else the resource does not exist (`404 not_found`). A token tied to\na user also needs the permission `ipam.release.manage`. Config only: nothing here writes DNS.\n","security":[{"serviceToken":["rdns:read"]}],"parameters":[{"name":"ip","in":"query","required":true,"description":"IPv4 or IPv6 address.","schema":{"type":"string","maxLength":64},"example":"192.0.2.10"}],"responses":{"200":{"description":"The resolved default (`target` null = none).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrDefaultResolution"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/ptr-defaults/{default}":{"parameters":[{"name":"default","in":"path","required":true,"description":"Template id (`ptd_…`).","schema":{"type":"string","pattern":"^ptd_[0-9]+$"},"example":"ptd_1"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getPtrDefault","tags":["Reverse DNS"],"summary":"Get one default rDNS template","description":"**Platform only:** needs a token of the PawHost platform tenant that does not act for a\ncustomer; for everyone else the resource does not exist (`404 not_found`). A token tied to\na user also needs the permission `ipam.release.manage`. Config only: nothing here writes DNS.\n","security":[{"serviceToken":["rdns:read"]}],"responses":{"200":{"description":"The entry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PtrDefault"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"deletePtrDefault","tags":["Reverse DNS"],"summary":"Delete a default rDNS template","description":"Removes the entry; the next larger entry (if any) applies to the block again. Audit\n`rdns.default.deleted`.\n**Platform only:** needs a token of the PawHost platform tenant that does not act for a\ncustomer; for everyone else the resource does not exist (`404 not_found`). A token tied to\na user also needs the permission `ipam.release.manage`. Config only: nothing here writes DNS.\n","security":[{"serviceToken":["rdns:write"]}],"responses":{"204":{"description":"Deleted."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/dyndns/hosts":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listDyndnsHosts","tags":["DynDNS"],"summary":"List DynDNS hosts","security":[{"serviceToken":["dyndns:read"]}],"parameters":[{"name":"zone","in":"query","description":"Only hosts in this zone.","schema":{"type":"string"},"example":"example.com"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of DynDNS hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DyndnsHostList"},"examples":{"page":{"$ref":"#/components/examples/DyndnsHostListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"createDyndnsHost","tags":["DynDNS"],"summary":"Create a DynDNS host","description":"Creates a DynDNS host for a name inside one of the acting tenant's zones. PawDns\nmanages the host's A and/or AAAA record (TTL = the backend minimum: 600 s at hosttech, 60 s in\nthe sandbox; see `ttl`); existing records of these types at\nthat name are taken over. A CNAME at that name is a conflict.\n\nThe response contains the **update token once**. Store or show it immediately; it\ncannot be read again (rotate it if lost). Counts against `max_dyndns_hosts`.\n","security":[{"serviceToken":["dyndns:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DyndnsHostCreate"},"examples":{"home":{"summary":"Home router, dual stack","value":{"hostname":"home.example.com","families":[4,6],"description":"FritzBox at home"}}}}}},"responses":{"201":{"description":"Host created; the token is returned once.","headers":{"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DyndnsHostWithToken"},"examples":{"created":{"$ref":"#/components/examples/DyndnsHostCreated"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"409":{"description":"`conflict` (host exists, CNAME at the name) or `quota_exceeded`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"quota":{"$ref":"#/components/examples/ProblemQuotaExceeded"}}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/dyndns/hosts/{host}":{"parameters":[{"$ref":"#/components/parameters/DyndnsHostId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getDyndnsHost","tags":["DynDNS"],"summary":"Get a DynDNS host","security":[{"serviceToken":["dyndns:read"]}],"responses":{"200":{"description":"The host, with the last update result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DyndnsHost"},"examples":{"host":{"$ref":"#/components/examples/DyndnsHostHome"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"deleteDyndnsHost","tags":["DynDNS"],"summary":"Delete a DynDNS host","description":"Deletes the host and invalidates its token. `keep_records=true` keeps the current A/AAAA\nrecords as normal records; otherwise they are deleted.\n","security":[{"serviceToken":["dyndns:write"]}],"parameters":[{"name":"keep_records","in":"query","schema":{"type":"boolean","default":false}}],"responses":{"204":{"description":"Deleted."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/dyndns/hosts/{host}/rotate-token":{"parameters":[{"$ref":"#/components/parameters/DyndnsHostId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"rotateDyndnsToken","tags":["DynDNS"],"summary":"Rotate the update token","description":"Issues a new token and invalidates the old one immediately. The new token is returned once.","security":[{"serviceToken":["dyndns:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"New token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DyndnsHostWithToken"},"examples":{"rotated":{"$ref":"#/components/examples/DyndnsHostCreated"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/nic/update":{"servers":[{"url":"https://manage.pawdns.de","description":"The dyndns2 endpoint is outside `/api/v1` so that routers and ddclient work unchanged."},{"url":"http://127.0.0.1:4010","description":"Local Prism mock server"}],"get":{"operationId":"dyndnsUpdate","tags":["DynDNS"],"summary":"dyndns2 update (routers, ddclient)","description":"dyndns2-compatible update. Authenticate with HTTP Basic auth, **user = host name,\npassword = the host's token**. Works with FritzBox (\"Benutzerdefiniert\"), ddclient\n(`protocol=dyndns2`), inadyn and most routers.\n\n```\nhttps://home.example.com:TOKEN@manage.pawdns.de/nic/update?hostname=home.example.com&myip=192.0.2.44&myipv6=2001:db8:44::1\n```\n\n- `myip` omitted: the source address of the request is used (v4 or v6).\n- `myip` present but empty (FritzBox without IPv4, e.g. DS-Lite): no IPv4 update.\n- `myip` may be IPv4 or IPv6, or both comma-separated (`myip=192.0.2.44,2001:db8::1`);\n  `myipv6` sets the AAAA separately. Only the families enabled on the host are\n  updated; others are ignored.\n- Routers that cannot send Basic auth may pass `username` and `password` as query\n  parameters instead (they then appear in proxy logs; prefer Basic auth).\n- The records get the backend's minimum TTL (600 s at hosttech, 60 s in the sandbox),\n  see `DyndnsHost.ttl`.\n- Several host names may be given comma-separated (each must use the same token\n  owner); the response then has one line per host, in order.\n- Updates closer together than `min_interval_seconds` (60 s) with a **changed**\n  address are answered `abuse` for that host. Unchanged addresses always get `nochg`.\n- The response is always `text/plain`. The status code is `200` except for missing\n  or wrong credentials (`401` + `badauth`, so that clients send Basic auth).\n\n| Return code | Meaning | Client should |\n|---|---|---|\n| `good <ip>` | updated; one line per host, both addresses space-separated when v4 and v6 were updated | continue |\n| `nochg <ip>` | address unchanged | continue, but not repeat too often |\n| `badauth` | wrong host name / token | stop and alert the user |\n| `notfqdn` | host name missing or not a FQDN | stop |\n| `nohost` | host name is not a DynDNS host of this token | stop |\n| `abuse` | updates too frequent, or host blocked | stop for at least 10 minutes |\n| `badagent` | request malformed (e.g. invalid `myip`) or user agent blocked | stop |\n| `dnserr` | internal DNS error | retry after 5+ minutes |\n| `911` | server maintenance or failure | retry after 5+ minutes |\n","security":[{"dyndnsBasic":[]}],"parameters":[{"name":"hostname","in":"query","required":true,"description":"FQDN of the DynDNS host, or several comma-separated.","schema":{"type":"string"},"example":"home.example.com"},{"name":"myip","in":"query","description":"New IPv4 (or IPv6) address. Defaults to the request's source address.","schema":{"type":"string"},"example":"192.0.2.44"},{"name":"myipv6","in":"query","description":"New IPv6 address.","schema":{"type":"string","format":"ipv6"},"example":"2001:db8:44::1"},{"name":"offline","in":"query","description":"`YES` removes the host's A/AAAA records until the next update.","schema":{"type":"string","enum":["YES","NO"]}}],"responses":{"200":{"description":"One line per host name.","content":{"text/plain":{"schema":{"type":"string","pattern":"^(good|nochg|notfqdn|nohost|abuse|badagent|dnserr|911)( .*)?(\\n(good|nochg|notfqdn|nohost|abuse|badagent|dnserr|911)( .*)?)*\\n?$"},"examples":{"good":{"summary":"Updated (v4 and v6)","value":"good 192.0.2.44 2001:db8:44::1"},"nochg":{"summary":"Unchanged","value":"nochg 192.0.2.44"},"nohost":{"summary":"Unknown host","value":"nohost"},"abuse":{"summary":"Too frequent","value":"abuse"}}}}},"401":{"description":"Missing or wrong credentials.","headers":{"WWW-Authenticate":{"description":"`Basic realm=\"PawDns DynDNS\"`","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","description":"`badauth`, newline-terminated like every dyndns2 answer.","pattern":"^badauth\\n?$"},"examples":{"badauth":{"value":"badauth"}}}}}}}},"/templates":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listTemplates","tags":["Templates"],"summary":"List available templates","description":"Templates the acting tenant may apply (PawHost system templates plus templates defined by its reseller).","security":[{"serviceToken":["zones:read"]}],"parameters":[{"name":"category","in":"query","schema":{"$ref":"#/components/schemas/TemplateCategory"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of templates (without records).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"},"examples":{"page":{"$ref":"#/components/examples/TemplateListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/templates/{template}":{"parameters":[{"$ref":"#/components/parameters/TemplateId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getTemplate","tags":["Templates"],"summary":"Get a template with its variables and records","security":[{"serviceToken":["zones:read"]}],"responses":{"200":{"description":"The template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"},"examples":{"mail":{"$ref":"#/components/examples/TemplateMail"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/audit":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listAuditEntries","tags":["Audit"],"summary":"List the tenant's audit entries","description":"Audit entries for the acting tenant's own objects (and, for reseller tokens without\n`X-Act-As-Customer`, its subtree), newest first. Kept for 365 days. Actions by\nPawHost staff appear with `actor.type: staff` and the display name \"PawHost Support\";\nstaff identities are not exposed.\n","security":[{"serviceToken":["audit:read"]}],"parameters":[{"name":"zone","in":"query","schema":{"type":"string"},"example":"example.com"},{"name":"action","in":"query","description":"Filter by action; prefix match with `*` (`record.*`).","schema":{"type":"string"},"example":"record.*"},{"name":"object_type","in":"query","schema":{"$ref":"#/components/schemas/AuditObjectType"}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of audit entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEntryList"},"examples":{"page":{"$ref":"#/components/examples/AuditListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/webhooks":{"parameters":[{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listWebhooks","tags":["Webhooks"],"summary":"List webhook endpoints","security":[{"serviceToken":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of webhooks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"},"examples":{"page":{"$ref":"#/components/examples/WebhookListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"createWebhook","tags":["Webhooks"],"summary":"Register a webhook endpoint","description":"Registers an HTTPS endpoint for the given event types. The signing `secret` is\nreturned **once**. PawDns sends a `ping` delivery right away; registration succeeds\nregardless of the ping result. Max 10 webhooks per tenant. URLs must be `https://`\nand must not resolve to private, loopback or link-local addresses.\n","security":[{"serviceToken":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreate"},"examples":{"panel":{"summary":"Panel listening to all events","value":{"url":"https://panel.example.com/hooks/pawdns","events":["zone.changed","record.published","record.failed","dnssec.state_changed","quota.warning"],"description":"Customer panel"}}}}}},"responses":{"201":{"description":"Webhook registered; the secret is returned once.","headers":{"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"},"examples":{"created":{"$ref":"#/components/examples/WebhookCreated"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ActAsUnknown"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/webhooks/{webhook}":{"parameters":[{"$ref":"#/components/parameters/WebhookId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"getWebhook","tags":["Webhooks"],"summary":"Get a webhook endpoint","security":[{"serviceToken":["webhooks:manage"]}],"responses":{"200":{"description":"The webhook.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"},"examples":{"webhook":{"$ref":"#/components/examples/WebhookPanel"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"operationId":"updateWebhook","tags":["Webhooks"],"summary":"Change a webhook endpoint","description":"Change URL, events, description, or re-enable a disabled webhook (`status: active`).","security":[{"serviceToken":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdate"},"examples":{"reenable":{"summary":"Re-enable after an outage","value":{"status":"active"}}}}}},"responses":{"200":{"description":"Updated webhook.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"},"examples":{"webhook":{"$ref":"#/components/examples/WebhookPanel"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"deleteWebhook","tags":["Webhooks"],"summary":"Delete a webhook endpoint","security":[{"serviceToken":["webhooks:manage"]}],"responses":{"204":{"description":"Deleted. Pending retries are dropped."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/webhooks/{webhook}/rotate-secret":{"parameters":[{"$ref":"#/components/parameters/WebhookId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"rotateWebhookSecret","tags":["Webhooks"],"summary":"Rotate the signing secret","description":"Issues a new secret (returned once). For 24 h deliveries are signed with both the old and the new secret.","security":[{"serviceToken":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"New secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"},"examples":{"rotated":{"$ref":"#/components/examples/WebhookCreated"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/webhooks/{webhook}/ping":{"parameters":[{"$ref":"#/components/parameters/WebhookId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"post":{"operationId":"pingWebhook","tags":["Webhooks"],"summary":"Send a test delivery","description":"Sends a `ping` event synchronously and returns the delivery result.","security":[{"serviceToken":["webhooks:manage"]}],"responses":{"200":{"description":"Delivery result (also for failed deliveries).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDelivery"},"examples":{"ok":{"$ref":"#/components/examples/WebhookDeliveryOk"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/webhooks/{webhook}/deliveries":{"parameters":[{"$ref":"#/components/parameters/WebhookId"},{"$ref":"#/components/parameters/ActAsCustomer"},{"$ref":"#/components/parameters/AcceptLanguage"}],"get":{"operationId":"listWebhookDeliveries","tags":["Webhooks"],"summary":"List recent deliveries","description":"Deliveries of the last 7 days, newest first, for debugging.","security":[{"serviceToken":["webhooks:manage"]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["succeeded","failed","retrying"]}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"A page of deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"},"examples":{"page":{"$ref":"#/components/examples/WebhookDeliveryListPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}}},"webhooks":{"zone.changed":{"post":{"operationId":"onZoneChanged","tags":["Webhooks"],"summary":"zone.changed","description":"A zone was created, deleted, its settings changed, or its records were changed (one\nevent per stored change set, not per record). Fired when the change is **stored**,\nbefore publication.\n","parameters":[{"$ref":"#/components/parameters/WebhookSignature"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookDeliveryHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneChangedEvent"},"examples":{"records":{"$ref":"#/components/examples/EventZoneChanged"}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery."},"410":{"description":"Gone — PawDns disables the webhook immediately."}}}},"record.published":{"post":{"operationId":"onRecordPublished","tags":["Webhooks"],"summary":"record.published","description":"A change set reached `pushed` (backend accepted) and again when it reached `live`\n(seen on all nameservers). `data.state` says which. Also fired for PTR changes.\n","parameters":[{"$ref":"#/components/parameters/WebhookSignature"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookDeliveryHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPublishedEvent"},"examples":{"live":{"$ref":"#/components/examples/EventRecordPublished"}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery."},"410":{"description":"Gone — PawDns disables the webhook immediately."}}}},"record.failed":{"post":{"operationId":"onRecordFailed","tags":["Webhooks"],"summary":"record.failed","description":"A change set could not be published (state `failed`).","parameters":[{"$ref":"#/components/parameters/WebhookSignature"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookDeliveryHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordFailedEvent"},"examples":{"failed":{"$ref":"#/components/examples/EventRecordFailed"}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery."},"410":{"description":"Gone — PawDns disables the webhook immediately."}}}},"dnssec.state_changed":{"post":{"operationId":"onDnssecStateChanged","tags":["Webhooks"],"summary":"dnssec.state_changed","description":"The zone's DNSSEC state machine moved to another state.","parameters":[{"$ref":"#/components/parameters/WebhookSignature"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookDeliveryHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnssecStateChangedEvent"},"examples":{"active":{"$ref":"#/components/examples/EventDnssecStateChanged"}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery."},"410":{"description":"Gone — PawDns disables the webhook immediately."}}}},"quota.warning":{"post":{"operationId":"onQuotaWarning","tags":["Webhooks"],"summary":"quota.warning","description":"A tenant quota crossed 80 % (`level: warning`) or 100 % (`level: exhausted`). Fired\nonce per crossing, not repeatedly; fired again after usage dropped below the threshold\nand crossed it again.\n","parameters":[{"$ref":"#/components/parameters/WebhookSignature"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookDeliveryHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaWarningEvent"},"examples":{"zones":{"$ref":"#/components/examples/EventQuotaWarning"}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery."},"410":{"description":"Gone — PawDns disables the webhook immediately."}}}}},"components":{"securitySchemes":{"serviceToken":{"type":"http","scheme":"bearer","bearerFormat":"pdns_live_… / pdns_test_…","description":"Service token of an API client. Scopes are listed per operation in its `security`\nrequirement and in the API description. Tokens are created by PawHost staff (for the\npanel and WHMCS modules) or by resellers for their own clients.\n"},"dyndnsBasic":{"type":"http","scheme":"basic","description":"Only for `/nic/update`: user = DynDNS host name, password = the host's update token."}},"parameters":{"ActAsCustomer":{"name":"X-Act-As-Customer","in":"header","required":false,"description":"Act for this customer (needs scope `act_as`). A PawDns tenant id (`ten_…`) or\n`whmcs:<client id>`. Must be a customer inside the token's subtree.\nA live platform token naming a WHMCS client that has no customer yet links that one\nclient on first use (see \"Acting for a customer\").\n","schema":{"type":"string","pattern":"^(ten_[A-Za-z0-9]+|whmcs:[0-9]+)$"},"examples":{"tenantId":{"value":"ten_4821"},"whmcs":{"value":"whmcs:1234"}}},"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Language of `title`/`detail` in errors and of human-readable hints. `de` or `en` (default).","schema":{"type":"string","default":"en"},"example":"de"},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for safe retries. Stored 24 h per API client + acting tenant.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"6f1c9a52-2a4b-4c3e-9d0e-8b7f4a1e2c33"},"IfMatch":{"name":"If-Match","in":"header","required":false,"description":"ETag from a previous read. On mismatch `412 precondition_failed`.","schema":{"type":"string"},"example":"\"r-7Hq2Lm-3\""},"IfNoneMatch":{"name":"If-None-Match","in":"header","required":false,"description":"ETag from a previous read. Unchanged gives `304`.","schema":{"type":"string"},"example":"\"z-4f1c2a-17\""},"Cursor":{"name":"cursor","in":"query","required":false,"description":"`pagination.next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJuIjoiZXhhbXBsZS5vcmcifQ"},"Limit":{"name":"limit","in":"query","required":false,"description":"Page size.","schema":{"type":"integer","minimum":1,"maximum":500,"default":50}},"ZoneName":{"name":"zone","in":"path","required":true,"description":"Zone name, lower case, no trailing dot (A-label for IDN).","schema":{"$ref":"#/components/schemas/ZoneName"},"example":"example.com"},"RecordId":{"name":"record","in":"path","required":true,"schema":{"type":"string","pattern":"^rec_[A-Za-z0-9]+$"},"example":"rec_7Hq2Lm"},"ChangeSetId":{"name":"changeset","in":"path","required":true,"schema":{"type":"string","pattern":"^chg_[A-Za-z0-9]+$"},"example":"chg_5Rt8Xw"},"PrefixId":{"name":"prefix","in":"path","required":true,"schema":{"type":"string","pattern":"^pfx_[A-Za-z0-9]+$"},"example":"pfx_3Kd9Qa"},"Ip":{"name":"ip","in":"path","required":true,"description":"IPv4 or IPv6 address (IPv6 in any valid notation; returned compressed).","schema":{"type":"string","anyOf":[{"format":"ipv4"},{"format":"ipv6"}]},"examples":{"v4":{"value":"192.0.2.10"},"v6":{"value":"2001:db8:1:2::10"}}},"DyndnsHostId":{"name":"host","in":"path","required":true,"schema":{"type":"string","pattern":"^dyn_[A-Za-z0-9]+$"},"example":"dyn_8Lw2Pe"},"TemplateId":{"name":"template","in":"path","required":true,"schema":{"type":"string","pattern":"^tpl_[a-z0-9_]+$"},"example":"tpl_mail_pawhost"},"WebhookId":{"name":"webhook","in":"path","required":true,"schema":{"type":"string","pattern":"^whk_[A-Za-z0-9]+$"},"example":"whk_2Fh7Nc"},"WebhookSignature":{"name":"PawDns-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<body>\")>`; two `v1` during secret rotation.","schema":{"type":"string"},"example":"t=1791711000,v1=5f2b8c0e9a7d41c3b6e2f08d9a1c4e7b3d6f9a2c5e8b1d4f7a0c3e6b9d2f5a8c"},"WebhookEventHeader":{"name":"PawDns-Event","in":"header","required":true,"schema":{"$ref":"#/components/schemas/WebhookEventType"},"example":"record.published"},"WebhookDeliveryHeader":{"name":"PawDns-Delivery","in":"header","required":true,"description":"Delivery id; equal to the event `id`, stable across retries.","schema":{"type":"string"},"example":"evt_9Qm4Ts"}},"headers":{"ETag":{"description":"Entity tag of the returned zone or record.","schema":{"type":"string"},"example":"\"r-7Hq2Lm-3\""},"Location":{"description":"URL of the created resource.","schema":{"type":"string","format":"uri-reference"},"example":"/api/v1/zones/example.com/records/rec_7Hq2Lm"},"Link":{"description":"RFC 8288 link to the next page.","schema":{"type":"string"},"example":"</api/v1/zones?cursor=eyJuIjoiZXhhbXBsZS5vcmcifQ&limit=50>; rel=\"next\""},"IdempotentReplayed":{"description":"`true` when this is a stored response replayed for a repeated `Idempotency-Key`.","schema":{"type":"boolean"}},"RateLimitLimit":{"description":"Requests allowed per window.","schema":{"type":"integer"},"example":600},"RateLimitRemaining":{"description":"Requests left in the current window.","schema":{"type":"integer"},"example":588},"RateLimitReset":{"description":"Seconds until the window resets.","schema":{"type":"integer"},"example":42},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"},"example":17},"XRequestId":{"description":"Request id for support.","schema":{"type":"string"},"example":"req_01JA4Z6W3K8N2Q"},"ContentLanguage":{"description":"Language of `title`/`detail`.","schema":{"type":"string","enum":["de","en"]}}},"responses":{"NotModified":{"description":"Not modified (`If-None-Match` matched)."},"Unauthorized":{"description":"`unauthenticated` (no token) or `invalid_token` (unknown, expired or revoked).","headers":{"WWW-Authenticate":{"description":"`Bearer realm=\"PawDns\"`","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"invalidToken":{"$ref":"#/components/examples/ProblemInvalidToken"}}}}},"Forbidden":{"description":"`insufficient_scope`, `not_owner`, `act_as_not_allowed`, `tenant_suspended`,\n`feature_disabled`, `zone_protected`, `zone_external`, `record_readonly`.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"scope":{"$ref":"#/components/examples/ProblemInsufficientScope"},"notOwner":{"$ref":"#/components/examples/ProblemNotOwner"}}}}},"NotFound":{"description":"`not_found` or `act_as_unknown`. Objects of other tenants are reported as\n`not_found`, never as `not_owner`, so their existence is not revealed — except for\nIPs (`not_owner`), whose existence is public anyway.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notFound":{"$ref":"#/components/examples/ProblemNotFound"}}}}},"ActAsUnknown":{"description":"`act_as_unknown`: the `X-Act-As-Customer` header names no customer in the token's\nsubtree (unknown tenant id or WHMCS client id; for platform tokens: a WHMCS client\nthat does not exist in WHMCS or is closed).\n","headers":{"Content-Language":{"$ref":"#/components/headers/ContentLanguage"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"actAsUnknown":{"$ref":"#/components/examples/ProblemActAsUnknown"}}}}},"Conflict":{"description":"`conflict`, `quota_exceeded`, `idempotency_in_progress` or a more specific conflict code.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"conflict":{"$ref":"#/components/examples/ProblemConflict"},"quota":{"$ref":"#/components/examples/ProblemQuotaExceeded"}}}}},"PreconditionFailed":{"description":"`precondition_failed`: the `If-Match` ETag (or an item's `if_match`) is stale.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"stale":{"$ref":"#/components/examples/ProblemPreconditionFailed"}}}}},"ValidationFailed":{"description":"`validation_failed` with field errors, or `idempotency_key_reused`.","headers":{"Content-Language":{"$ref":"#/components/headers/ContentLanguage"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation":{"$ref":"#/components/examples/ProblemValidation"}}}}},"PayloadTooLarge":{"description":"`payload_too_large` (import > 1 MiB, change set > 1000 items).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"tooLarge":{"$ref":"#/components/examples/ProblemPayloadTooLarge"}}}}},"TooManyRequests":{"description":"`rate_limited`.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rateLimited":{"$ref":"#/components/examples/ProblemRateLimited"}}}}},"NotImplemented":{"description":"`not_implemented`: not available yet (DNSSEC transitions until the registrar integration exists).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"notImplemented":{"$ref":"#/components/examples/ProblemNotImplemented"}}}}},"ServiceUnavailable":{"description":"`backend_capacity_exhausted` (no new zones possible right now) or `maintenance`.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"capacity":{"$ref":"#/components/examples/ProblemBackendCapacity"}}}}}},"schemas":{"ZoneName":{"type":"string","description":"Zone name, lower case, A-label, no trailing dot. 2+ labels.","minLength":3,"maxLength":253,"pattern":"^(?=.{3,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+(?:[a-z]{2,63}|xn--[a-z0-9-]{1,59}|arpa)$","examples":["example.com","shop.example.de","100.51.198.in-addr.arpa"]},"Hostname":{"type":"string","description":"Target host name (FQDN). Returned without trailing dot; a trailing dot on input is\naccepted and stripped. Labels 1–63 chars of letters, digits, `-` (and `_` for\nservice-style names), total ≤ 253. Never an IP address.\n","minLength":1,"maxLength":254,"pattern":"^(?=.{1,254}$)([A-Za-z0-9_](?:[A-Za-z0-9_-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z][A-Za-z0-9-]{0,61}[A-Za-z0-9]\\.?$","examples":["mail.example.com"]},"RecordName":{"type":"string","description":"Owner name **relative to the zone**. `@` (or an empty string) is the apex and is always\nreturned as `@`. Labels 1–63 chars of letters, digits, `-` and `_`; the FQDN must not\nexceed 253 chars. A leading `*.` label (or just `*`) makes a wildcard; wildcards are\nallowed for A, AAAA, CNAME, MX, TXT and CAA. Names are case-insensitive and returned in\nlower case. Do not append the zone name.\n","maxLength":253,"pattern":"^(@|\\*|(\\*\\.)?[A-Za-z0-9_]([A-Za-z0-9_-]{0,61}[A-Za-z0-9_])?(\\.[A-Za-z0-9_]([A-Za-z0-9_-]{0,61}[A-Za-z0-9_])?)*)?$","examples":["@","www","_dmarc","*.dev","_25._tcp.mail"]},"Ttl":{"type":["integer","null"],"description":"TTL in seconds, up to 86400. The minimum depends on the zone's backend: currently\n**600** for production zones (hosttech) and 60 for sandbox zones; `/me`\n`tenant.policies.ttl_min` gives it. Lower values give `out_of_range` with the minimum\nin `detail`. `null` = use the zone's `default_ttl`.\n","minimum":60,"maximum":86400,"examples":[3600]},"Comment":{"type":["string","null"],"maxLength":255,"description":"Free text, stored in PawDns only (not published in DNS)."},"Timestamp":{"type":"string","format":"date-time","examples":["2026-10-11T09:30:00Z"]},"Pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` on the last page."},"has_more":{"type":"boolean"},"total":{"type":"integer","description":"Total count; only on `/zones`."}}},"TenantRef":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","examples":["ten_4821"]},"name":{"type":"string","examples":["Example GmbH"]}}},"TenantType":{"type":"string","enum":["platform","reseller","customer"]},"Tenant":{"type":"object","required":["id","type","name","status","limits","usage","branding","features","nameservers"],"properties":{"id":{"type":"string"},"type":{"$ref":"#/components/schemas/TenantType"},"name":{"type":"string"},"status":{"type":"string","enum":["active","suspended"],"description":"`suspended`: published DNS stays, but every write gives `403 tenant_suspended`."},"whmcs_client_id":{"type":["integer","null"],"description":"Only for direct PawHost customers."},"parent":{"oneOf":[{"$ref":"#/components/schemas/TenantRef"},{"type":"null"}],"description":"The reseller (or platform) above this tenant."},"limits":{"$ref":"#/components/schemas/TenantLimits"},"usage":{"$ref":"#/components/schemas/TenantUsage"},"branding":{"$ref":"#/components/schemas/TenantBranding"},"features":{"$ref":"#/components/schemas/TenantFeatures"},"nameservers":{"type":"array","description":"The nameservers customers must use at their registrar (vanity NS for white-label resellers).","items":{"$ref":"#/components/schemas/Hostname"}},"policies":{"type":"object","properties":{"dnssec_automatic":{"type":"boolean","description":"New zones get DNSSEC enabled automatically."},"ttl_min":{"type":"integer","examples":[60]},"ttl_max":{"type":"integer","examples":[86400]}}}}},"TenantLimits":{"type":"object","description":"`null` = unlimited.","required":["max_zones","max_records_per_zone","max_dyndns_hosts","max_webhooks","api_rate_limit_per_minute"],"properties":{"max_zones":{"type":["integer","null"]},"max_records_per_zone":{"type":["integer","null"]},"max_dyndns_hosts":{"type":["integer","null"]},"max_webhooks":{"type":["integer","null"]},"api_rate_limit_per_minute":{"type":["integer","null"]}}},"TenantUsage":{"type":"object","required":["zones","dyndns_hosts","webhooks","records_total","largest_zone"],"properties":{"zones":{"type":"integer"},"dyndns_hosts":{"type":"integer"},"webhooks":{"type":"integer"},"records_total":{"type":"integer"},"largest_zone":{"type":["object","null"],"description":"The zone with the most records, against `max_records_per_zone`.","properties":{"name":{"$ref":"#/components/schemas/ZoneName"},"records":{"type":"integer"}}},"warnings":{"type":"array","description":"Quotas at or above 80 %.","items":{"type":"object","properties":{"quota":{"$ref":"#/components/schemas/QuotaName"},"used":{"type":"integer"},"limit":{"type":"integer"},"level":{"type":"string","enum":["warning","exhausted"]}}}}}},"QuotaName":{"type":"string","enum":["zones","records_per_zone","dyndns_hosts","webhooks"]},"TenantBranding":{"type":"object","description":"White-label data for the panel. For direct PawHost customers this is PawHost's branding.","properties":{"name":{"type":"string"},"logo_url":{"type":["string","null"],"format":"uri"},"colors":{"type":"object","properties":{"primary":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"accent":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}}},"support_email":{"type":["string","null"],"format":"email"},"support_url":{"type":["string","null"],"format":"uri"},"panel_domain":{"type":["string","null"],"description":"Custom UI domain of a white-label reseller."}}},"TenantFeatures":{"type":"object","description":"What the acting tenant may use. Hide panel areas whose feature is `false`; the API answers `403 feature_disabled` anyway.","required":["zone_create","zone_delete","import_export","dnssec","dyndns","templates","rdns","customer_prefixes","webhooks","audit"],"properties":{"zone_create":{"type":"boolean"},"zone_delete":{"type":"boolean"},"import_export":{"type":"boolean"},"dnssec":{"type":"boolean"},"dyndns":{"type":"boolean"},"templates":{"type":"boolean"},"rdns":{"type":"boolean","description":"The tenant has IP assignments (PawHost space)."},"customer_prefixes":{"type":"boolean","description":"The tenant has own prefixes."},"webhooks":{"type":"boolean"},"audit":{"type":"boolean"}}},"Me":{"type":"object","required":["tenant","caller"],"properties":{"tenant":{"$ref":"#/components/schemas/Tenant"},"caller":{"type":"object","required":["api_client","token_tenant","scopes","acting_as"],"properties":{"api_client":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"token_tenant":{"$ref":"#/components/schemas/TenantRef"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/Scope"}},"acting_as":{"type":"boolean","description":"`true` when `X-Act-As-Customer` is in effect."},"sandbox":{"type":"boolean"}}}}},"Stats":{"type":"object","description":"Dashboard numbers of the acting subtree, cached for about 60 seconds.","required":["scope","generated_at","zones","records","ip","dyndns","webhooks","api_tokens"],"properties":{"scope":{"type":"string","enum":["platform","tenant"],"description":"`platform` for a token of the platform tenant (everything), else `tenant` (the acting subtree)."},"generated_at":{"type":"string","format":"date-time","description":"When the numbers were computed."},"zones":{"type":"object","required":["forward","reverse","total"],"properties":{"forward":{"type":"integer","minimum":0},"reverse":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0}}},"records":{"type":"object","required":["total","by_type"],"properties":{"total":{"type":"integer","minimum":0},"by_type":{"type":"object","description":"Record count per type, e.g. `{\"A\": 12, \"MX\": 2}`.","additionalProperties":{"type":"integer","minimum":0}}}},"ip":{"type":"object","required":["networks","ipv4_addresses","ptr"],"properties":{"networks":{"type":"integer","minimum":0,"description":"Networks, ranges and single IPs the subtree holds (platform: all IPAM subnets)."},"ipv4_addresses":{"type":"integer","minimum":0,"description":"IPv4 addresses in them (nested assignments counted once)."},"ptr":{"$ref":"#/components/schemas/StatsPtrCounts"},"ipv6":{"$ref":"#/components/schemas/StatsIpv6"}}},"hosttech_quota":{"type":"object","description":"Only with `scope: platform`. hosttech zone quota from the last successful reading (refreshed every 15 minutes, never a live call); `accounts` is empty before the first reading.","required":["accounts","pawdns_zones"],"properties":{"accounts":{"type":"array","items":{"type":"object","required":["account","zones_used","zones_max","usage_pct","level","fetched_at"],"properties":{"account":{"type":"string","example":"hosttech default"},"zones_used":{"type":"integer","minimum":0},"zones_max":{"type":["integer","null"],"minimum":0},"usage_pct":{"type":["integer","null"],"minimum":0},"level":{"type":"string","enum":["ok","warn","crit","unknown"],"description":"`warn` / `crit` from the configured zone thresholds."},"fetched_at":{"type":["string","null"],"format":"date-time"}}}},"pawdns_zones":{"type":"integer","minimum":0,"description":"Zones PawDNS itself keeps at hosttech (the quota also counts zones of other writers)."}}},"dyndns":{"type":"object","required":["hosts","changes_24h"],"properties":{"hosts":{"type":"integer","minimum":0},"changes_24h":{"type":"integer","minimum":0,"description":"Address changes applied by DynDNS updates in the last 24 hours."}}},"webhooks":{"type":"object","required":["active","failed_24h"],"properties":{"active":{"type":"integer","minimum":0},"failed_24h":{"type":"integer","minimum":0,"description":"Deliveries that failed finally in the last 24 hours."}}},"api_tokens":{"type":"object","required":["active"],"properties":{"active":{"type":"integer","minimum":0,"description":"Tokens that are not revoked."}}}}},"StatsPtrCounts":{"type":"object","required":["total","default","custom"],"description":"PTR records in that space; `default` = a default name (prefix template, OpenStack VM name, or a name containing the address).","properties":{"total":{"type":"integer","minimum":0},"default":{"type":"integer","minimum":0},"custom":{"type":"integer","minimum":0}}},"StatsIpv6Assigned":{"type":"object","description":"ip_owners rows of one source that are in use (used or reserved), by prefix length.","required":["prefixes_48","prefixes_56","prefixes_64","prefixes_other","addresses"],"properties":{"prefixes_48":{"type":"integer","minimum":0},"prefixes_56":{"type":"integer","minimum":0},"prefixes_64":{"type":"integer","minimum":0},"prefixes_other":{"type":"integer","minimum":0},"addresses":{"type":"integer","minimum":0,"description":"Single addresses (/128)."}}},"StatsIpv6":{"type":"object","description":"Only with `scope: platform`. IPv6 is counted in prefixes, never as an address percentage.","required":["networks","assigned","ptr"],"properties":{"networks":{"type":"object","required":["total","top_level","by_source","by_size"],"properties":{"total":{"type":"integer","minimum":0},"top_level":{"type":"integer","minimum":0},"by_source":{"type":"object","required":["tenantos","openstack","manual"],"description":"A network reported by several sources counts for each.","properties":{"tenantos":{"type":"integer","minimum":0},"openstack":{"type":"integer","minimum":0},"manual":{"type":"integer","minimum":0}}},"by_size":{"type":"object","description":"Networks per prefix length, e.g. `{\"/48\": 37, \"/56\": 683}`.","additionalProperties":{"type":"integer","minimum":0}}}},"assigned":{"type":"object","required":["tenantos","openstack","whmcs"],"properties":{"tenantos":{"$ref":"#/components/schemas/StatsIpv6Assigned"},"openstack":{"$ref":"#/components/schemas/StatsIpv6Assigned"},"whmcs":{"$ref":"#/components/schemas/StatsIpv6Assigned"}}},"ptr":{"$ref":"#/components/schemas/StatsPtrCounts"}}},"Scope":{"type":"string","enum":["tenant:read","zones:read","zones:write","records:read","records:write","dnssec:read","dnssec:write","rdns:read","rdns:write","dyndns:read","dyndns:write","audit:read","webhooks:manage","act_as"]},"ZoneKind":{"type":"string","enum":["forward","reverse"]},"ZoneStatus":{"type":"string","enum":["activating","active","deleting","suspended"],"description":"- `activating`: created, waiting until the nameservers answer for it (usually < 15 min)\n- `active`: normal\n- `deleting`: deletion running (DS removal first, see `deleteZone`)\n- `suspended`: tenant suspended; published but read-only\n"},"PublicationState":{"type":"string","enum":["pending","pushed","live","failed"],"description":"See \"Publication\" in the API description."},"ZonePublicationState":{"type":"string","enum":["live","publishing","failed"],"description":"`live`: every change set is live. `publishing`: some are `pending`/`pushed`. `failed`: at least one change set failed."},"ZonePublication":{"type":"object","required":["state","pending_changesets","failed_changesets"],"properties":{"state":{"$ref":"#/components/schemas/ZonePublicationState"},"pending_changesets":{"type":"integer","description":"Change sets in `pending` or `pushed`."},"failed_changesets":{"type":"integer"},"last_live_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]}}},"Delegation":{"type":"object","description":"Do the parent's NS for this domain point at our nameservers? Checked every 6 h and on demand.","required":["status"],"properties":{"status":{"type":"string","enum":["ok","partial","not_delegated","unknown"],"description":"`partial`: some, but not all, NS are ours. `unknown`: not checked yet or the lookup failed."},"expected":{"type":"array","items":{"$ref":"#/components/schemas/Hostname"}},"observed":{"type":"array","items":{"$ref":"#/components/schemas/Hostname"}},"checked_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]}}},"Zone":{"type":"object","required":["name","kind","status","tenant","default_ttl","soa","nameservers","record_count","dnssec","publication","delegation","created_at","updated_at","etag"],"properties":{"name":{"$ref":"#/components/schemas/ZoneName"},"display_name":{"type":"string","description":"U-label form for IDN zones, otherwise equal to `name`."},"kind":{"$ref":"#/components/schemas/ZoneKind"},"status":{"$ref":"#/components/schemas/ZoneStatus"},"tenant":{"$ref":"#/components/schemas/TenantRef"},"default_ttl":{"type":"integer","minimum":60,"maximum":86400},"soa":{"type":"object","readOnly":true,"properties":{"mname":{"$ref":"#/components/schemas/Hostname"},"rname":{"type":"string","format":"email","description":"SOA contact as an e-mail address."},"refresh":{"type":"integer"},"retry":{"type":"integer"},"expire":{"type":"integer"},"minimum":{"type":"integer"}}},"nameservers":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/Hostname"}},"record_count":{"type":"integer"},"prefix_id":{"type":["string","null"],"description":"For reverse zones of a customer-owned prefix."},"dnssec":{"type":"object","required":["state"],"properties":{"state":{"$ref":"#/components/schemas/DnssecState"},"mode":{"oneOf":[{"$ref":"#/components/schemas/DnssecMode"},{"type":"null"}]}}},"publication":{"$ref":"#/components/schemas/ZonePublication"},"delegation":{"$ref":"#/components/schemas/Delegation"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"},"etag":{"type":"string","description":"Same value as the `ETag` header."}}},"ZoneList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Zone"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"ZoneCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Zone name. U-labels are accepted and converted; a trailing dot is stripped.","maxLength":254,"examples":["example.com"]},"default_ttl":{"type":"integer","minimum":60,"maximum":86400,"default":3600,"description":"Minimum depends on the backend (600 in production, 60 in the sandbox), see `Ttl`."},"soa_email":{"type":"string","format":"email","description":"Defaults to `hostmaster@<zone>`."},"dnssec":{"type":"boolean","description":"Enable DNSSEC right away. Default from the tenant policy `dnssec_automatic`."},"template":{"type":"object","required":["template_id"],"properties":{"template_id":{"type":"string"},"variables":{"type":"object","additionalProperties":{"type":"string"}}}},"records":{"type":"array","maxItems":1000,"description":"Initial records, applied in the same atomic change set as the template.","items":{"$ref":"#/components/schemas/RecordInput"}}}},"ZoneUpdate":{"type":"object","minProperties":1,"properties":{"default_ttl":{"type":"integer","minimum":60,"maximum":86400},"soa_email":{"type":"string","format":"email"}}},"ZoneImportRequest":{"type":"object","required":["zonefile"],"properties":{"zonefile":{"type":"string","maxLength":1048576,"description":"BIND zone file content."},"mode":{"type":"string","enum":["merge","replace"],"default":"merge"},"dry_run":{"type":"boolean","default":false},"comment":{"type":"string","maxLength":255}}},"ZoneImportUpload":{"type":"object","required":["file"],"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","description":"The zone file (max 1 MiB, UTF-8 or ASCII)."},"mode":{"type":"string","enum":["merge","replace"],"default":"merge"},"dry_run":{"type":"boolean","default":false}}},"ZoneImportResult":{"type":"object","required":["dry_run","parsed","diff","skipped"],"properties":{"dry_run":{"type":"boolean"},"parsed":{"type":"integer","description":"Records parsed from the file."},"diff":{"type":"array","items":{"$ref":"#/components/schemas/DiffEntry"}},"skipped":{"type":"array","items":{"type":"object","required":["line","reason"],"properties":{"line":{"type":"integer"},"text":{"type":"string"},"reason":{"type":"string","enum":["soa","apex_ns","dnssec_record","unsupported_type","duplicate"]}}}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/ValidationWarning"}},"changeset":{"oneOf":[{"$ref":"#/components/schemas/ChangeSet"},{"type":"null"}],"description":"The stored change set; `null` on dry runs."}}},"RecordType":{"type":"string","enum":["A","AAAA","CNAME","MX","TXT","SRV","CAA","TLSA","NS","PTR"]},"RecordInput":{"description":"A record as sent by clients: `type`, `name`, `ttl`, `comment` and a type-specific\n`data` object.\n\n**Rules that apply to every type**\n- The tenant's `max_records_per_zone` (`quota_exceeded`).\n- Exact duplicates (same name, type and data) are rejected (`duplicate_record`).\n- Names at or below a delegation (NS record at a non-apex name) may only carry\n  glue (A/AAAA for in-bailiwick nameserver names); anything else is\n  `occluded_by_delegation`.\n- Names inside a protected name (e.g. a zone's `pawhost.de` slice) are never\n  writable (`name_protected`).\n- Records of one RRset (same name and type) should share one TTL; differing TTLs\n  give the warning `rrset_ttl_mismatch` (not an error).\n- In reverse zones only PTR and NS are allowed; PTR only in reverse zones\n  (`wrong_zone_kind`).\n","oneOf":[{"$ref":"#/components/schemas/ARecord"},{"$ref":"#/components/schemas/AaaaRecord"},{"$ref":"#/components/schemas/CnameRecord"},{"$ref":"#/components/schemas/MxRecord"},{"$ref":"#/components/schemas/TxtRecord"},{"$ref":"#/components/schemas/SrvRecord"},{"$ref":"#/components/schemas/CaaRecord"},{"$ref":"#/components/schemas/TlsaRecord"},{"$ref":"#/components/schemas/NsRecord"},{"$ref":"#/components/schemas/PtrRecord"}],"discriminator":{"propertyName":"type","mapping":{"A":"#/components/schemas/ARecord","AAAA":"#/components/schemas/AaaaRecord","CNAME":"#/components/schemas/CnameRecord","MX":"#/components/schemas/MxRecord","TXT":"#/components/schemas/TxtRecord","SRV":"#/components/schemas/SrvRecord","CAA":"#/components/schemas/CaaRecord","TLSA":"#/components/schemas/TlsaRecord","NS":"#/components/schemas/NsRecord","PTR":"#/components/schemas/PtrRecord"}}},"RecordCommon":{"type":"object","required":["type","name","data"],"properties":{"type":{"$ref":"#/components/schemas/RecordType"},"name":{"$ref":"#/components/schemas/RecordName"},"ttl":{"$ref":"#/components/schemas/Ttl"},"comment":{"$ref":"#/components/schemas/Comment"}}},"ARecord":{"title":"A","description":"IPv4 address. Wildcards allowed. Not at a name that has a CNAME.","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"A"},"data":{"$ref":"#/components/schemas/AData"}}}]},"AData":{"type":"object","required":["address"],"properties":{"address":{"type":"string","format":"ipv4","description":"Dotted quad, no leading zeros.","examples":["192.0.2.10"]}}},"AaaaRecord":{"title":"AAAA","description":"IPv6 address. Wildcards allowed. Not at a name that has a CNAME. IPv4-mapped addresses are rejected.","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"AAAA"},"data":{"$ref":"#/components/schemas/AaaaData"}}}]},"AaaaData":{"type":"object","required":["address"],"properties":{"address":{"type":"string","format":"ipv6","description":"Any valid notation on input; returned in RFC 5952 compressed form.","examples":["2001:db8:10::10"]}}},"CnameRecord":{"title":"CNAME","description":"Alias. **Constraints:**\n- never at the zone apex (`cname_at_apex`; use A/AAAA at the apex instead)\n- no other record of any type at the same name, and only one CNAME per name\n  (`cname_conflict`)\n- the target must not be the record's own name (`cname_loop`)\n- not at a DynDNS host name\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"CNAME"},"data":{"$ref":"#/components/schemas/CnameData"}}}]},"CnameData":{"type":"object","required":["target"],"properties":{"target":{"$ref":"#/components/schemas/Hostname"}}},"MxRecord":{"title":"MX","description":"Mail exchanger. `exchange` must be a host name, never an IP. A target that is a CNAME\ngives the warning `target_is_cname`. Null MX (RFC 7505) is `preference: 0,\nexchange: \".\"` and must be the only MX at that name.\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"MX"},"data":{"$ref":"#/components/schemas/MxData"}}}]},"MxData":{"type":"object","required":["preference","exchange"],"properties":{"preference":{"type":"integer","minimum":0,"maximum":65535,"examples":[10]},"exchange":{"oneOf":[{"$ref":"#/components/schemas/Hostname"},{"type":"string","const":"."}]}}},"TxtRecord":{"title":"TXT","description":"Text. `text` is the **logical** value, unquoted and not chunked: send\n`v=spf1 mx -all`, not `\"v=spf1 mx -all\"`. Values longer than 255 bytes (DKIM keys) are\nsupported up to 4096 bytes; PawDns splits them into 255-byte strings when publishing\n(`chunks` in the response shows the split). Long TXT is published through a slower\nbackend path and may take a few minutes longer (warning `long_txt_slow_path`).\nPrintable UTF-8; control characters except tab are rejected.\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"TXT"},"data":{"$ref":"#/components/schemas/TxtData"}}}]},"TxtData":{"type":"object","required":["text"],"properties":{"text":{"type":"string","minLength":0,"maxLength":4096,"description":"Logical value; max 4096 bytes in UTF-8.","examples":["v=spf1 mx -all"]},"chunks":{"type":"array","readOnly":true,"description":"The 255-byte character-strings as published.","items":{"type":"string","maxLength":255}}}},"SrvRecord":{"title":"SRV","description":"Service locator. `name` must start with `_service._proto` (`_sip._tcp`,\n`_xmpp-client._tcp.chat`), proto `_tcp`, `_udp`, `_tls` or `_sctp`\n(`invalid_srv_name`). `target: \".\"` means \"service not available\".\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"SRV"},"name":{"type":"string","pattern":"^_[A-Za-z0-9-]{1,62}\\._(tcp|udp|tls|sctp)(\\.[A-Za-z0-9_]([A-Za-z0-9_-]{0,61}[A-Za-z0-9_])?)*$"},"data":{"$ref":"#/components/schemas/SrvData"}}}]},"SrvData":{"type":"object","required":["priority","weight","port","target"],"properties":{"priority":{"type":"integer","minimum":0,"maximum":65535},"weight":{"type":"integer","minimum":0,"maximum":65535},"port":{"type":"integer","minimum":0,"maximum":65535},"target":{"oneOf":[{"$ref":"#/components/schemas/Hostname"},{"type":"string","const":"."}]}}},"CaaRecord":{"title":"CAA","description":"Certification Authority Authorization (RFC 8659). `flags` is 0 or 128 (critical).\n`issue`/`issuewild` values are a CA domain optionally followed by parameters\n(`letsencrypt.org; validationmethods=dns-01`), or `;` to forbid issuance. `iodef` is a\n`mailto:` or `https:` URL.\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"CAA"},"data":{"$ref":"#/components/schemas/CaaData"}}}]},"CaaData":{"type":"object","required":["flags","tag","value"],"properties":{"flags":{"type":"integer","enum":[0,128]},"tag":{"type":"string","enum":["issue","issuewild","iodef","issuemail","issuevmc","contactemail","contactphone"]},"value":{"type":"string","minLength":1,"maxLength":255,"description":"Unquoted.","examples":["letsencrypt.org"]}}},"TlsaRecord":{"title":"TLSA","description":"DANE (RFC 6698). `name` must be `_<port>._<proto>[.<host>]` (`invalid_tlsa_name`).\n`certificate_data` is hex; with `matching_type` 1 it must be 64 hex chars (SHA-256),\nwith 2 it must be 128 (SHA-512), with 0 at most 4096 hex chars (`invalid_tlsa_data`).\nReturned in lower case.\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"TLSA"},"name":{"type":"string","pattern":"^_[0-9]{1,5}\\._(tcp|udp|sctp)(\\.[A-Za-z0-9_]([A-Za-z0-9_-]{0,61}[A-Za-z0-9_])?)*$"},"data":{"$ref":"#/components/schemas/TlsaData"}}}]},"TlsaData":{"type":"object","required":["usage","selector","matching_type","certificate_data"],"properties":{"usage":{"type":"integer","enum":[0,1,2,3],"description":"0 PKIX-TA, 1 PKIX-EE, 2 DANE-TA, 3 DANE-EE."},"selector":{"type":"integer","enum":[0,1],"description":"0 full certificate, 1 SubjectPublicKeyInfo."},"matching_type":{"type":"integer","enum":[0,1,2],"description":"0 exact, 1 SHA-256, 2 SHA-512."},"certificate_data":{"type":"string","pattern":"^[0-9A-Fa-f]{2,4096}$"}}},"NsRecord":{"title":"NS","description":"**Delegation of a subdomain only.** NS at the apex are managed by PawDns and are\nread-only (`ns_at_apex`). A delegated name may carry no other records except glue\n(`occluded_by_delegation`). If the nameserver lies inside the delegated name (in\nbailiwick), A/AAAA glue at that name is required (`glue_missing`). In reverse zones of\ncustomer-owned prefixes, NS may delegate parts of the prefix but never outside it\n(`outside_prefix`).\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"NS"},"data":{"$ref":"#/components/schemas/NsData"}}}]},"NsData":{"type":"object","required":["nameserver"],"properties":{"nameserver":{"$ref":"#/components/schemas/Hostname"}}},"PtrRecord":{"title":"PTR","description":"Only in reverse zones (`wrong_zone_kind`). `name` is the reverse label(s) relative to\nthe zone, e.g. `10` in `100.51.198.in-addr.arpa`. Usually it is easier to use\n`/ptr/{ip}`, which takes a plain IP address.\n","allOf":[{"$ref":"#/components/schemas/RecordCommon"},{"type":"object","properties":{"type":{"const":"PTR"},"data":{"$ref":"#/components/schemas/PtrData"}}}]},"PtrData":{"type":"object","required":["target"],"properties":{"target":{"$ref":"#/components/schemas/Hostname"}}},"RecordPublication":{"type":"object","required":["state","changeset_id"],"properties":{"state":{"$ref":"#/components/schemas/PublicationState"},"changeset_id":{"type":"string","description":"The change set that last changed this record."},"pushed_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"live_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"error":{"oneOf":[{"$ref":"#/components/schemas/PublicationError"},{"type":"null"}]}}},"Record":{"description":"A stored record. `RecordInput` plus server-side fields.","allOf":[{"$ref":"#/components/schemas/RecordInput"},{"type":"object","required":["id","fqdn","content","effective_ttl","system","source","publication","created_at","updated_at","etag"],"properties":{"id":{"type":"string","examples":["rec_7Hq2Lm"]},"fqdn":{"type":"string","description":"Absolute owner name, no trailing dot.","examples":["www.example.com"]},"content":{"type":"string","readOnly":true,"description":"RDATA in presentation format, for display only (`10 mail.example.com.`)."},"effective_ttl":{"type":"integer","description":"`ttl`, or the zone default when `ttl` is `null`."},"system":{"type":"boolean","description":"Managed by PawDns (apex NS, DynDNS-managed A/AAAA); read-only via this API."},"system_reason":{"type":["string","null"],"enum":["apex_ns","dyndns",null]},"source":{"type":"string","description":"Where the record came from (`api`, `panel`, `import`, `template:<id>`, `dyndns`, `migration:plesk`, …).","examples":["api"]},"dyndns_host_id":{"type":["string","null"]},"publication":{"$ref":"#/components/schemas/RecordPublication"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"},"created_by":{"type":["string","null"],"description":"Display name of the actor."},"updated_by":{"type":["string","null"]},"etag":{"type":"string"}}}]},"RecordList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Record"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"RecordPatch":{"type":"object","minProperties":1,"description":"Partial update. `type` cannot be changed (`type_immutable`).","properties":{"name":{"$ref":"#/components/schemas/RecordName"},"ttl":{"$ref":"#/components/schemas/Ttl"},"comment":{"$ref":"#/components/schemas/Comment"},"data":{"description":"The complete data object for the record's type.","anyOf":[{"$ref":"#/components/schemas/AData"},{"$ref":"#/components/schemas/AaaaData"},{"$ref":"#/components/schemas/CnameData"},{"$ref":"#/components/schemas/MxData"},{"$ref":"#/components/schemas/TxtData"},{"$ref":"#/components/schemas/SrvData"},{"$ref":"#/components/schemas/CaaData"},{"$ref":"#/components/schemas/TlsaData"},{"$ref":"#/components/schemas/NsData"},{"$ref":"#/components/schemas/PtrData"}]}}},"ChangeSetRequest":{"type":"object","required":["changes"],"properties":{"comment":{"type":"string","maxLength":255,"description":"Shown in the audit log and change set list."},"dry_run":{"type":"boolean","default":false},"zone_if_match":{"type":"string","description":"Zone ETag; the whole change set fails with `412` if the zone changed since."},"changes":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/ChangeItem"}}}},"ChangeItem":{"oneOf":[{"$ref":"#/components/schemas/CreateChange"},{"$ref":"#/components/schemas/UpdateChange"},{"$ref":"#/components/schemas/DeleteChange"},{"$ref":"#/components/schemas/ReplaceRrsetChange"}],"discriminator":{"propertyName":"op","mapping":{"create":"#/components/schemas/CreateChange","update":"#/components/schemas/UpdateChange","delete":"#/components/schemas/DeleteChange","replace_rrset":"#/components/schemas/ReplaceRrsetChange"}}},"CreateChange":{"type":"object","title":"create","required":["op","record"],"properties":{"op":{"type":"string","const":"create"},"record":{"$ref":"#/components/schemas/RecordInput"}}},"UpdateChange":{"type":"object","title":"update","required":["op","id","record"],"properties":{"op":{"type":"string","const":"update"},"id":{"type":"string"},"if_match":{"type":"string","description":"The record's ETag."},"record":{"$ref":"#/components/schemas/RecordPatch"}}},"DeleteChange":{"type":"object","title":"delete","required":["op","id"],"properties":{"op":{"type":"string","const":"delete"},"id":{"type":"string"},"if_match":{"type":"string"}}},"ReplaceRrsetChange":{"type":"object","title":"replace_rrset","description":"Makes the RRset (name + type) contain exactly `records`, all with `ttl`. An empty\n`records` array deletes the RRset. Existing records with equal data keep their id.\n","required":["op","name","type","records"],"properties":{"op":{"type":"string","const":"replace_rrset"},"name":{"$ref":"#/components/schemas/RecordName"},"type":{"$ref":"#/components/schemas/RecordType"},"ttl":{"$ref":"#/components/schemas/Ttl"},"comment":{"$ref":"#/components/schemas/Comment"},"records":{"type":"array","maxItems":100,"description":"Data objects of `type`.","items":{"anyOf":[{"$ref":"#/components/schemas/AData"},{"$ref":"#/components/schemas/AaaaData"},{"$ref":"#/components/schemas/CnameData"},{"$ref":"#/components/schemas/MxData"},{"$ref":"#/components/schemas/TxtData"},{"$ref":"#/components/schemas/SrvData"},{"$ref":"#/components/schemas/CaaData"},{"$ref":"#/components/schemas/TlsaData"},{"$ref":"#/components/schemas/NsData"},{"$ref":"#/components/schemas/PtrData"}]}}}},"DiffEntry":{"type":"object","required":["action"],"properties":{"index":{"type":["integer","null"],"description":"Index of the request item that caused it (`null` for imports/templates)."},"action":{"type":"string","enum":["create","update","delete"]},"before":{"description":"The record before the change (`null` for creates).","oneOf":[{"$ref":"#/components/schemas/Record"},{"type":"null"}]},"after":{"description":"The record after the change (`null` for deletes).","oneOf":[{"$ref":"#/components/schemas/RecordInput"},{"type":"null"}]}}},"ChangeSetSummary":{"type":"object","required":["id","zone","state","origin","counts","created_at"],"properties":{"id":{"type":"string","examples":["chg_5Rt8Xw"]},"zone":{"$ref":"#/components/schemas/ZoneName"},"state":{"$ref":"#/components/schemas/PublicationState"},"origin":{"type":"string","enum":["changeset","record","import","template","zone_settings","zone_create","zone_delete","ptr","dyndns","dnssec"],"description":"What produced the change set."},"comment":{"type":["string","null"]},"counts":{"type":"object","required":["created","updated","deleted"],"properties":{"created":{"type":"integer"},"updated":{"type":"integer"},"deleted":{"type":"integer"}}},"actor":{"$ref":"#/components/schemas/AuditActor"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"pushed_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"live_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"expected_live_by":{"description":"Estimate while `pushed` (push time + ~15 min).","oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"error":{"oneOf":[{"$ref":"#/components/schemas/PublicationError"},{"type":"null"}]}}},"ChangeSet":{"allOf":[{"$ref":"#/components/schemas/ChangeSetSummary"},{"type":"object","required":["results"],"properties":{"attempts":{"type":"integer","description":"Push attempts so far."},"next_attempt_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"live_check":{"type":["object","null"],"description":"Per-nameserver visibility while `pushed`.","properties":{"nameservers":{"type":"array","items":{"type":"object","properties":{"host":{"$ref":"#/components/schemas/Hostname"},"seen":{"type":"boolean"},"checked_at":{"$ref":"#/components/schemas/Timestamp"}}}}}},"results":{"type":"array","description":"One entry per request item (same order), or per applied change for imports/templates.","items":{"type":"object","required":["action"],"properties":{"index":{"type":["integer","null"]},"action":{"type":"string","enum":["create","update","delete"]},"record_id":{"type":"string"},"record":{"description":"The record after the change (`null` for deletes).","oneOf":[{"$ref":"#/components/schemas/Record"},{"type":"null"}]}}}}}}]},"ChangeSetList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChangeSetSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"ChangeSetDryRun":{"type":"object","required":["dry_run","valid","diff"],"properties":{"dry_run":{"type":"boolean","const":true},"valid":{"type":"boolean","description":"`false` if the change set would be rejected; `errors` then lists why."},"diff":{"type":"array","items":{"$ref":"#/components/schemas/DiffEntry"}},"errors":{"type":"array","items":{"$ref":"#/components/schemas/ValidationError"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/ValidationWarning"}}}},"PublicationError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["backend_rejected","backend_unavailable","backend_timeout","zone_not_activated","verification_timeout"]},"message":{"type":"string","description":"Localised, safe to show to customers."},"occurred_at":{"$ref":"#/components/schemas/Timestamp"}}},"DnssecState":{"type":"string","enum":["off","signing","keys_published","ds_submitted","awaiting_ds","ds_published","validated","active","rollover","ds_removing","unsigning","error"],"description":"DESIGN.md §9a. Forward path:\n`off → signing → keys_published → ds_submitted | awaiting_ds → ds_published → validated → active`.\n\n| State | Meaning |\n|---|---|\n| `off` | not signed |\n| `signing` | signing requested at the backend |\n| `keys_published` | DNSKEYs visible on all our nameservers |\n| `ds_submitted` | DS sent to the registrar (automatic mode) |\n| `awaiting_ds` | assisted mode: the customer must add `ds` at their registrar |\n| `ds_published` | DS visible at the parent |\n| `validated` | chain validated; waiting one parent TTL before declaring active |\n| `active` | secure |\n| `rollover` | KSK rollover with double DS in progress; still secure |\n| `ds_removing` | rollback/disable: DS being removed at the parent |\n| `unsigning` | DS gone; signing being switched off |\n| `error` | stuck; see `last_error`. The zone is never left bogus: PawDns removes the DS before anything else |\n"},"DnssecMode":{"type":"string","enum":["automatic","assisted"],"description":"`automatic`: PawDns submits the DS via the registrar API. `assisted`: the customer adds the DS; PawDns shows it and monitors the chain."},"Dnskey":{"type":"object","required":["key_tag","flags","protocol","algorithm","public_key"],"properties":{"key_tag":{"type":"integer"},"flags":{"type":"integer","description":"257 = KSK, 256 = ZSK."},"protocol":{"type":"integer","const":3},"algorithm":{"type":"integer","description":"e.g. 8 (RSASHA256), 13 (ECDSAP256SHA256)."},"public_key":{"type":"string","contentEncoding":"base64"}}},"DsRecord":{"type":"object","required":["key_tag","algorithm","digest_type","digest"],"properties":{"key_tag":{"type":"integer"},"algorithm":{"type":"integer"},"digest_type":{"type":"integer","description":"2 = SHA-256 (recommended), 1 = SHA-1 (legacy), 4 = SHA-384."},"digest":{"type":"string","pattern":"^[0-9A-F]+$"},"presentation":{"type":"string","description":"Ready to paste into a registrar form."}}},"Dnssec":{"type":"object","required":["zone","state","enabled","keys","ds","parent_ds","chain"],"properties":{"zone":{"$ref":"#/components/schemas/ZoneName"},"state":{"$ref":"#/components/schemas/DnssecState"},"enabled":{"type":"boolean","description":"`true` from `signing` until `active`/`rollover`; `false` in `off` and during disabling."},"mode":{"oneOf":[{"$ref":"#/components/schemas/DnssecMode"},{"type":"null"}]},"registrar":{"type":"object","properties":{"managed_by_pawhost":{"type":"boolean","description":"The domain is registered through PawHost, so automatic DS submission is possible."},"name":{"type":["string","null"]}}},"state_since":{"$ref":"#/components/schemas/Timestamp"},"next_action":{"type":["string","null"],"enum":["add_ds_at_registrar","remove_ds_at_registrar","point_ns_to_pawdns","wait","contact_support",null],"description":"What the customer has to do, if anything."},"keys":{"type":"array","description":"DNSKEYs published at our nameservers.","items":{"$ref":"#/components/schemas/Dnskey"}},"ds":{"type":"array","description":"DS records the parent should carry (SHA-256 first).","items":{"$ref":"#/components/schemas/DsRecord"}},"parent_ds":{"type":"array","description":"DS records currently seen at the parent.","items":{"$ref":"#/components/schemas/DsRecord"}},"chain":{"$ref":"#/components/schemas/ChainValidation"},"history":{"type":"array","description":"Last 20 transitions, newest first.","items":{"type":"object","properties":{"from":{"$ref":"#/components/schemas/DnssecState"},"to":{"$ref":"#/components/schemas/DnssecState"},"at":{"$ref":"#/components/schemas/Timestamp"},"reason":{"type":["string","null"]}}}},"last_error":{"type":["object","null"],"properties":{"code":{"type":"string","enum":["registrar_rejected","ds_mismatch","validation_bogus","keys_not_visible","ns_not_delegated","timeout"]},"message":{"type":"string"},"at":{"$ref":"#/components/schemas/Timestamp"}}},"warnings":{"type":"array","items":{"type":"string","enum":["ns_not_pointing_to_pawdns","sha1_ds_at_parent","extra_ds_at_parent","transfer_pending"]}}}},"ChainValidation":{"type":"object","required":["result"],"properties":{"result":{"type":"string","enum":["secure","insecure","bogus","indeterminate","not_checked"],"description":"Result of PawDns's own check. `bogus` means resolvers will SERVFAIL."},"ds_matches_dnskey":{"type":["boolean","null"]},"resolver_ad":{"type":["boolean","null"],"description":"The validating resolver set the AD bit."},"checked_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"details":{"type":"array","items":{"type":"string"}}}},"DnssecEnableRequest":{"type":"object","properties":{"mode":{"$ref":"#/components/schemas/DnssecMode"}}},"IpAssignment":{"type":"object","required":["cidr","family","source","ptr"],"properties":{"cidr":{"type":"string","description":"The assignment; `/32` or `/128` for single addresses.","examples":["192.0.2.10/32","2001:db8:1:2::/64"]},"family":{"type":"integer","enum":[4,6]},"source":{"type":"string","enum":["tenantos","openstack","whmcs","manual"]},"resource":{"type":["object","null"],"description":"The server or VM the IP belongs to.","properties":{"kind":{"type":"string","enum":["server","vm","service"]},"name":{"type":"string"},"id":{"type":"string"}}},"description":{"type":["string","null"]},"ptr":{"description":"For single addresses the PTR; `null` for subnets (use `/ptr?within=`).","oneOf":[{"$ref":"#/components/schemas/Ptr"},{"type":"null"}]},"assigned_since":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"owner":{"description":"Owner of the address space as PawDNS sees it (additional field, may be absent in older responses):\nthe most specific owner tenant you can see (e.g. your customer for a sub-assigned range) and,\nwhen exactly one is set, the responsible user inside it. `null` when no tenant owns it.\n","oneOf":[{"$ref":"#/components/schemas/IpOwner"},{"type":"null"}]}}},"IpOwner":{"type":"object","required":["tenant_id"],"properties":{"tenant_id":{"type":"string","description":"Public id of the owner tenant.","examples":["ten_12"]},"user_id":{"type":["string","null"],"description":"Public id of the responsible user (user-level assignment), if exactly one.","examples":["usr_34"]}}},"IpAssignmentList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IpAssignment"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"ReverseZoneDelegation":{"type":"object","required":["zone","delegation"],"properties":{"zone":{"$ref":"#/components/schemas/ZoneName"},"covers":{"type":"string","description":"The part of the prefix this zone covers."},"rfc2317":{"type":"boolean","description":"Classless delegation (IPv4 prefixes longer than /24)."},"delegation":{"$ref":"#/components/schemas/Delegation"}}},"Prefix":{"type":"object","required":["id","prefix","family","reverse_zones","ptr_count","assigned_at"],"properties":{"id":{"type":"string","examples":["pfx_3Kd9Qa"]},"prefix":{"type":"string","examples":["198.51.100.0/24"]},"family":{"type":"integer","enum":[4,6]},"note":{"type":["string","null"]},"reverse_zones":{"type":"array","items":{"$ref":"#/components/schemas/ReverseZoneDelegation"}},"delegation_status":{"type":"string","enum":["ok","partial","not_delegated","unknown"],"description":"Worst status over all reverse zones. Anything but `ok` means PTRs are not publicly visible."},"ptr_count":{"type":"integer"},"assigned_at":{"$ref":"#/components/schemas/Timestamp"},"owner":{"description":"Owner of the address space as PawDNS sees it (additional field, may be absent in older responses):\nthe most specific owner tenant you can see (e.g. your customer for a sub-assigned range) and,\nwhen exactly one is set, the responsible user inside it. `null` when no tenant owns it.\n","oneOf":[{"$ref":"#/components/schemas/IpOwner"},{"type":"null"}]}}},"PrefixList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Prefix"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"Ptr":{"type":"object","required":["ip","reverse_name","target","is_default","scope","publication"],"properties":{"ip":{"type":"string","examples":["192.0.2.10"]},"reverse_name":{"type":"string","examples":["10.2.0.192.in-addr.arpa"]},"target":{"type":["string","null"],"description":"Current PTR target; `null` when there is none."},"ttl":{"$ref":"#/components/schemas/Ttl"},"is_default":{"type":"boolean","description":"The target is the prefix default (never set by the customer)."},"default_target":{"type":["string","null"],"description":"What a reset would set (the default template of the most specific block, else the OpenStack VM name); `null` when no default is configured for the IP's block and always in customer-owned prefixes."},"scope":{"type":"string","enum":["assignment","customer_prefix"]},"forward_confirmed":{"type":["boolean","null"],"description":"The target's A/AAAA contains this IP (FCrDNS). `null` = not checked yet."},"publication":{"$ref":"#/components/schemas/RecordPublication"},"updated_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"etag":{"type":"string"}}},"PtrUpdate":{"type":"object","required":["target"],"properties":{"target":{"$ref":"#/components/schemas/Hostname"},"ttl":{"$ref":"#/components/schemas/Ttl"}}},"PtrList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Ptr"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"PtrArchive":{"type":"object","required":["id","ip","kind","archived_at","ptr","restore_available"],"properties":{"id":{"type":"string","examples":["arc_12"]},"ip":{"type":"string","examples":["192.0.2.10"]},"kind":{"type":"string","enum":["reset","owner_release"],"description":"`reset`: taken before the PTR was reset; `owner_release`: taken before a released owner was removed."},"archived_at":{"$ref":"#/components/schemas/Timestamp"},"ptr":{"type":"array","description":"The PTR(s) before the reset.","items":{"type":"object","required":["target"],"properties":{"target":{"type":"string","examples":["mail.example.com"]},"ttl":{"type":["integer","null"]}}}},"reset_to":{"type":["string","null"],"description":"What the reset wrote (`null` = the PTR was removed, or not reset)."},"description":{"type":["string","null"]},"status":{"type":["string","null"],"description":"IP status in the sources at archive time (informational)."},"owner_tenant_id":{"type":["string","null"],"description":"Public id of the owner tenant at archive time.","examples":["ten_12"]},"signals":{"type":"array","description":"Why the IP counted as released.","items":{"type":"string","enum":["openstack_deleted","openstack_released","tenantos_unassigned","tenantos_freed","whmcs_terminated","whmcs_removed"]}},"released_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"last_used_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"trigger":{"type":"string","enum":["scheduled","owner_conflict","manual"]},"restored_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"restore_available":{"type":"boolean","description":"The archived customer uses this IP again; a restore is suggested (never automatic)."},"sources":{"description":"Source object ids at archive time; platform tokens only, else `null`.","oneOf":[{"$ref":"#/components/schemas/PtrArchiveSources"},{"type":"null"}]}}},"PtrArchiveSources":{"type":"object","properties":{"whmcs_client_id":{"type":["integer","null"]},"whmcs_service_id":{"type":["integer","null"]},"openstack_project_id":{"type":["string","null"]},"openstack_instance_id":{"type":["string","null"]},"tenantos_server_id":{"type":["string","null"]},"tenantos_user_id":{"type":["string","null"]}}},"PtrArchiveList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PtrArchive"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"PtrDefault":{"type":"object","description":"Default rDNS template of one block (`ptr_defaults`).","required":["id","prefix","family","template","no_default","note","example","created_by","created_at","updated_at"],"properties":{"id":{"type":"string","example":"ptd_1"},"prefix":{"type":"string","example":"192.0.2.0/24"},"family":{"type":"integer","enum":[4,6]},"template":{"type":["string","null"],"description":"`null` = \"no default\" for this block.","example":"{a}.{b}.{c}.{d}.example.net"},"no_default":{"type":"boolean"},"note":{"type":["string","null"]},"example":{"description":"The template rendered for the first host address of the block.","oneOf":[{"type":"object","required":["ip","ptr"],"properties":{"ip":{"type":"string"},"ptr":{"type":"string"}}},{"type":"null"}]},"created_by":{"type":["string","null"]},"created_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"updated_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]}}},"PtrDefaultList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PtrDefault"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"PtrDefaultInput":{"type":"object","required":["prefix","template"],"properties":{"prefix":{"type":"string","maxLength":64,"description":"IPv4 /24 to /32 or any IPv6 prefix."},"template":{"type":["string","null"],"maxLength":255,"description":"`null` = \"no default\" for this block."},"note":{"type":["string","null"],"maxLength":255}}},"PtrDefaultResolution":{"type":"object","required":["ip","target","prefix","template","source","reason","default_id"],"properties":{"ip":{"type":"string"},"target":{"type":["string","null"],"description":"The default PTR, or null when none applies."},"prefix":{"type":["string","null"],"description":"The matched entry's prefix."},"template":{"type":["string","null"]},"source":{"type":"string","enum":["template","openstack","none"]},"reason":{"type":"string"},"default_id":{"type":["string","null"],"description":"Id of the matched entry (`ptd_…`)."}}},"PtrRestoreRequest":{"type":"object","required":["archive_id"],"properties":{"archive_id":{"type":"string","description":"Id of an archive entry from `GET /ptr/{ip}/archive`.","examples":["arc_12"]},"dry_run":{"type":"boolean","default":false,"description":"Only preview; nothing is written."}}},"PtrRestore":{"type":"object","required":["archive","dry_run","changes","ptr"],"properties":{"archive":{"$ref":"#/components/schemas/PtrArchive"},"dry_run":{"type":"boolean"},"changes":{"type":"array","items":{"type":"object","required":["field","current","archived","change"],"properties":{"field":{"type":"string","enum":["ptr"]},"current":{"type":"array","items":{"type":"string"}},"archived":{"type":"array","items":{"type":"string"}},"change":{"type":"boolean"}}}},"ptr":{"$ref":"#/components/schemas/Ptr"}}},"DyndnsHost":{"type":"object","required":["id","hostname","zone","families","ttl","min_interval_seconds","created_at"],"properties":{"id":{"type":"string","examples":["dyn_8Lw2Pe"]},"hostname":{"type":"string","examples":["home.example.com"]},"zone":{"$ref":"#/components/schemas/ZoneName"},"families":{"type":"array","items":{"type":"integer","enum":[4,6]}},"description":{"type":["string","null"]},"ttl":{"type":"integer","description":"Effective TTL of the host's A/AAAA records: the backend minimum (600 s at hosttech;\n60 s in the sandbox). Routers cannot change it.\n","minimum":60,"examples":[600]},"min_interval_seconds":{"type":"integer","examples":[60]},"last_ipv4":{"type":["string","null"],"format":"ipv4"},"last_ipv6":{"type":["string","null"],"format":"ipv6"},"last_update_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"last_result":{"type":["string","null"],"enum":["good","nochg","badauth","notfqdn","nohost","abuse","badagent","dnserr","911",null]},"last_client":{"type":["string","null"],"description":"User agent of the last update."},"update_url":{"type":"string","description":"Template for routers; `<token>` is a placeholder."},"created_at":{"$ref":"#/components/schemas/Timestamp"}}},"DyndnsHostWithToken":{"allOf":[{"$ref":"#/components/schemas/DyndnsHost"},{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"The update token, shown **only in this response**."}}}]},"DyndnsHostCreate":{"type":"object","required":["hostname"],"properties":{"hostname":{"type":"string","description":"FQDN inside one of the tenant's zones; a single label below the zone or deeper. No wildcard.","maxLength":253},"families":{"type":"array","minItems":1,"uniqueItems":true,"default":[4,6],"items":{"type":"integer","enum":[4,6]}},"description":{"type":"string","maxLength":255}}},"DyndnsHostList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DyndnsHost"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"TemplateCategory":{"type":"string","enum":["mail","hosting","verification","security","other"]},"TemplateVariable":{"type":"object","required":["name","label","required"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"string","description":"Localised via `Accept-Language`."},"type":{"type":"string","enum":["hostname","ipv4","ipv6","string"]},"required":{"type":"boolean"},"default":{"type":["string","null"]}}},"TemplateSummary":{"type":"object","required":["id","name","category","owner"],"properties":{"id":{"type":"string","examples":["tpl_mail_pawhost"]},"name":{"type":"string"},"description":{"type":"string"},"category":{"$ref":"#/components/schemas/TemplateCategory"},"owner":{"type":"string","enum":["platform","reseller"]}}},"Template":{"allOf":[{"$ref":"#/components/schemas/TemplateSummary"},{"type":"object","required":["variables","records"],"properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/TemplateVariable"}},"records":{"type":"array","description":"Records with `{{variable}}` and `{{zone}}` placeholders in names and data strings.","items":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/RecordType"},"name":{"type":"string"},"ttl":{"$ref":"#/components/schemas/Ttl"},"data":{"type":"object","additionalProperties":true}}}}}}]},"TemplateList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TemplateSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"TemplateApplyRequest":{"type":"object","required":["template_id"],"properties":{"template_id":{"type":"string"},"variables":{"type":"object","additionalProperties":{"type":"string"}},"on_conflict":{"type":"string","enum":["skip","replace"],"default":"skip"},"dry_run":{"type":"boolean","default":false}}},"AuditObjectType":{"type":"string","enum":["zone","record","changeset","dnssec","ptr","dyndns_host","webhook","tenant"]},"AuditActor":{"type":"object","required":["type","name"],"properties":{"type":{"type":"string","enum":["user","api_client","staff","system"]},"name":{"type":"string","description":"Display name (\"PawHost Support\" for staff, \"PawDns\" for system)."},"api_client":{"type":["string","null"],"description":"The API client that made the call (e.g. \"Kundenpanel\"), also when acting for a customer."},"acting_for":{"oneOf":[{"$ref":"#/components/schemas/TenantRef"},{"type":"null"}]}}},"AuditEntry":{"type":"object","required":["id","at","action","actor","object"],"properties":{"id":{"type":"string"},"at":{"$ref":"#/components/schemas/Timestamp"},"action":{"type":"string","description":"`<object>.<verb>`, e.g. `zone.created`, `record.updated`, `ptr.reset`, `dnssec.enabled`, `dyndns.updated`, `webhook.deleted`.","examples":["record.updated"]},"actor":{"$ref":"#/components/schemas/AuditActor"},"object":{"type":"object","required":["type","id"],"properties":{"type":{"$ref":"#/components/schemas/AuditObjectType"},"id":{"type":"string"},"label":{"type":"string","description":"Human-readable, e.g. `www.example.com A`."},"zone":{"type":["string","null"]}}},"changeset_id":{"type":["string","null"]},"before":{"type":["object","null"],"additionalProperties":true},"after":{"type":["object","null"],"additionalProperties":true},"request_id":{"type":["string","null"]},"ip":{"type":["string","null"],"description":"Client IP of the end user if the panel forwarded it (`X-Forwarded-For` from trusted clients), otherwise of the API client."}}},"AuditEntryList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"WebhookEventType":{"type":"string","enum":["zone.changed","record.published","record.failed","dnssec.state_changed","quota.warning","ping"]},"Webhook":{"type":"object","required":["id","url","events","status","scope","created_at"],"properties":{"id":{"type":"string","examples":["whk_2Fh7Nc"]},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"description":{"type":["string","null"]},"status":{"type":"string","enum":["active","disabled"]},"disabled_reason":{"type":["string","null"],"enum":["failing","gone","manual",null]},"scope":{"description":"Whose events the webhook receives.","$ref":"#/components/schemas/TenantRef"},"includes_subtree":{"type":"boolean","description":"`true` for reseller/platform webhooks created without `X-Act-As-Customer`."},"last_delivery":{"type":["object","null"],"properties":{"at":{"$ref":"#/components/schemas/Timestamp"},"status":{"type":"string","enum":["succeeded","failed","retrying"]},"response_code":{"type":["integer","null"]}}},"created_at":{"$ref":"#/components/schemas/Timestamp"}}},"WebhookWithSecret":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"HMAC signing secret, shown **only in this response**."}}}]},"WebhookCreate":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","pattern":"^https://","maxLength":2048},"events":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/WebhookEventType"}},"description":{"type":"string","maxLength":255}}},"WebhookUpdate":{"type":"object","minProperties":1,"properties":{"url":{"type":"string","format":"uri","pattern":"^https://","maxLength":2048},"events":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/WebhookEventType"}},"description":{"type":"string","maxLength":255},"status":{"type":"string","enum":["active","disabled"]}}},"WebhookList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"WebhookDelivery":{"type":"object","required":["id","event","status","attempts","created_at"],"properties":{"id":{"type":"string"},"event":{"$ref":"#/components/schemas/WebhookEventType"},"status":{"type":"string","enum":["succeeded","failed","retrying"]},"attempts":{"type":"integer"},"response_code":{"type":["integer","null"]},"response_time_ms":{"type":["integer","null"]},"error":{"type":["string","null"],"description":"e.g. `timeout`, `tls_error`, `connection_refused`."},"next_attempt_at":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"created_at":{"$ref":"#/components/schemas/Timestamp"}}},"WebhookDeliveryList":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"EventEnvelope":{"type":"object","required":["id","type","occurred_at","tenant","data"],"properties":{"id":{"type":"string","description":"Event id, equal to `PawDns-Delivery`.","examples":["evt_9Qm4Ts"]},"type":{"$ref":"#/components/schemas/WebhookEventType"},"occurred_at":{"$ref":"#/components/schemas/Timestamp"},"tenant":{"$ref":"#/components/schemas/TenantRef"},"sandbox":{"type":"boolean"},"data":{"type":"object"}}},"ZoneChangedEvent":{"allOf":[{"$ref":"#/components/schemas/EventEnvelope"},{"type":"object","properties":{"type":{"const":"zone.changed"},"data":{"type":"object","required":["zone","change"],"properties":{"zone":{"$ref":"#/components/schemas/ZoneName"},"change":{"type":"string","enum":["created","deleted","settings","records"]},"changeset_id":{"type":["string","null"]},"counts":{"type":"object","properties":{"created":{"type":"integer"},"updated":{"type":"integer"},"deleted":{"type":"integer"}}},"actor":{"$ref":"#/components/schemas/AuditActor"}}}}}]},"RecordPublishedEvent":{"allOf":[{"$ref":"#/components/schemas/EventEnvelope"},{"type":"object","properties":{"type":{"const":"record.published"},"data":{"type":"object","required":["zone","changeset_id","state","record_ids"],"properties":{"zone":{"$ref":"#/components/schemas/ZoneName"},"changeset_id":{"type":"string"},"state":{"type":"string","enum":["pushed","live"]},"record_ids":{"type":"array","items":{"type":"string"}},"ptr_ips":{"type":"array","description":"For PTR changes made via `/ptr/{ip}`.","items":{"type":"string"}}}}}}]},"RecordFailedEvent":{"allOf":[{"$ref":"#/components/schemas/EventEnvelope"},{"type":"object","properties":{"type":{"const":"record.failed"},"data":{"type":"object","required":["zone","changeset_id","error"],"properties":{"zone":{"$ref":"#/components/schemas/ZoneName"},"changeset_id":{"type":"string"},"record_ids":{"type":"array","items":{"type":"string"}},"error":{"$ref":"#/components/schemas/PublicationError"},"attempts":{"type":"integer"}}}}}]},"DnssecStateChangedEvent":{"allOf":[{"$ref":"#/components/schemas/EventEnvelope"},{"type":"object","properties":{"type":{"const":"dnssec.state_changed"},"data":{"type":"object","required":["zone","from","to"],"properties":{"zone":{"$ref":"#/components/schemas/ZoneName"},"from":{"$ref":"#/components/schemas/DnssecState"},"to":{"$ref":"#/components/schemas/DnssecState"},"mode":{"$ref":"#/components/schemas/DnssecMode"},"next_action":{"type":["string","null"]},"reason":{"type":["string","null"]}}}}}]},"QuotaWarningEvent":{"allOf":[{"$ref":"#/components/schemas/EventEnvelope"},{"type":"object","properties":{"type":{"const":"quota.warning"},"data":{"type":"object","required":["quota","used","limit","level"],"properties":{"quota":{"$ref":"#/components/schemas/QuotaName"},"zone":{"type":["string","null"],"description":"Set for `records_per_zone`."},"used":{"type":"integer"},"limit":{"type":"integer"},"level":{"type":"string","enum":["warning","exhausted"]}}}}}]},"ProblemCode":{"type":"string","description":"Stable machine-readable error code. New codes may be added; treat unknown codes by\ntheir HTTP status.\n\n| Code | Status | Meaning |\n|---|---|---|\n| `unauthenticated` | 401 | no `Authorization` header |\n| `invalid_token` | 401 | token unknown, expired or revoked |\n| `insufficient_scope` | 403 | the token lacks the scope in `required_scope` |\n| `act_as_not_allowed` | 403 | `X-Act-As-Customer` without `act_as` scope, outside the subtree, or not a customer |\n| `act_as_unknown` | 404 | no such tenant / WHMCS client |\n| `not_owner` | 403 | the acting tenant does not own this IP / object |\n| `tenant_suspended` | 403 | writes blocked because the tenant is suspended |\n| `feature_disabled` | 403 | the tenant may not use this feature (see `/me` `features`) |\n| `zone_protected` | 403 | the name is a protected PawHost name or below one |\n| `zone_external` | 403 | the zone is listed but hosted elsewhere (mode `external`), read-only |\n| `record_readonly` | 403 | system record (apex NS, DynDNS-managed) |\n| `not_found` | 404 | no such object in the acting tenant's subtree |\n| `validation_failed` | 422 | see `errors` |\n| `idempotency_key_reused` | 422 | same `Idempotency-Key`, different request |\n| `conflict` | 409 | generic state conflict (see `detail`) |\n| `zone_exists` | 409 | the zone already exists (at this or another tenant) |\n| `zone_overlap` | 409 | the zone is a parent or child of an existing zone |\n| `reverse_zone_managed` | 409 | reverse zones are created from prefixes, not directly |\n| `quota_exceeded` | 409 | a tenant limit is reached; see `quota` |\n| `idempotency_in_progress` | 409 | the first request with this key is still running |\n| `dnssec_transition_in_progress` | 409 | DNSSEC is in a transition that cannot be interrupted |\n| `dnssec_not_delegated` | 409 | the domain's NS do not point at our nameservers |\n| `dnssec_ds_present` | 409 | a customer-managed DS is still at the parent |\n| `precondition_failed` | 412 | `If-Match` / `if_match` stale |\n| `payload_too_large` | 413 | body or change set too large |\n| `unsupported_media_type` | 415 | wrong `Content-Type` |\n| `rate_limited` | 429 | slow down; see `Retry-After` |\n| `internal_error` | 500 | bug; quote `request_id` |\n| `not_implemented` | 501 | the operation exists in the contract but is not available yet (DNSSEC transitions until the registrar integration exists) |\n| `backend_capacity_exhausted` | 503 | no new zones can be created right now |\n| `maintenance` | 503 | planned maintenance |\n","enum":["unauthenticated","invalid_token","insufficient_scope","act_as_not_allowed","act_as_unknown","not_owner","tenant_suspended","feature_disabled","zone_protected","zone_external","record_readonly","not_found","validation_failed","idempotency_key_reused","conflict","zone_exists","zone_overlap","reverse_zone_managed","quota_exceeded","idempotency_in_progress","dnssec_transition_in_progress","dnssec_not_delegated","dnssec_ds_present","precondition_failed","payload_too_large","unsupported_media_type","rate_limited","internal_error","not_implemented","backend_capacity_exhausted","maintenance"]},"Problem":{"type":"object","description":"RFC 7807 problem details, extended with `code`, `request_id` and `errors`.","required":["type","title","status","code","request_id"],"properties":{"type":{"type":"string","format":"uri","description":"`https://manage.pawdns.de/api/problems/<code>` (documentation link)."},"title":{"type":"string","description":"Short, localised summary."},"status":{"type":"integer"},"detail":{"type":"string","description":"Localised explanation, safe to show to the end user."},"instance":{"type":"string","description":"The request path."},"code":{"$ref":"#/components/schemas/ProblemCode"},"request_id":{"type":"string"},"errors":{"type":"array","description":"Field/item errors for `validation_failed` and `precondition_failed`.","items":{"$ref":"#/components/schemas/ValidationError"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/ValidationWarning"}},"required_scope":{"$ref":"#/components/schemas/Scope"},"quota":{"type":"object","description":"For `quota_exceeded`.","properties":{"name":{"$ref":"#/components/schemas/QuotaName"},"limit":{"type":"integer"},"used":{"type":"integer"}}},"retry_after":{"type":"integer","description":"Seconds; for `rate_limited` and 503 errors."}}},"ValidationError":{"type":"object","required":["pointer","code","detail"],"properties":{"index":{"type":["integer","null"],"description":"Change set item index (also for imports, where `line` is set too)."},"line":{"type":["integer","null"],"description":"Zone file line for imports."},"pointer":{"type":"string","description":"JSON Pointer (RFC 6901) into the request body.","examples":["/changes/2/record/data/address"]},"code":{"$ref":"#/components/schemas/ValidationErrorCode"},"detail":{"type":"string","description":"Localised."}}},"ValidationErrorCode":{"type":"string","enum":["required","invalid_type","invalid_format","invalid_name","invalid_ipv4","invalid_ipv6","invalid_hostname","out_of_range","too_long","not_allowed","unsupported_type","type_immutable","wildcard_not_allowed","name_protected","outside_zone","outside_prefix","wrong_zone_kind","duplicate_record","cname_at_apex","cname_conflict","cname_loop","ns_at_apex","occluded_by_delegation","glue_missing","invalid_srv_name","invalid_tlsa_name","invalid_tlsa_data","invalid_caa_value","null_mx_conflict","record_not_found","version_mismatch","quota_exceeded","dyndns_managed","template_variable_missing","parse_error"]},"ValidationWarning":{"type":"object","required":["code","detail"],"properties":{"index":{"type":["integer","null"]},"pointer":{"type":["string","null"]},"code":{"type":"string","enum":["rrset_ttl_mismatch","target_is_cname","target_not_resolving","long_txt_slow_path","ptr_not_forward_confirmed","spf_multiple","low_ttl"]},"detail":{"type":"string"}}}},"examples":{"StatsReseller":{"summary":"Reseller token","value":{"scope":"tenant","generated_at":"2026-10-12T10:40:00+00:00","zones":{"forward":12,"reverse":0,"total":12},"records":{"total":143,"by_type":{"A":40,"TXT":38,"MX":24,"CNAME":21,"AAAA":20}},"ip":{"networks":3,"ipv4_addresses":144,"ptr":{"total":61,"default":40,"custom":21}},"dyndns":{"hosts":2,"changes_24h":3},"webhooks":{"active":1,"failed_24h":0},"api_tokens":{"active":2}}},"MeCustomer":{"summary":"Panel token acting for a direct PawHost customer","value":{"tenant":{"id":"ten_4821","type":"customer","name":"Example GmbH","status":"active","whmcs_client_id":1234,"parent":{"id":"ten_1","name":"PawHost"},"limits":{"max_zones":25,"max_records_per_zone":500,"max_dyndns_hosts":10,"max_webhooks":10,"api_rate_limit_per_minute":600},"usage":{"zones":21,"dyndns_hosts":1,"webhooks":0,"records_total":412,"largest_zone":{"name":"example.com","records":83},"warnings":[{"quota":"zones","used":21,"limit":25,"level":"warning"}]},"branding":{"name":"PawHost","logo_url":"https://manage.pawdns.de/img/pawhost-logo.svg","colors":{"primary":"#1e3a8a","accent":"#f59e0b"},"support_email":"support@example.com","support_url":"https://www.example.com/support","panel_domain":null},"features":{"zone_create":true,"zone_delete":true,"import_export":true,"dnssec":true,"dyndns":true,"templates":true,"rdns":true,"customer_prefixes":true,"webhooks":true,"audit":true},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"policies":{"dnssec_automatic":true,"ttl_min":60,"ttl_max":86400}},"caller":{"api_client":{"id":"api_7","name":"Kundenpanel"},"token_tenant":{"id":"ten_1","name":"PawHost"},"scopes":["tenant:read","zones:read","zones:write","records:read","records:write","dnssec:read","dnssec:write","rdns:read","rdns:write","dyndns:read","dyndns:write","audit:read","webhooks:manage","act_as"],"acting_as":true,"sandbox":false}}},"ZoneActive":{"summary":"Active zone with DNSSEC","value":{"name":"example.com","display_name":"example.com","kind":"forward","status":"active","tenant":{"id":"ten_4821","name":"Example GmbH"},"default_ttl":3600,"soa":{"mname":"ns1.pawdns.de","rname":"hostmaster@example.com","refresh":7200,"retry":3600,"expire":1209600,"minimum":3600},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"record_count":14,"prefix_id":null,"dnssec":{"state":"active","mode":"automatic"},"publication":{"state":"live","pending_changesets":0,"failed_changesets":0,"last_live_at":"2026-10-11T08:47:12Z"},"delegation":{"status":"ok","expected":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"observed":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"checked_at":"2026-10-11T06:00:00Z"},"created_at":"2025-03-02T10:15:00Z","updated_at":"2026-10-11T08:31:40Z","etag":"\"z-4f1c2a-17\""}},"ZoneActivating":{"summary":"Newly created zone","value":{"name":"example.com","display_name":"example.com","kind":"forward","status":"activating","tenant":{"id":"ten_4821","name":"Example GmbH"},"default_ttl":3600,"soa":{"mname":"ns1.pawdns.de","rname":"hostmaster@example.com","refresh":7200,"retry":3600,"expire":1209600,"minimum":3600},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"record_count":9,"prefix_id":null,"dnssec":{"state":"signing","mode":"automatic"},"publication":{"state":"publishing","pending_changesets":1,"failed_changesets":0,"last_live_at":null},"delegation":{"status":"unknown","expected":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"observed":[],"checked_at":null},"created_at":"2026-10-11T09:30:00Z","updated_at":"2026-10-11T09:30:00Z","etag":"\"z-8a7d3e-1\""}},"ZoneDeleting":{"summary":"Zone being deleted","value":{"name":"example.com","display_name":"example.com","kind":"forward","status":"deleting","tenant":{"id":"ten_4821","name":"Example GmbH"},"default_ttl":3600,"soa":{"mname":"ns1.pawdns.de","rname":"hostmaster@example.com","refresh":7200,"retry":3600,"expire":1209600,"minimum":3600},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"record_count":14,"prefix_id":null,"dnssec":{"state":"ds_removing","mode":"automatic"},"publication":{"state":"publishing","pending_changesets":1,"failed_changesets":0,"last_live_at":"2026-10-11T08:47:12Z"},"delegation":{"status":"ok","expected":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"observed":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"checked_at":"2026-10-11T06:00:00Z"},"created_at":"2025-03-02T10:15:00Z","updated_at":"2026-10-11T09:40:00Z","etag":"\"z-4f1c2a-18\""}},"ZoneListPage":{"summary":"First page of zones","value":{"data":[{"name":"example.com","display_name":"example.com","kind":"forward","status":"active","tenant":{"id":"ten_4821","name":"Example GmbH"},"default_ttl":3600,"soa":{"mname":"ns1.pawdns.de","rname":"hostmaster@example.com"},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"record_count":14,"prefix_id":null,"dnssec":{"state":"active","mode":"automatic"},"publication":{"state":"publishing","pending_changesets":1,"failed_changesets":0,"last_live_at":"2026-10-11T08:47:12Z"},"delegation":{"status":"ok","checked_at":"2026-10-11T06:00:00Z"},"created_at":"2025-03-02T10:15:00Z","updated_at":"2026-10-11T09:31:40Z","etag":"\"z-4f1c2a-17\""},{"name":"example.net","display_name":"example.net","kind":"forward","status":"active","tenant":{"id":"ten_4821","name":"Example GmbH"},"default_ttl":3600,"soa":{"mname":"ns1.pawdns.de","rname":"hostmaster@example.net"},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"record_count":6,"prefix_id":null,"dnssec":{"state":"awaiting_ds","mode":"assisted"},"publication":{"state":"live","pending_changesets":0,"failed_changesets":0,"last_live_at":"2026-09-30T14:02:00Z"},"delegation":{"status":"ok","checked_at":"2026-10-11T06:00:00Z"},"created_at":"2025-07-19T08:00:00Z","updated_at":"2026-09-30T13:50:00Z","etag":"\"z-91b0c4-5\""},{"name":"100.51.198.in-addr.arpa","display_name":"100.51.198.in-addr.arpa","kind":"reverse","status":"active","tenant":{"id":"ten_4821","name":"Example GmbH"},"default_ttl":10800,"soa":{"mname":"ns1.pawdns.de","rname":"hostmaster@example.com"},"nameservers":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"record_count":42,"prefix_id":"pfx_3Kd9Qa","dnssec":{"state":"off","mode":null},"publication":{"state":"live","pending_changesets":0,"failed_changesets":0,"last_live_at":"2026-10-01T12:00:00Z"},"delegation":{"status":"not_delegated","checked_at":"2026-10-11T06:00:00Z"},"created_at":"2026-01-10T09:00:00Z","updated_at":"2026-10-01T11:45:00Z","etag":"\"z-c3d2e1-9\""}],"pagination":{"next_cursor":"eyJuIjoiZXhhbXBsZS5vcmcifQ","has_more":true,"total":21}}},"ImportDryRun":{"summary":"Diff of a merge import","value":{"dry_run":true,"parsed":7,"diff":[{"index":null,"action":"create","before":null,"after":{"type":"MX","name":"@","ttl":3600,"data":{"preference":10,"exchange":"mail.example.com"}}},{"index":null,"action":"create","before":null,"after":{"type":"A","name":"mail","ttl":3600,"data":{"address":"192.0.2.25"}}},{"index":null,"action":"create","before":null,"after":{"type":"TXT","name":"@","ttl":3600,"data":{"text":"v=spf1 mx -all"}}}],"skipped":[{"line":3,"text":"@       IN SOA  ns1.example.net. hostmaster.example.com. 2026101101 7200 3600 1209600 3600","reason":"soa"},{"line":4,"text":"@       IN NS   ns1.example.net.","reason":"apex_ns"},{"line":5,"text":"@       IN A    192.0.2.10","reason":"duplicate"},{"line":6,"text":"www     IN CNAME example.com.","reason":"duplicate"}],"warnings":[],"changeset":null}},"ImportApplied":{"summary":"Import stored as a change set","value":{"dry_run":false,"parsed":7,"diff":[{"index":null,"action":"create","before":null,"after":{"type":"A","name":"mail","ttl":3600,"data":{"address":"192.0.2.25"}}}],"skipped":[{"line":3,"reason":"soa"},{"line":4,"reason":"apex_ns"}],"warnings":[],"changeset":{"id":"chg_6Yp1Vb","zone":"example.com","state":"pending","origin":"import","comment":"Import aus altem Provider","counts":{"created":1,"updated":0,"deleted":0},"actor":{"type":"api_client","name":"Example GmbH","api_client":"Kundenpanel","acting_for":{"id":"ten_4821","name":"Example GmbH"}},"created_at":"2026-10-11T09:35:00Z","pushed_at":null,"live_at":null,"expected_live_by":null,"error":null,"attempts":0,"next_attempt_at":null,"live_check":null,"results":[{"index":null,"action":"create","record_id":"rec_4Gx7Hn","record":null}]}}},"RecordAPending":{"summary":"A record just created","value":{"id":"rec_7Hq2Lm","type":"A","name":"www","fqdn":"www.example.com","ttl":3600,"effective_ttl":3600,"comment":null,"data":{"address":"192.0.2.10"},"content":"192.0.2.10","system":false,"system_reason":null,"source":"api","dyndns_host_id":null,"publication":{"state":"pending","changeset_id":"chg_5Rt8Xw","pushed_at":null,"live_at":null,"error":null},"created_at":"2026-10-11T09:30:00Z","updated_at":"2026-10-11T09:30:00Z","created_by":"Example GmbH via Kundenpanel","updated_by":"Example GmbH via Kundenpanel","etag":"\"r-7Hq2Lm-1\""}},"RecordMxLive":{"summary":"MX record, live","value":{"id":"rec_3Nf6Bq","type":"MX","name":"@","fqdn":"example.com","ttl":null,"effective_ttl":3600,"comment":null,"data":{"preference":10,"exchange":"mail.example.com"},"content":"10 mail.example.com.","system":false,"system_reason":null,"source":"migration:plesk","dyndns_host_id":null,"publication":{"state":"live","changeset_id":"chg_1Ab2Cd","pushed_at":"2026-10-01T10:00:05Z","live_at":"2026-10-01T10:12:40Z","error":null},"created_at":"2026-10-01T10:00:00Z","updated_at":"2026-10-01T10:00:00Z","created_by":"PawDns","updated_by":"PawDns","etag":"\"r-3Nf6Bq-1\""}},"RecordMxPending":{"summary":"MX record after an update","value":{"id":"rec_3Nf6Bq","type":"MX","name":"@","fqdn":"example.com","ttl":null,"effective_ttl":3600,"comment":"moved to new mail provider","data":{"preference":10,"exchange":"mx1.example.net"},"content":"10 mx1.example.net.","system":false,"system_reason":null,"source":"migration:plesk","dyndns_host_id":null,"publication":{"state":"pending","changeset_id":"chg_8Kj3Wz","pushed_at":null,"live_at":null,"error":null},"created_at":"2026-10-01T10:00:00Z","updated_at":"2026-10-11T09:31:00Z","created_by":"PawDns","updated_by":"Example GmbH via Kundenpanel","etag":"\"r-3Nf6Bq-2\""}},"RecordListPage":{"summary":"Records of example.com","value":{"data":[{"id":"rec_0Ns1Aa","type":"NS","name":"@","fqdn":"example.com","ttl":3600,"effective_ttl":3600,"comment":null,"data":{"nameserver":"ns1.pawdns.de"},"content":"ns1.pawdns.de.","system":true,"system_reason":"apex_ns","source":"system","dyndns_host_id":null,"publication":{"state":"live","changeset_id":"chg_0Zz0Aa","pushed_at":"2025-03-02T10:15:05Z","live_at":"2025-03-02T10:25:00Z","error":null},"created_at":"2025-03-02T10:15:00Z","updated_at":"2025-03-02T10:15:00Z","created_by":"PawDns","updated_by":"PawDns","etag":"\"r-0Ns1Aa-1\""},{"id":"rec_9mWq3T","type":"AAAA","name":"@","fqdn":"example.com","ttl":3600,"effective_ttl":3600,"comment":null,"data":{"address":"2001:db8:10::10"},"content":"2001:db8:10::10","system":false,"system_reason":null,"source":"panel","dyndns_host_id":null,"publication":{"state":"live","changeset_id":"chg_2Ee4Ff","pushed_at":"2026-09-12T08:00:04Z","live_at":"2026-09-12T08:11:30Z","error":null},"created_at":"2026-09-12T08:00:00Z","updated_at":"2026-09-12T08:00:00Z","created_by":"Example GmbH via Kundenpanel","updated_by":"Example GmbH via Kundenpanel","etag":"\"r-9mWq3T-4\""},{"id":"rec_5Tx2Dk","type":"TXT","name":"default._domainkey","fqdn":"default._domainkey.example.com","ttl":3600,"effective_ttl":3600,"comment":"DKIM","data":{"text":"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwq3cZ0W2y6Jq1v8m4Ea7pP1h3c2Qy0p9g8vH6sX5Zt3Lr7Kq2Nw4Uj9Bf6Ym1Dc0Tg8Sx3Ve5Ra7Qp2Wn9Lk4Hb6Gc1Fd3Mj0Pz8Ky5Ix2Ow7Tv4Ru1Qs9Ep6Ah3Jg0Ni8Lm5Kc2Bb7Va4Uz1Yx9Wt6Sr3Qo0Pn8Mk5Lj2Ih9Gf6Ed3Dc0Ba7Zy4Xw1Vu8Ts5Rq2Po9Nm6Lk3Ji0Hg7Fe4Dc1Ba8Zy5Xw2Vu9Ts6Rq3Po0Nm7Lk4Ji1Hg8Fe5Dc2Ba9QIDAQAB","chunks":["v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwq3cZ0W2y6Jq1v8m4Ea7pP1h3c2Qy0p9g8vH6sX5Zt3Lr7Kq2Nw4Uj9Bf6Ym1Dc0Tg8Sx3Ve5Ra7Qp2Wn9Lk4Hb6Gc1Fd3Mj0Pz8Ky5Ix2Ow7Tv4Ru1Qs9Ep6Ah3Jg0Ni8Lm5Kc2Bb7Va4Uz1Yx9Wt6Sr3Qo0Pn8Mk5Lj2Ih9Gf6Ed3Dc0Ba7Zy4Xw1Vu8Ts5","Rq2Po9Nm6Lk3Ji0Hg7Fe4Dc1Ba8Zy5Xw2Vu9Ts6Rq3Po0Nm7Lk4Ji1Hg8Fe5Dc2Ba9QIDAQAB"]},"content":"\"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwq3cZ0W2y6Jq1v8m4Ea7pP1h3c2Qy0p9g8vH6sX5Zt3Lr7Kq2Nw4Uj9Bf6Ym1Dc0Tg8Sx3Ve5Ra7Qp2Wn9Lk4Hb6Gc1Fd3Mj0Pz8Ky5Ix2Ow7Tv4Ru1Qs9Ep6Ah3Jg0Ni8Lm5Kc2Bb7Va4Uz1Yx9Wt6Sr3Qo0Pn8Mk5Lj2Ih9Gf6Ed3Dc0Ba7Zy4Xw1Vu8Ts5\" \"Rq2Po9Nm6Lk3Ji0Hg7Fe4Dc1Ba8Zy5Xw2Vu9Ts6Rq3Po0Nm7Lk4Ji1Hg8Fe5Dc2Ba9QIDAQAB\"","system":false,"system_reason":null,"source":"import","dyndns_host_id":null,"publication":{"state":"pushed","changeset_id":"chg_5Rt8Xw","pushed_at":"2026-10-11T09:30:20Z","live_at":null,"error":null},"created_at":"2026-10-11T09:30:00Z","updated_at":"2026-10-11T09:30:00Z","created_by":"Example GmbH via Kundenpanel","updated_by":"Example GmbH via Kundenpanel","etag":"\"r-5Tx2Dk-1\""},{"id":"rec_6Sv9Lm","type":"SRV","name":"_sip._tcp","fqdn":"_sip._tcp.example.com","ttl":3600,"effective_ttl":3600,"comment":null,"data":{"priority":10,"weight":5,"port":5060,"target":"sip.example.com"},"content":"10 5 5060 sip.example.com.","system":false,"system_reason":null,"source":"api","dyndns_host_id":null,"publication":{"state":"live","changeset_id":"chg_2Ee4Ff","pushed_at":"2026-09-12T08:00:04Z","live_at":"2026-09-12T08:11:30Z","error":null},"created_at":"2026-09-12T08:00:00Z","updated_at":"2026-09-12T08:00:00Z","created_by":"Example GmbH via Kundenpanel","updated_by":"Example GmbH via Kundenpanel","etag":"\"r-6Sv9Lm-1\""}],"pagination":{"next_cursor":null,"has_more":false}}},"ChangeSetPending":{"summary":"Stored, waiting for the push","value":{"id":"chg_5Rt8Xw","zone":"example.com","state":"pending","origin":"changeset","comment":"Umzug auf neuen Webserver","counts":{"created":3,"updated":1,"deleted":2},"actor":{"type":"api_client","name":"Example GmbH","api_client":"Kundenpanel","acting_for":{"id":"ten_4821","name":"Example GmbH"}},"created_at":"2026-10-11T09:30:00Z","pushed_at":null,"live_at":null,"expected_live_by":null,"error":null,"attempts":0,"next_attempt_at":"2026-10-11T09:30:01Z","live_check":null,"results":[{"index":0,"action":"create","record_id":"rec_1Aq9Zs","record":null},{"index":0,"action":"create","record_id":"rec_1Bq9Zt","record":null},{"index":0,"action":"delete","record_id":"rec_7Ux1Ok","record":null},{"index":1,"action":"update","record_id":"rec_9mWq3T","record":null},{"index":2,"action":"create","record_id":"rec_8Ca1Aa","record":null},{"index":3,"action":"delete","record_id":"rec_2Pz8Kd","record":null}]}},"ChangeSetPushed":{"summary":"Accepted by the backend, waiting to be visible","value":{"id":"chg_5Rt8Xw","zone":"example.com","state":"pushed","origin":"changeset","comment":"Umzug auf neuen Webserver","counts":{"created":3,"updated":1,"deleted":2},"created_at":"2026-10-11T09:30:00Z","pushed_at":"2026-10-11T09:30:20Z","live_at":null,"expected_live_by":"2026-10-11T09:45:20Z","error":null,"attempts":1,"next_attempt_at":null,"live_check":{"nameservers":[{"host":"ns1.pawdns.de","seen":true,"checked_at":"2026-10-11T09:38:00Z"},{"host":"ns2.pawdns.com","seen":false,"checked_at":"2026-10-11T09:38:00Z"},{"host":"ns3.pawdns.eu","seen":false,"checked_at":"2026-10-11T09:38:00Z"}]},"results":[{"index":1,"action":"update","record_id":"rec_9mWq3T","record":null}]}},"ChangeSetLive":{"summary":"Live on all nameservers","value":{"id":"chg_5Rt8Xw","zone":"example.com","state":"live","origin":"changeset","comment":"Umzug auf neuen Webserver","counts":{"created":3,"updated":1,"deleted":2},"created_at":"2026-10-11T09:30:00Z","pushed_at":"2026-10-11T09:30:20Z","live_at":"2026-10-11T09:42:10Z","expected_live_by":null,"error":null,"attempts":1,"next_attempt_at":null,"live_check":null,"results":[{"index":1,"action":"update","record_id":"rec_9mWq3T","record":null}]}},"ChangeSetFailed":{"summary":"Publication failed","value":{"id":"chg_4Lm2Np","zone":"example.com","state":"failed","origin":"record","comment":null,"counts":{"created":1,"updated":0,"deleted":0},"created_at":"2026-10-11T07:00:00Z","pushed_at":null,"live_at":null,"expected_live_by":null,"error":{"code":"backend_unavailable","message":"The nameserver provider did not respond. PawDns keeps retrying automatically.","occurred_at":"2026-10-11T08:10:00Z"},"attempts":8,"next_attempt_at":null,"live_check":null,"results":[{"index":0,"action":"create","record_id":"rec_3Tt3Tt","record":null}]}},"ChangeSetDeletePending":{"summary":"Implicit change set of a record delete","value":{"id":"chg_9Ds4Er","zone":"example.com","state":"pending","origin":"record","comment":null,"counts":{"created":0,"updated":0,"deleted":1},"created_at":"2026-10-11T09:33:00Z","pushed_at":null,"live_at":null,"expected_live_by":null,"error":null,"attempts":0,"next_attempt_at":"2026-10-11T09:33:01Z","live_check":null,"results":[{"index":0,"action":"delete","record_id":"rec_7Hq2Lm","record":null}]}},"ChangeSetListPage":{"summary":"Recent change sets","value":{"data":[{"id":"chg_5Rt8Xw","zone":"example.com","state":"pushed","origin":"changeset","comment":"Umzug auf neuen Webserver","counts":{"created":3,"updated":1,"deleted":2},"actor":{"type":"api_client","name":"Example GmbH","api_client":"Kundenpanel","acting_for":{"id":"ten_4821","name":"Example GmbH"}},"created_at":"2026-10-11T09:30:00Z","pushed_at":"2026-10-11T09:30:20Z","live_at":null,"expected_live_by":"2026-10-11T09:45:20Z","error":null},{"id":"chg_2Ee4Ff","zone":"example.com","state":"live","origin":"record","comment":null,"counts":{"created":1,"updated":0,"deleted":0},"actor":{"type":"user","name":"Erika Mustermann","api_client":"Kundenpanel","acting_for":null},"created_at":"2026-09-12T08:00:00Z","pushed_at":"2026-09-12T08:00:04Z","live_at":"2026-09-12T08:11:30Z","expected_live_by":null,"error":null}],"pagination":{"next_cursor":"eyJ0IjoiMjAyNi0wOS0xMiJ9","has_more":true}}},"ChangeSetDryRunResult":{"summary":"Dry run with an error","value":{"dry_run":true,"valid":false,"diff":[{"index":0,"action":"create","before":null,"after":{"type":"CNAME","name":"@","data":{"target":"example.net"}}}],"errors":[{"index":0,"line":null,"pointer":"/changes/0/record/name","code":"cname_at_apex","detail":"A CNAME is not allowed at the zone apex. Use A/AAAA records instead."}],"warnings":[]}},"DnssecActive":{"summary":"Fully active, automatic mode","value":{"zone":"example.com","state":"active","enabled":true,"mode":"automatic","registrar":{"managed_by_pawhost":true,"name":"InternetX"},"state_since":"2026-09-01T11:20:00Z","next_action":null,"keys":[{"key_tag":24513,"flags":257,"protocol":3,"algorithm":13,"public_key":"mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ=="},{"key_tag":47110,"flags":256,"protocol":3,"algorithm":13,"public_key":"oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA=="}],"ds":[{"key_tag":24513,"algorithm":13,"digest_type":2,"digest":"1F0A3C7E9B2D4F6A8C0E2B4D6F8A0C2E4B6D8F0A2C4E6B8D0F2A4C6E8B0D2F4A","presentation":"example.com. IN DS 24513 13 2 1F0A3C7E9B2D4F6A8C0E2B4D6F8A0C2E4B6D8F0A2C4E6B8D0F2A4C6E8B0D2F4A"}],"parent_ds":[{"key_tag":24513,"algorithm":13,"digest_type":2,"digest":"1F0A3C7E9B2D4F6A8C0E2B4D6F8A0C2E4B6D8F0A2C4E6B8D0F2A4C6E8B0D2F4A"}],"chain":{"result":"secure","ds_matches_dnskey":true,"resolver_ad":true,"checked_at":"2026-10-11T09:00:00Z","details":[]},"history":[{"from":"validated","to":"active","at":"2026-09-01T11:20:00Z","reason":null},{"from":"ds_published","to":"validated","at":"2026-09-01T10:20:00Z","reason":null}],"last_error":null,"warnings":[]}},"DnssecAwaitingDs":{"summary":"Assisted mode, customer must add the DS","value":{"zone":"example.net","state":"awaiting_ds","enabled":true,"mode":"assisted","registrar":{"managed_by_pawhost":false,"name":null},"state_since":"2026-09-30T14:30:00Z","next_action":"add_ds_at_registrar","keys":[{"key_tag":31337,"flags":257,"protocol":3,"algorithm":13,"public_key":"kXHn4W9iH7e1QrCq5C3tb3a1Gp0ExQbNn2vO8Yl6r3U0bH5vQ1oZ9J2xMBe6uR0aDqLe0Kd5u2N7rSx1Gq3W9A=="}],"ds":[{"key_tag":31337,"algorithm":13,"digest_type":2,"digest":"9C2E4A6B8D0F1A3C5E7B9D1F3A5C7E9B1D3F5A7C9E1B3D5F7A9C1E3B5D7F9A1C","presentation":"example.net. IN DS 31337 13 2 9C2E4A6B8D0F1A3C5E7B9D1F3A5C7E9B1D3F5A7C9E1B3D5F7A9C1E3B5D7F9A1C"}],"parent_ds":[],"chain":{"result":"insecure","ds_matches_dnskey":null,"resolver_ad":false,"checked_at":"2026-10-11T09:00:00Z","details":["No DS record at the parent zone yet."]},"history":[{"from":"keys_published","to":"awaiting_ds","at":"2026-09-30T14:30:00Z","reason":"Domain is registered at a foreign registrar."}],"last_error":null,"warnings":[]}},"DnssecOff":{"summary":"Not signed","value":{"zone":"example.org","state":"off","enabled":false,"mode":null,"registrar":{"managed_by_pawhost":true,"name":"InternetX"},"state_since":"2025-05-05T10:00:00Z","next_action":null,"keys":[],"ds":[],"parent_ds":[],"chain":{"result":"insecure","ds_matches_dnskey":null,"resolver_ad":false,"checked_at":"2026-10-11T09:00:00Z","details":[]},"history":[],"last_error":null,"warnings":[]}},"DnssecSigning":{"summary":"Enabling just started","value":{"zone":"example.org","state":"signing","enabled":true,"mode":"automatic","registrar":{"managed_by_pawhost":true,"name":"InternetX"},"state_since":"2026-10-11T09:30:00Z","next_action":"wait","keys":[],"ds":[],"parent_ds":[],"chain":{"result":"not_checked","checked_at":null},"history":[{"from":"off","to":"signing","at":"2026-10-11T09:30:00Z","reason":"Enabled via API."}],"last_error":null,"warnings":[]}},"DnssecDsRemoving":{"summary":"Disabling, DS removal first","value":{"zone":"example.com","state":"ds_removing","enabled":false,"mode":"automatic","registrar":{"managed_by_pawhost":true,"name":"InternetX"},"state_since":"2026-10-11T09:30:00Z","next_action":"wait","keys":[{"key_tag":24513,"flags":257,"protocol":3,"algorithm":13,"public_key":"mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ=="}],"ds":[],"parent_ds":[{"key_tag":24513,"algorithm":13,"digest_type":2,"digest":"1F0A3C7E9B2D4F6A8C0E2B4D6F8A0C2E4B6D8F0A2C4E6B8D0F2A4C6E8B0D2F4A"}],"chain":{"result":"secure","ds_matches_dnskey":true,"resolver_ad":true,"checked_at":"2026-10-11T09:30:00Z","details":[]},"history":[{"from":"active","to":"ds_removing","at":"2026-10-11T09:30:00Z","reason":"Disabled via API."}],"last_error":null,"warnings":[]}},"PtrCustom":{"summary":"Customer-set PTR","value":{"ip":"192.0.2.25","reverse_name":"25.2.0.192.in-addr.arpa","target":"mail.example.com","ttl":3600,"is_default":false,"default_target":"192-0-2-25.pawhost.de","scope":"assignment","forward_confirmed":true,"publication":{"state":"live","changeset_id":"chg_7Pt1Rr","pushed_at":"2026-10-02T12:00:03Z","live_at":"2026-10-02T12:13:00Z","error":null},"updated_at":"2026-10-02T12:00:00Z","etag":"\"p-192.0.2.25-3\""}},"PtrArchiveListPage":{"summary":"One archive entry","value":{"data":[{"id":"arc_12","ip":"192.0.2.10","kind":"reset","archived_at":"2026-10-21T09:25:00Z","ptr":[{"target":"mail.example.com","ttl":3600}],"reset_to":"192-0-2-10.pawhost.de","description":"Mailserver","status":"whmcs: free (Terminated)","owner_tenant_id":"ten_12","signals":["whmcs_terminated"],"released_at":"2026-10-14T09:20:00Z","last_used_at":"2026-10-14T09:10:00Z","trigger":"scheduled","restored_at":null,"restore_available":false,"sources":null}],"pagination":{"next_cursor":null,"has_more":false}}},"PtrRestoreDone":{"summary":"PTR restored, publication pending","value":{"archive":{"id":"arc_12","ip":"192.0.2.10","kind":"reset","archived_at":"2026-10-21T09:25:00Z","ptr":[{"target":"mail.example.com","ttl":3600}],"reset_to":"192-0-2-10.pawhost.de","description":"Mailserver","status":"whmcs: free (Terminated)","owner_tenant_id":"ten_12","signals":["whmcs_terminated"],"released_at":"2026-10-14T09:20:00Z","last_used_at":"2026-10-14T09:10:00Z","trigger":"scheduled","restored_at":"2026-11-02T08:00:00Z","restore_available":false,"sources":null},"dry_run":false,"changes":[{"field":"ptr","current":["192-0-2-10.pawhost.de"],"archived":["mail.example.com"],"change":true}],"ptr":{"ip":"192.0.2.10","reverse_name":"10.2.0.192.in-addr.arpa","target":"mail.example.com","ttl":3600,"is_default":false,"default_target":"192-0-2-10.pawhost.de","scope":"assignment","forward_confirmed":null,"publication":{"state":"pending","changeset_id":"chg_7Hq2Lm9Xa1Bc","pushed_at":null,"live_at":null,"error":null},"updated_at":"2026-11-02T08:00:00Z","etag":"\"p-192.0.2.10-3f2a9c1d0e\""}}},"PtrDefault":{"summary":"Default PTR (never changed, or reset)","value":{"ip":"192.0.2.10","reverse_name":"10.2.0.192.in-addr.arpa","target":"192-0-2-10.pawhost.de","ttl":null,"is_default":true,"default_target":"192-0-2-10.pawhost.de","scope":"assignment","forward_confirmed":true,"publication":{"state":"live","changeset_id":"chg_0Pt0Aa","pushed_at":"2026-06-01T08:00:03Z","live_at":"2026-06-01T08:12:00Z","error":null},"updated_at":"2026-06-01T08:00:00Z","etag":"\"p-192.0.2.10-1\""}},"PtrPending":{"summary":"PTR just set","value":{"ip":"192.0.2.25","reverse_name":"25.2.0.192.in-addr.arpa","target":"mail.example.com","ttl":3600,"is_default":false,"default_target":"192-0-2-25.pawhost.de","scope":"assignment","forward_confirmed":true,"publication":{"state":"pending","changeset_id":"chg_7Pt2Ss","pushed_at":null,"live_at":null,"error":null},"updated_at":"2026-10-11T09:30:00Z","etag":"\"p-192.0.2.25-4\""}},"PtrListPage":{"summary":"PTRs in a customer prefix","value":{"data":[{"ip":"198.51.100.1","reverse_name":"1.100.51.198.in-addr.arpa","target":"gw.example.com","ttl":null,"is_default":false,"default_target":null,"scope":"customer_prefix","forward_confirmed":true,"publication":{"state":"live","changeset_id":"chg_1Pf1Aa","pushed_at":"2026-01-10T09:05:00Z","live_at":"2026-01-10T09:18:00Z","error":null},"updated_at":"2026-01-10T09:05:00Z","etag":"\"p-198.51.100.1-1\""},{"ip":"198.51.100.2","reverse_name":"2.100.51.198.in-addr.arpa","target":null,"ttl":null,"is_default":false,"default_target":null,"scope":"customer_prefix","forward_confirmed":null,"publication":{"state":"live","changeset_id":"chg_1Pf1Aa","pushed_at":null,"live_at":null,"error":null},"updated_at":null,"etag":"\"p-198.51.100.2-0\""}],"pagination":{"next_cursor":"eyJpcCI6IjE5OC41MS4xMDAuMiJ9","has_more":true}}},"IpAssignmentListPage":{"summary":"A server IP, a VM IP and a v6 subnet","value":{"data":[{"cidr":"192.0.2.10/32","family":4,"source":"tenantos","resource":{"kind":"server","name":"web01","id":"311"},"description":"Hauptserver","ptr":{"ip":"192.0.2.10","reverse_name":"10.2.0.192.in-addr.arpa","target":"192-0-2-10.pawhost.de","ttl":null,"is_default":true,"default_target":"192-0-2-10.pawhost.de","scope":"assignment","forward_confirmed":true,"publication":{"state":"live","changeset_id":"chg_0Pt0Aa"},"updated_at":"2026-06-01T08:00:00Z","etag":"\"p-192.0.2.10-1\""},"assigned_since":"2025-03-01T00:00:00Z"},{"cidr":"192.0.2.25/32","family":4,"source":"openstack","resource":{"kind":"vm","name":"vps10452","id":"6c1f0a3e-2b4d-4e8f-9a1c-3d5e7f9b1a2c"},"description":null,"ptr":{"ip":"192.0.2.25","reverse_name":"25.2.0.192.in-addr.arpa","target":"mail.example.com","ttl":3600,"is_default":false,"default_target":"vps10452.cloud.yoursrv.de","scope":"assignment","forward_confirmed":true,"publication":{"state":"live","changeset_id":"chg_7Pt1Rr"},"updated_at":"2026-10-02T12:00:00Z","etag":"\"p-192.0.2.25-3\""},"assigned_since":"2026-02-14T10:00:00Z"},{"cidr":"2001:db8:1:2::/64","family":6,"source":"tenantos","resource":{"kind":"server","name":"web01","id":"311"},"description":null,"ptr":null,"assigned_since":"2025-03-01T00:00:00Z"}],"pagination":{"next_cursor":null,"has_more":false}}},"PrefixV4":{"summary":"Customer /24, not yet delegated","value":{"id":"pfx_3Kd9Qa","prefix":"198.51.100.0/24","family":4,"note":"Eigenes PI-Netz (AS64500)","reverse_zones":[{"zone":"100.51.198.in-addr.arpa","covers":"198.51.100.0/24","rfc2317":false,"delegation":{"status":"not_delegated","expected":["ns1.pawdns.de","ns2.pawdns.com","ns3.pawdns.eu"],"observed":["ns1.example.net","ns2.example.net"],"checked_at":"2026-10-11T06:00:00Z"}}],"delegation_status":"not_delegated","ptr_count":42,"assigned_at":"2026-01-10T09:00:00Z"}},"PrefixListPage":{"summary":"v4 and v6 prefixes","value":{"data":[{"id":"pfx_3Kd9Qa","prefix":"198.51.100.0/24","family":4,"note":"Eigenes PI-Netz (AS64500)","reverse_zones":[{"zone":"100.51.198.in-addr.arpa","covers":"198.51.100.0/24","rfc2317":false,"delegation":{"status":"not_delegated","checked_at":"2026-10-11T06:00:00Z"}}],"delegation_status":"not_delegated","ptr_count":42,"assigned_at":"2026-01-10T09:00:00Z"},{"id":"pfx_7Vq2Lx","prefix":"2001:db8:abcd::/48","family":6,"note":null,"reverse_zones":[{"zone":"d.c.b.a.8.b.d.0.1.0.0.2.ip6.arpa","covers":"2001:db8:abcd::/48","rfc2317":false,"delegation":{"status":"ok","checked_at":"2026-10-11T06:00:00Z"}}],"delegation_status":"ok","ptr_count":7,"assigned_at":"2026-01-10T09:00:00Z"}],"pagination":{"next_cursor":null,"has_more":false}}},"DyndnsHostHome":{"summary":"Dual-stack home router","value":{"id":"dyn_8Lw2Pe","hostname":"home.example.com","zone":"example.com","families":[4,6],"description":"FritzBox at home","ttl":600,"min_interval_seconds":60,"last_ipv4":"192.0.2.44","last_ipv6":"2001:db8:44::1","last_update_at":"2026-10-11T04:12:09Z","last_result":"good","last_client":"AVM FRITZ!Box","update_url":"https://manage.pawdns.de/nic/update?hostname=home.example.com&myip=<ipaddr>&myipv6=<ip6addr>","created_at":"2026-05-01T18:00:00Z"}},"DyndnsHostCreated":{"summary":"New host with its token (shown once)","value":{"id":"dyn_8Lw2Pe","hostname":"home.example.com","zone":"example.com","families":[4,6],"description":"FritzBox at home","ttl":600,"min_interval_seconds":60,"last_ipv4":null,"last_ipv6":null,"last_update_at":null,"last_result":null,"last_client":null,"update_url":"https://manage.pawdns.de/nic/update?hostname=home.example.com&myip=<ipaddr>&myipv6=<ip6addr>","created_at":"2026-10-11T09:30:00Z","token":"dyn_tok_Vb7Qe2Lx9Rm4Ks1Np8Td3Wf6Yh"}},"DyndnsHostListPage":{"summary":"DynDNS hosts","value":{"data":[{"id":"dyn_8Lw2Pe","hostname":"home.example.com","zone":"example.com","families":[4,6],"description":"FritzBox at home","ttl":600,"min_interval_seconds":60,"last_ipv4":"192.0.2.44","last_ipv6":"2001:db8:44::1","last_update_at":"2026-10-11T04:12:09Z","last_result":"good","last_client":"AVM FRITZ!Box","update_url":"https://manage.pawdns.de/nic/update?hostname=home.example.com&myip=<ipaddr>&myipv6=<ip6addr>","created_at":"2026-05-01T18:00:00Z"}],"pagination":{"next_cursor":null,"has_more":false}}},"TemplateMail":{"summary":"PawHost mail template","value":{"id":"tpl_mail_pawhost","name":"PawHost Mail","description":"MX, SPF, DMARC and autodiscover for PawHost mail hosting.","category":"mail","owner":"platform","variables":[{"name":"mail_host","label":"Mailserver","type":"hostname","required":true,"default":null}],"records":[{"type":"MX","name":"@","ttl":3600,"data":{"preference":10,"exchange":"{{mail_host}}"}},{"type":"TXT","name":"@","ttl":3600,"data":{"text":"v=spf1 mx a:{{mail_host}} -all"}},{"type":"TXT","name":"_dmarc","ttl":3600,"data":{"text":"v=DMARC1; p=quarantine; rua=mailto:dmarc@{{zone}}"}},{"type":"CNAME","name":"autodiscover","ttl":3600,"data":{"target":"{{mail_host}}"}}]}},"TemplateListPage":{"summary":"Available templates","value":{"data":[{"id":"tpl_mail_pawhost","name":"PawHost Mail","description":"MX, SPF, DMARC and autodiscover for PawHost mail hosting.","category":"mail","owner":"platform"},{"id":"tpl_hosting_web","name":"Webhosting","description":"Apex and www pointing to a web server.","category":"hosting","owner":"platform"},{"id":"tpl_m365","name":"Microsoft 365","description":"MX, SPF, autodiscover and the verification TXT for Microsoft 365.","category":"mail","owner":"platform"}],"pagination":{"next_cursor":null,"has_more":false}}},"AuditListPage":{"summary":"Audit entries","value":{"data":[{"id":"aud_91kQ2x","at":"2026-10-11T09:31:00Z","action":"record.updated","actor":{"type":"api_client","name":"Example GmbH","api_client":"Kundenpanel","acting_for":{"id":"ten_4821","name":"Example GmbH"}},"object":{"type":"record","id":"rec_3Nf6Bq","label":"example.com MX","zone":"example.com"},"changeset_id":"chg_8Kj3Wz","before":{"data":{"preference":10,"exchange":"mail.example.com"}},"after":{"data":{"preference":10,"exchange":"mx1.example.net"}},"request_id":"req_01JA4Z6W3K8N2Q","ip":"203.0.113.45"},{"id":"aud_91kQ1w","at":"2026-10-10T16:02:00Z","action":"ptr.reset","actor":{"type":"staff","name":"PawHost Support","api_client":null,"acting_for":null},"object":{"type":"ptr","id":"192.0.2.10","label":"192.0.2.10","zone":null},"changeset_id":"chg_0Pt9Zz","before":{"target":"old.example.org"},"after":{"target":"192-0-2-10.pawhost.de"},"request_id":null,"ip":null}],"pagination":{"next_cursor":"eyJpZCI6ImF1ZF85MWtRMXcifQ","has_more":true}}},"WebhookPanel":{"summary":"Panel webhook for the whole subtree","value":{"id":"whk_2Fh7Nc","url":"https://panel.example.com/hooks/pawdns","events":["zone.changed","record.published","record.failed","dnssec.state_changed","quota.warning"],"description":"Customer panel","status":"active","disabled_reason":null,"scope":{"id":"ten_1","name":"PawHost"},"includes_subtree":true,"last_delivery":{"at":"2026-10-11T09:42:10Z","status":"succeeded","response_code":204},"created_at":"2026-10-01T12:00:00Z"}},"WebhookCreated":{"summary":"Webhook with its secret (shown once)","value":{"id":"whk_2Fh7Nc","url":"https://panel.example.com/hooks/pawdns","events":["zone.changed","record.published","record.failed","dnssec.state_changed","quota.warning"],"description":"Customer panel","status":"active","disabled_reason":null,"scope":{"id":"ten_1","name":"PawHost"},"includes_subtree":true,"last_delivery":null,"created_at":"2026-10-11T09:30:00Z","secret":"whsec_N4q8Zt2Lp6Vx1Rc9Kb3Ym7Hd5Fs0Jw"}},"WebhookListPage":{"summary":"Webhooks","value":{"data":[{"id":"whk_2Fh7Nc","url":"https://panel.example.com/hooks/pawdns","events":["zone.changed","record.published","record.failed","dnssec.state_changed","quota.warning"],"description":"Customer panel","status":"active","disabled_reason":null,"scope":{"id":"ten_1","name":"PawHost"},"includes_subtree":true,"last_delivery":{"at":"2026-10-11T09:42:10Z","status":"succeeded","response_code":204},"created_at":"2026-10-01T12:00:00Z"}],"pagination":{"next_cursor":null,"has_more":false}}},"WebhookDeliveryOk":{"summary":"Successful ping","value":{"id":"evt_0Pg1Aa","event":"ping","status":"succeeded","attempts":1,"response_code":200,"response_time_ms":143,"error":null,"next_attempt_at":null,"created_at":"2026-10-11T09:30:01Z"}},"WebhookDeliveryListPage":{"summary":"Recent deliveries","value":{"data":[{"id":"evt_9Qm4Ts","event":"record.published","status":"succeeded","attempts":1,"response_code":204,"response_time_ms":88,"error":null,"next_attempt_at":null,"created_at":"2026-10-11T09:42:10Z"},{"id":"evt_9Qm3Sr","event":"zone.changed","status":"retrying","attempts":2,"response_code":502,"response_time_ms":2011,"error":null,"next_attempt_at":"2026-10-11T10:05:00Z","created_at":"2026-10-11T09:30:00Z"}],"pagination":{"next_cursor":null,"has_more":false}}},"EventZoneChanged":{"summary":"Records changed via a change set","value":{"id":"evt_9Qm3Sr","type":"zone.changed","occurred_at":"2026-10-11T09:30:00Z","tenant":{"id":"ten_4821","name":"Example GmbH"},"sandbox":false,"data":{"zone":"example.com","change":"records","changeset_id":"chg_5Rt8Xw","counts":{"created":3,"updated":1,"deleted":2},"actor":{"type":"api_client","name":"Example GmbH","api_client":"Kundenpanel","acting_for":{"id":"ten_4821","name":"Example GmbH"}}}}},"EventRecordPublished":{"summary":"Change set live","value":{"id":"evt_9Qm4Ts","type":"record.published","occurred_at":"2026-10-11T09:42:10Z","tenant":{"id":"ten_4821","name":"Example GmbH"},"sandbox":false,"data":{"zone":"example.com","changeset_id":"chg_5Rt8Xw","state":"live","record_ids":["rec_1Aq9Zs","rec_1Bq9Zt","rec_9mWq3T","rec_8Ca1Aa"]}}},"EventRecordFailed":{"summary":"Publication failed","value":{"id":"evt_9Qm5Uv","type":"record.failed","occurred_at":"2026-10-11T08:10:00Z","tenant":{"id":"ten_4821","name":"Example GmbH"},"sandbox":false,"data":{"zone":"example.com","changeset_id":"chg_4Lm2Np","record_ids":["rec_3Tt3Tt"],"error":{"code":"backend_unavailable","message":"The nameserver provider did not respond. PawDns keeps retrying automatically.","occurred_at":"2026-10-11T08:10:00Z"},"attempts":8}}},"EventDnssecStateChanged":{"summary":"DNSSEC became active","value":{"id":"evt_9Qm6Wx","type":"dnssec.state_changed","occurred_at":"2026-09-01T11:20:00Z","tenant":{"id":"ten_4821","name":"Example GmbH"},"sandbox":false,"data":{"zone":"example.com","from":"validated","to":"active","mode":"automatic","next_action":null,"reason":null}}},"EventQuotaWarning":{"summary":"Zone quota at 84 %","value":{"id":"evt_9Qm7Yz","type":"quota.warning","occurred_at":"2026-10-11T09:30:00Z","tenant":{"id":"ten_4821","name":"Example GmbH"},"sandbox":false,"data":{"quota":"zones","zone":null,"used":21,"limit":25,"level":"warning"}}},"ProblemInvalidToken":{"summary":"Unknown or revoked token","value":{"type":"https://manage.pawdns.de/api/problems/invalid_token","title":"Invalid token","status":401,"detail":"The access token is unknown, expired or revoked.","instance":"/api/v1/zones","code":"invalid_token","request_id":"req_01JA4Z6W3K8N2Q"}},"ProblemInsufficientScope":{"summary":"Missing scope","value":{"type":"https://manage.pawdns.de/api/problems/insufficient_scope","title":"Insufficient scope","status":403,"detail":"This token lacks the scope \"records:write\".","instance":"/api/v1/zones/example.com/records","code":"insufficient_scope","request_id":"req_01JA4Z6W3K8N2R","required_scope":"records:write"}},"ProblemNotOwner":{"summary":"IP not owned (German)","value":{"type":"https://manage.pawdns.de/api/problems/not_owner","title":"Keine Berechtigung","status":403,"detail":"Die IP-Adresse 192.0.2.99 ist diesem Kunden nicht zugeordnet.","instance":"/api/v1/ptr/192.0.2.99","code":"not_owner","request_id":"req_01JA4Z6W3K8N2S"}},"ProblemNotFound":{"summary":"Not found","value":{"type":"https://manage.pawdns.de/api/problems/not_found","title":"Not found","status":404,"detail":"Zone \"example.org\" was not found.","instance":"/api/v1/zones/example.org","code":"not_found","request_id":"req_01JA4Z6W3K8N2T"}},"ProblemActAsUnknown":{"summary":"Unknown act-as customer","value":{"type":"https://manage.pawdns.de/api/problems/act_as_unknown","title":"Unknown customer","status":404,"detail":"There is no customer \"whmcs:1234\".","instance":"/api/v1/zones","code":"act_as_unknown","request_id":"req_01JA4Z6W3K8N2T"}},"ProblemConflict":{"summary":"State conflict","value":{"type":"https://manage.pawdns.de/api/problems/conflict","title":"Conflict","status":409,"detail":"Only failed change sets can be retried.","instance":"/api/v1/zones/example.com/changesets/chg_5Rt8Xw/retry","code":"conflict","request_id":"req_01JA4Z6W3K8N2U"}},"ProblemQuotaExceeded":{"summary":"Zone quota reached (German)","value":{"type":"https://manage.pawdns.de/api/problems/quota_exceeded","title":"Kontingent erschöpft","status":409,"detail":"Sie haben bereits 25 von 25 Zonen angelegt.","instance":"/api/v1/zones","code":"quota_exceeded","request_id":"req_01JA4Z6W3K8N2V","quota":{"name":"zones","limit":25,"used":25}}},"ProblemZoneProtected":{"summary":"Protected name","value":{"type":"https://manage.pawdns.de/api/problems/zone_protected","title":"Protected name","status":403,"detail":"shop.pawhost.de cannot be managed in PawDns.","instance":"/api/v1/zones","code":"zone_protected","request_id":"req_01JA4Z6W3K8N2W"}},"ProblemZoneExists":{"summary":"Zone already exists","value":{"type":"https://manage.pawdns.de/api/problems/zone_exists","title":"Zone already exists","status":409,"detail":"example.com is already managed in PawDns. Contact support if this is your domain.","instance":"/api/v1/zones","code":"zone_exists","request_id":"req_01JA4Z6W3K8N2X"}},"ProblemDsPresent":{"summary":"DS still at the parent","value":{"type":"https://manage.pawdns.de/api/problems/dnssec_ds_present","title":"DS record still published","status":409,"detail":"Remove the DS record for example.net at your registrar first, or the domain will fail DNSSEC validation. Use force=true to delete anyway.","instance":"/api/v1/zones/example.net","code":"dnssec_ds_present","request_id":"req_01JA4Z6W3K8N2Y"}},"ProblemDnssecNotDelegated":{"summary":"NS not pointing at PawDns","value":{"type":"https://manage.pawdns.de/api/problems/dnssec_not_delegated","title":"Domain not delegated to PawDns","status":409,"detail":"The nameservers of example.com do not point at ns1.pawdns.de, ns2.pawdns.com and ns3.pawdns.eu.","instance":"/api/v1/zones/example.com/dnssec/enable","code":"dnssec_not_delegated","request_id":"req_01JA4Z6W3K8N2Z"}},"ProblemPreconditionFailed":{"summary":"Stale ETag","value":{"type":"https://manage.pawdns.de/api/problems/precondition_failed","title":"Precondition failed","status":412,"detail":"The record was changed by someone else. Reload and try again.","instance":"/api/v1/zones/example.com/records/rec_7Hq2Lm","code":"precondition_failed","request_id":"req_01JA4Z6W3K8N30","errors":[{"index":null,"line":null,"pointer":"/","code":"version_mismatch","detail":"Current version is \"r-7Hq2Lm-4\"."}]}},"ProblemValidation":{"summary":"Field errors","value":{"type":"https://manage.pawdns.de/api/problems/validation_failed","title":"Validation failed","status":422,"detail":"The request contains invalid fields.","instance":"/api/v1/zones/example.com/records","code":"validation_failed","request_id":"req_01JA4Z6W3K8N31","errors":[{"index":null,"line":null,"pointer":"/data/address","code":"invalid_ipv4","detail":"\"192.0.2.300\" is not a valid IPv4 address."},{"index":null,"line":null,"pointer":"/ttl","code":"out_of_range","detail":"TTL must be between 60 and 86400 seconds."}]}},"ProblemChangeSetValidation":{"summary":"Per-item errors (German)","value":{"type":"https://manage.pawdns.de/api/problems/validation_failed","title":"Ungültige Änderungen","status":422,"detail":"2 von 4 Änderungen sind ungültig. Es wurde nichts gespeichert.","instance":"/api/v1/zones/example.com/changesets","code":"validation_failed","request_id":"req_01JA4Z6W3K8N32","errors":[{"index":1,"line":null,"pointer":"/changes/1/record/name","code":"cname_conflict","detail":"Unter \"www\" existiert bereits ein A-Eintrag; ein CNAME darf keine weiteren Einträge neben sich haben."},{"index":3,"line":null,"pointer":"/changes/3/record/data/certificate_data","code":"invalid_tlsa_data","detail":"Bei matching_type 1 muss certificate_data aus genau 64 Hex-Zeichen bestehen."}],"warnings":[{"index":0,"pointer":"/changes/0","code":"rrset_ttl_mismatch","detail":"Die anderen A-Einträge unter \"@\" haben TTL 3600."}]}},"ProblemPayloadTooLarge":{"summary":"Too many items","value":{"type":"https://manage.pawdns.de/api/problems/payload_too_large","title":"Payload too large","status":413,"detail":"A change set may contain at most 1000 changes.","instance":"/api/v1/zones/example.com/changesets","code":"payload_too_large","request_id":"req_01JA4Z6W3K8N33"}},"ProblemRateLimited":{"summary":"Rate limit","value":{"type":"https://manage.pawdns.de/api/problems/rate_limited","title":"Too many requests","status":429,"detail":"Rate limit of 600 requests per minute exceeded. Retry in 17 seconds.","instance":"/api/v1/zones","code":"rate_limited","request_id":"req_01JA4Z6W3K8N34","retry_after":17}},"ProblemNotImplemented":{"summary":"DNSSEC transition not available yet","value":{"type":"https://manage.pawdns.de/api/problems/not_implemented","title":"Not implemented yet","status":501,"detail":"This operation is not available yet: the registrar integration for DNSSEC is still being built. GET …/dnssec shows the current state.","instance":"/api/v1/zones/example.com/dnssec/enable","code":"not_implemented","request_id":"req_01JA4Z6W3K8N2V"}},"ProblemBackendCapacity":{"summary":"No backend capacity","value":{"type":"https://manage.pawdns.de/api/problems/backend_capacity_exhausted","title":"Zone cannot be created right now","status":503,"detail":"No new zones can be created at the moment. Please contact support.","instance":"/api/v1/zones","code":"backend_capacity_exhausted","request_id":"req_01JA4Z6W3K8N35","retry_after":3600}}}}}