Zum Inhalt

DNS-API: Zonen und Records

Diese Seite beschreibt die Endpunkte für Zonen, Records und delegierte Subdomains. Grundlagen wie Basis-URL, Authentifizierung und Fehlerformat findest du im API-Überblick. Alle Endpunkte auf dieser Seite erfordern einen Token mit Zugriff auf den Bereich Records; auf ausgewählte Zonen beschränkte Token erreichen nur diese Zonen.

Die Beispiele verwenden die Variable XCLOUD_DNS_TOKEN für deinen API-Token und folgende Basis-URL:

export XCLOUD_DNS_TOKEN='xcdns_...'
export XCLOUD_DNS_API='https://app.xcloud.eu/api/dns/v1'

Zonen

Methode Pfad Beschreibung
GET /zones Alle Zonen auflisten, nach Namen sortiert
GET /zones/{id} Eine Zone anzeigen
POST /zones Eine Zone anlegen
DELETE /zones/{id} Eine Zone samt aller Records löschen
POST /zones/{id}/delegation_check Die Delegierung erneut prüfen
POST /zones/{id}/dnssec DNSSEC einschalten
DELETE /zones/{id}/dnssec DNSSEC ausschalten
GET /zones/{id}/export Records als BIND-Zonendatei exportieren
POST /zones/{id}/import Records aus einer BIND-Zonendatei importieren
POST /zones/{id}/retrieve Zonentransfer anstoßen (nur sekundäre Zonen)

Eine Zone lässt sich nach dem Anlegen nicht mehr ändern; für Zonen gibt es keinen PATCH-Endpunkt.

Attribute einer Zone

Attribut Beschreibung
id ID der Zone
name Zonenname, z. B. example.com
state pending, active, error oder deleting
state_detail Zusätzliche Informationen zum Zustand oder null
nameservers Die für die Zone zuständigen xCloud-Nameserver
serial Aktuelle Seriennummer der Zone oder null
zone_type master (Records werden in xCloud verwaltet) oder slave (sekundär)
masters IP-Adressen der Primary-Nameserver einer sekundären Zone
dnssec_status DNSSEC-Signierungsstatus oder null
dnssec_ds DS-Records der signierten Zone
dnssec_dnskey DNSKEY-Records der signierten Zone
delegation_status ok, misconfigured oder unknown
delegation_checked_at Zeitpunkt der letzten Delegierungsprüfung oder null
delegation_detail Am Parent beobachtete Nameserver oder null
records_count Anzahl der Records in der Zone
registration_id ID der verknüpften, über xCloud registrierten Domain oder null
content_changed_at Zeitpunkt der letzten Änderung an den Records oder null
created_at Zeitpunkt der Erstellung
updated_at Zeitpunkt der letzten Änderung

Zone anzeigen

GET /zones/{id} liefert eine Zone, GET /zones alle Zonen in derselben Form:

curl "$XCLOUD_DNS_API/zones/$ZONE_ID" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN"
{
  "data": {
    "id": "0b7c4f0e-2a51-4c4e-9a7f-5d3c1e8b9f10",
    "name": "example.com",
    "state": "active",
    "state_detail": null,
    "nameservers": ["ns1.example.net", "ns2.example.net"],
    "serial": "2026102001",
    "zone_type": "master",
    "masters": [],
    "dnssec_status": null,
    "dnssec_ds": [],
    "dnssec_dnskey": [],
    "delegation_status": "ok",
    "delegation_checked_at": "2026-10-20T06:00:12+02:00",
    "delegation_detail": "ns1.example.net, ns2.example.net",
    "records_count": 12,
    "registration_id": "1d2e3f4a-5b6c-4d7e-8f9a-0b1c2d3e4f51",
    "created_at": "2026-09-14T10:21:37+02:00",
    "updated_at": "2026-10-20T06:00:12+02:00",
    "content_changed_at": "2026-10-19T17:42:05+02:00"
  }
}

Zone anlegen

POST /zones legt eine Zone an. Die Zone startet im Zustand pending und wird active, sobald sie auf den Nameservern eingerichtet ist.

Attribut Pflicht Beschreibung
name ja Domainname der Zone, z. B. example.com
zone_type nein master (Standard) oder slave für eine sekundäre Zone
masters bei slave IP-Adressen (IPv4 oder IPv6) deiner Primary-Nameserver

Jeder Zonenname kann nur einmal existieren. NS- und SOA-Records werden automatisch gesetzt. Sobald die Zone aktiv ist, trage für die Domain die Nameserver aus dem Attribut nameservers der Zone ein.

Eine sekundäre Zone anlegen, die ihre Records per Zonentransfer (AXFR) von zwei Primary-Nameservern übernimmt:

curl -X POST "$XCLOUD_DNS_API/zones" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "zone": {
      "name": "example.com",
      "zone_type": "slave",
      "masters": ["192.0.2.53", "2001:db8::53"]
    }
  }'

Für eine normale Zone schickst du nur den Namen: {"zone": {"name": "example.com"}}.

Die Records einer sekundären Zone sind schreibgeschützt: Anlegen, Ändern oder Löschen von Records, DNSSEC und Import werden mit 422 abgelehnt. POST /zones/{id}/retrieve stößt einen sofortigen Zonentransfer von den Primary-Nameservern an; bei anderen Zonen liefert es 422.

Delegierungsprüfung

xCloud prüft regelmäßig, ob die Delegierung der Domain bei der Registry auf die Nameserver der Zone zeigt; das Ergebnis steht in delegation_status. POST /zones/{id}/delegation_check stellt eine zusätzliche Prüfung in die Warteschlange. Eine Zone lässt sich höchstens einmal pro Minute erneut prüfen; häufigere Anfragen werden mit 422 abgelehnt.

DNSSEC

POST /zones/{id}/dnssec schaltet die DNSSEC-Signierung der Zone ein, DELETE /zones/{id}/dnssec schaltet sie aus. Beides läuft im Hintergrund; die Antwort zeigt die Zone zum Zeitpunkt der Anfrage.

  • Bei über xCloud registrierten Domains werden die DS-Records automatisch bei der Registry veröffentlicht (und beim Ausschalten von DNSSEC entfernt).
  • Bei extern registrierten Domains entnimmst du die DS-Records dem Attribut dnssec_ds und hinterlegst sie bei deinem Registrar.

Zonendatei exportieren

GET /zones/{id}/export liefert alle Records der Zone als BIND-Zonendatei mit dem Content-Type text/plain. SOA- und Apex-NS-Records verwalten die Nameserver selbst; sie sind nicht enthalten.

Zonendatei importieren

POST /zones/{id}/import legt Records aus einer BIND-Zonendatei an.

Attribut Pflicht Beschreibung
zonefile ja Inhalt der BIND-Zonendatei als String
dry_run nein true zeigt nur, was importiert würde; Standard false

Anders als bei anderen Endpunkten sind die Attribute nicht in ein äußeres Objekt verpackt. Das folgende Beispiel liest eine lokale Zonendatei, verpackt sie mit jq in die Anfrage und führt zunächst einen Probelauf aus:

jq -n --rawfile zonefile example.com.zone '{zonefile: $zonefile, dry_run: true}' |
  curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/import" \
    -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
    -H "Content-Type: application/json" \
    -d @-

Die Antwort ist ein Importbericht:

{
  "data": {
    "dry_run": true,
    "creates": [
      { "record_type": "A", "subdomain": "www", "content": "192.0.2.10", "ttl": 3600,
        "prio": null }
    ],
    "skipped": [
      { "line": 7, "reason": "an identical record already exists" }
    ],
    "errors": []
  }
}
  • creates: Records, die angelegt werden (bzw. beim Probelauf angelegt würden)
  • skipped: übersprungene Zeilen, z. B. weil ein identischer Record schon existiert oder die Zeile nicht unterstützt wird
  • errors: Zeilen, die die Validierung nicht bestehen, mit Zeilennummer und Meldung

Wiederhole die Anfrage mit "dry_run": false, um den Import zu übernehmen. Die Records aus creates werden nur angelegt, wenn errors leer ist; dann alle gemeinsam.

Zone löschen

DELETE /zones/{id} löscht die Zone samt aller Records endgültig.

Records

Methode Pfad Beschreibung
GET /zones/{zone_id}/records Die Records einer Zone auflisten
GET /records Die Records aller Zonen auflisten
POST /zones/{zone_id}/records Einen Record anlegen
PATCH /records/{id} Einen Record ändern
DELETE /records/{id} Einen Record löschen
PATCH /zones/{zone_id}/records/bulk_ttl Die TTL mehrerer Records setzen
POST /zones/{zone_id}/records/bulk_destroy Mehrere Records löschen
POST /records/{id}/dyndns DynDNS für einen Record einschalten
DELETE /records/{id}/dyndns DynDNS für einen Record ausschalten

GET /zones/{zone_id}/records liefert die Records einer Zone, sortiert nach Subdomain und Typ. GET /records liefert die Records aller Zonen, sortiert nach Zonenname, Subdomain und Typ; der optionale Query-Parameter zone_id beschränkt die Liste auf eine Zone. Automatisch gesetzte NS- und SOA-Records sind nicht enthalten.

Attribute eines Records

Attribut Beschreibung
id ID des Records
zone_id ID der Zone
zone_name Name der Zone
record_type A, AAAA, CNAME, MX, NS, PTR, SPF, SRV, TXT oder TLSA
subdomain Name relativ zur Zone, z. B. www; leer für die Zone selbst
content Wert des Records
ttl TTL in Sekunden
prio Priorität bei MX und SRV, sonst null
comment Freitext-Notiz oder null
dyndns_enabled Ob DynDNS eingeschaltet ist
dyndns_url DynDNS-Update-URL oder null
created_at Zeitpunkt der Erstellung
updated_at Zeitpunkt der letzten Änderung

Records auflisten

Die Records einer Zone abrufen:

curl "$XCLOUD_DNS_API/zones/$ZONE_ID/records" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN"
{
  "data": [
    {
      "id": "5f0c2d6e-8b1a-4f3e-9c2d-1a7b8e9f0c11",
      "zone_id": "0b7c4f0e-2a51-4c4e-9a7f-5d3c1e8b9f10",
      "zone_name": "example.com",
      "record_type": "MX",
      "subdomain": "",
      "content": "mail.example.com",
      "ttl": 3600,
      "prio": 10,
      "comment": "Primärer Mailserver",
      "dyndns_enabled": false,
      "dyndns_url": null,
      "created_at": "2026-09-14T10:25:03+02:00",
      "updated_at": "2026-09-14T10:25:03+02:00"
    },
    {
      "id": "9a3e7b21-4c5d-4e6f-8a9b-0c1d2e3f4a52",
      "zone_id": "0b7c4f0e-2a51-4c4e-9a7f-5d3c1e8b9f10",
      "zone_name": "example.com",
      "record_type": "A",
      "subdomain": "www",
      "content": "192.0.2.10",
      "ttl": 3600,
      "prio": null,
      "comment": null,
      "dyndns_enabled": false,
      "dyndns_url": null,
      "created_at": "2026-09-14T10:24:51+02:00",
      "updated_at": "2026-10-19T17:42:05+02:00"
    }
  ]
}

Record anlegen

POST /zones/{zone_id}/records legt einen Record in der Zone an.

Attribut Pflicht Beschreibung
record_type ja Record-Typ, siehe Tabelle unten
content ja Wert des Records, höchstens 20.000 Zeichen
subdomain nein Name relativ zur Zone; für die Zone selbst weglassen
ttl nein TTL in Sekunden, mindestens 60; Standard 3600
prio bei MX, SRV Priorität (Ganzzahl ab 0); nur bei MX und SRV
comment nein Freitext-Notiz, höchstens 255 Zeichen

Das Format des Werts hängt vom Record-Typ ab:

Typ content Hinweise
A IPv4-Adresse, z. B. 192.0.2.10 DynDNS möglich
AAAA IPv6-Adresse, z. B. 2001:db8::10 DynDNS möglich
CNAME Ziel-Hostname, z. B. shop.example.net Nicht auf der Zone selbst
MX Hostname des Mailservers, z. B. mail.example.com prio erforderlich
NS Hostname des Nameservers, z. B. ns1.example.net Delegiert die Subdomain
PTR Ziel-Hostname
SPF SPF-Richtlinie als Text
SRV Weight Port Ziel, z. B. 10 5060 sip.example.com prio erforderlich
TXT Freier Text, z. B. v=DMARC1; p=reject
TLSA Usage Selector Matching-Type Daten, z. B. 3 1 1 2b4f… Name _port._protokoll

Weitere Regeln:

  • Der Name eines TLSA-Records muss mit _port._protokoll beginnen, z. B. _443._tcp oder _25._tcp.mail.
  • Ein CNAME-Record muss der einzige Record mit seinem Namen sein.
  • Gib TXT- und SPF-Werte als reinen Text ohne umschließende Anführungszeichen an. Hostnamen kannst du mit oder ohne abschließenden Punkt angeben.
  • Unterhalb einer delegierten Subdomain sind nur NS-Records erlaubt, unterhalb einer virtuellen Subdomain alle Typen außer NS.

Ein MX-Record für die Zone selbst:

curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/records" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "record_type": "MX",
      "content": "mail.example.com",
      "prio": 10,
      "ttl": 3600,
      "comment": "Primärer Mailserver"
    }
  }'

Ein SRV-Record für SIP über TCP. Die Priorität gehört in prio, die übrigen drei Werte in content:

curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/records" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "record_type": "SRV",
      "subdomain": "_sip._tcp",
      "content": "5 5060 sip.example.com",
      "prio": 10,
      "ttl": 3600
    }
  }'

Ein TXT-Record mit einer DMARC-Richtlinie:

curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/records" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "record_type": "TXT",
      "subdomain": "_dmarc",
      "content": "v=DMARC1; p=reject; rua=mailto:dmarc@example.com",
      "ttl": 3600
    }
  }'

Ein TLSA-Record für einen Mailserver auf Port 25:

curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/records" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "record_type": "TLSA",
      "subdomain": "_25._tcp.mail",
      "content": "3 1 1 0c72ac70b745ac19998811b131d662c9ac69dbdbe7cb23e5b514b56664c5d3d6",
      "ttl": 3600
    }
  }'

Die Antwort enthält den angelegten Record in data.

Record ändern

PATCH /records/{id} ändert die Attribute subdomain, content, ttl, prio und comment. Attribute, die du weglässt, bleiben unverändert. Den Record-Typ kannst du nicht ändern; lösche dafür den Record und lege einen neuen an. Um einen Kommentar zu entfernen, schickst du null:

curl -X PATCH "$XCLOUD_DNS_API/records/$RECORD_ID" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "content": "198.51.100.20",
      "ttl": 300,
      "comment": null
    }
  }'

Mehrere Records ändern oder löschen

PATCH /zones/{zone_id}/records/bulk_ttl setzt eine gemeinsame TTL für mehrere Records einer Zone, POST /zones/{zone_id}/records/bulk_destroy löscht mehrere Records einer Zone. Beide nehmen höchstens 200 Record-IDs pro Anfrage an und sind atomar: Entweder werden alle Records geändert oder keiner.

Die TTL zweier Records vor einem Umzug senken:

curl -X PATCH "$XCLOUD_DNS_API/zones/$ZONE_ID/records/bulk_ttl" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "ids": [
        "5f0c2d6e-8b1a-4f3e-9c2d-1a7b8e9f0c11",
        "9a3e7b21-4c5d-4e6f-8a9b-0c1d2e3f4a52"
      ],
      "ttl": 300
    }
  }'

bulk_destroy nimmt dieselbe Struktur {"record": {"ids": [...]}} ohne ttl an.

DynDNS

DynDNS steht für A- und AAAA-Records zur Verfügung. POST /records/{id}/dyndns schaltet DynDNS ein und erzeugt eine neue Update-URL, die die Antwort in dyndns_url liefert. Ein erneuter Aufruf erzeugt eine neue URL und macht die bisherige ungültig. DELETE /records/{id}/dyndns schaltet DynDNS aus und macht die URL ungültig.

Um den Record zu aktualisieren, rufst du die Update-URL mit einer einfachen GET-Anfrage von dem Gerät aus auf, auf dessen IP-Adresse der Record zeigen soll. Ein Token ist nicht nötig, die URL selbst autorisiert die Anfrage:

curl "$DYNDNS_URL"

Der Record wird auf die IP-Adresse gesetzt, von der die Anfrage kommt. Die Adressfamilie muss zum Record-Typ passen: Ein A-Record erfordert eine IPv4-Anfrage (z. B. curl -4), ein AAAA-Record eine IPv6-Anfrage (curl -6); sonst wird die Anfrage mit 422 abgelehnt. Halte die URL geheim: Jeder, der sie kennt, kann den Record ändern.

Delegierte Subdomains

Methode Pfad Beschreibung
GET /zones/{zone_id}/sub_zones Delegierte und virtuelle Subdomains auflisten
POST /zones/{zone_id}/sub_zones Eine Subdomain anlegen
DELETE /zones/{zone_id}/sub_zones/{id} Eine Subdomain löschen

Eine Subdomain (sub_zone) legt fest, wie ein Teil der Zone verwaltet wird:

  • delegating: Die Subdomain wird an andere Nameserver delegiert. Darunter sind nur NS-Records erlaubt, also die Delegierung selbst.
  • virtual: Die Subdomain gruppiert Records nur und bleibt Teil der Zone. Darunter sind alle Record-Typen außer NS erlaubt.

Attribute einer Subdomain

Attribut Beschreibung
id ID der Subdomain
zone_id ID der Zone
name Name relativ zur Zone, z. B. shop
function delegating oder virtual
records_count Anzahl der Records unterhalb der Subdomain
created_at Zeitpunkt der Erstellung

Die Subdomains einer Zone abrufen:

curl "$XCLOUD_DNS_API/zones/$ZONE_ID/sub_zones" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN"
{
  "data": [
    {
      "id": "c4d5e6f7-0a1b-4c2d-8e3f-4a5b6c7d8e93",
      "zone_id": "0b7c4f0e-2a51-4c4e-9a7f-5d3c1e8b9f10",
      "name": "shop",
      "function": "delegating",
      "records_count": 2,
      "created_at": "2026-10-02T14:08:44+02:00"
    }
  ]
}

Subdomain anlegen und löschen

Eine delegierte Subdomain shop.example.com anlegen und danach die NS-Records der Delegierung:

curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/sub_zones" \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sub_zone": {"name": "shop", "function": "delegating"}}'

for ns in ns1.shop-hosting.example ns2.shop-hosting.example; do
  curl -X POST "$XCLOUD_DNS_API/zones/$ZONE_ID/records" \
    -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"record\": {\"record_type\": \"NS\", \"subdomain\": \"shop\", \"content\": \"$ns\"}}"
done

Der name ist relativ zur Zone und darf nicht die Zone selbst sein. Bereits vorhandene Records unterhalb der neuen Subdomain werden ihr zugeordnet. Löschst du eine Subdomain, bleiben ihre Records in der Zone. Eine delegierte Subdomain lässt sich erst löschen, wenn sie keine NS-Records mehr enthält.