Skip to content

xCloud DNS API

Overview

With the xCloud DNS REST API, you manage zones, records, domains, and contacts from scripts, CI pipelines, or your own tools, just as in the DNS section of the web app. This documentation covers the following areas:

  • Zones and records: create zones, manage records of all types, DynDNS, DNSSEC, delegated subdomains, zone file import and export
  • Domains and contacts: check availability, register and transfer domains, change nameservers and contacts, auto-renew, transfer lock, auth code, and manage contacts (handles)
  • Webhooks: events that xCloud sends to your own URL
  • Prices: the public price list for domains

The API uses JSON for requests and responses and standard HTTP methods: GET reads, POST creates or triggers actions, PATCH changes, and DELETE deletes.

Base URL

All paths in this documentation are relative to the following base URL:

https://app.xcloud.eu/api/dns/v1

For example, the zone list is available at https://app.xcloud.eu/api/dns/v1/zones.

Authentication

The API authenticates requests with API tokens. Create a token in the web app on the API tokens tab of the DNS section (see API tokens). Tokens start with xcdns_ and are only shown once when they are created. Store them securely, e.g. in a password manager or your CI system's secret store.

Each token belongs to the project in which you created it. It only acts on the zones, records, domains, and contacts of that project; the paths therefore don't contain the project.

Send the token in the Authorization header of every request:

export XCLOUD_DNS_TOKEN='xcdns_...'

curl https://app.xcloud.eu/api/dns/v1/zones \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN"

API tokens and notifications are managed in the web app only. An API token has no access to them; such requests are rejected with 403.

Permissions

Each token has an access level for three areas: No access, Read, or Write.

Area Covers
Records Zones, records, and delegated subdomains (/zones, /records)
Domains Domains (/domains)
Contacts Contacts (/handles)

Write includes Read. With Read, a token may use GET requests as well as the domain availability check (POST /domains/check); all other requests require Write.

Additional restrictions you can set per token:

  • Zones: a token restricted to selected zones only sees those zones and their records. Lists contain only the permitted zones; requests for other zones are rejected with 403. Without a restriction, the token applies to all zones, including future ones.
  • IP allowlist: a token with an IP allowlist is only accepted from the listed IP addresses or CIDR ranges; requests from other addresses are rejected with 401.
  • Validity: an expired or revoked token is rejected with 401, just like a missing or unknown token.

Requests

  • Send request bodies as JSON with the header Content-Type: application/json.
  • Most request bodies wrap their attributes in an object named after the resource, e.g. {"zone": {...}}, {"record": {...}}, {"domain": {...}}, or {"handle": {...}}.
  • Resources are identified by their id (a UUID string), not by their name. How to find the ID for a name is described under Lists.

Responses

Successful responses carry status 200 and wrap their content in data:

{
  "data": {
    "id": "0b7c4f0e-2a51-4c4e-9a7f-5d3c1e8b9f10",
    "name": "example.com",
    "...": "..."
  }
}

Timestamps use ISO 8601 format. You'll find complete example responses with the individual resources.

DELETE requests that delete something return 200 without content. This applies to zones, records, subdomains, and contacts, and also to POST /zones/{zone_id}/records/bulk_destroy. DELETE requests that switch a feature off or cancel a domain, on the other hand, return the changed resource in data:

  • DELETE /domains/{id} and DELETE /domains/{id}/auto_renew (cancel a domain)
  • DELETE /domains/{id}/transfer_lock (remove the transfer lock)
  • DELETE /zones/{id}/dnssec (switch DNSSEC off)
  • DELETE /records/{id}/dyndns (switch DynDNS off)

Like all bulk actions for domains, DELETE /domains/bulk_auto_renew returns the domains in data and rejected domains in errors (see Bulk actions).

Lists

Lists return an array in data and are always complete: there is no pagination and, apart from the zone_id parameter of GET /records, no filtering. To find the ID of a zone or domain by its name, fetch the list and look up the name on your side, e.g. with jq:

curl -s https://app.xcloud.eu/api/dns/v1/zones \
  -H "Authorization: Bearer $XCLOUD_DNS_TOKEN" |
  jq -r '.data[] | select(.name == "example.com") | .id'

Asynchronous operations

Some operations take effect in the background. The response then describes the state directly after the request; query the resource again later to see the result:

  • Zones start in state pending and become active once they are set up on the nameservers.
  • Domain registrations, transfers, and changes are processed by the registry. The domain's state shows the progress (ordering, transfer_pending, update_pending, then registered or failed).
  • DNSSEC signing, the delegation check, and the zone transfer of secondary zones are queued.

Record changes are active on all nameservers after 60 seconds at the latest.

Errors

Errors are returned with an HTTP status code and an error object:

Status Meaning
400 A required parameter is missing, or a list (e.g. of IDs) is empty or too long
401 No valid token, or the sender's address is not in the token's IP allowlist
402 A validated payment method is required
403 The token lacks the required permission or zone, or the action is not allowed
404 The resource does not exist or does not belong to the token's project
422 The request is invalid, e.g. a validation error or a disallowed state

If DNS is locked in the token's project, all requests are answered with 404.

Validation errors (422) list each problem with the affected field in name:

{
  "error": {
    "status": "422",
    "errors": [
      { "name": "content", "message": "is not a valid IPv4 address" }
    ]
  }
}

General errors carry a message instead:

{
  "error": {
    "status": "403",
    "message": "Forbidden"
  }
}

Webhooks

xCloud can call your own URL when something happens with your domains and zones. You set the webhook URL and the events for which xCloud calls it in the web app on the Notifications tab (see Notifications). The URL must start with https://.

For each event, xCloud sends a POST request with the header Content-Type: application/json and the following body:

{
  "event": "transfer_update",
  "occurred_at": "2026-10-20T09:14:02+02:00",
  "data": {
    "domain": "example.com",
    "status": "completed",
    "detail": ""
  }
}
  • event: name of the event, see below
  • occurred_at: time of the event
  • data: details of the event; which fields it contains depends on the event

Events

The name of the event in the web app is given in parentheses.

  • expiry (Domain expiring): a domain with auto-renew switched off will be released within the configured expiry lead time. The event is sent once per term. Fields: domain, expires_on (release date).
  • operation_failed (Renewal failed): ordering a domain has failed. Fields: domain, operation (registration), detail (error message).
  • delegation_broken (Delegation broken): the delegation check of a zone was fine so far and now finds different nameservers. Fields: zone, observed (the nameservers found at the registry, comma-separated).
  • transfer_update (Transfer status): a transfer to xCloud has completed (status: completed) or could not be started (status: failed, reason in detail). Fields: domain, status, detail.
  • zone_changed (Zone changed): records of a zone were created, changed, deleted, or imported, in the web app or through the API. Fields: zone.
  • dnssec_update (DNSSEC key expiring): xCloud has published the DS records of a domain registered through xCloud at the registry. Fields: zone, status (ds_published).

Signature

Webhooks are not signed: the request contains neither a signature nor credentials. Use a URL that is hard to guess, and treat the content as a hint. If needed, query the authoritative state through the API.

Prices

GET /prices returns the prices for domains per extension (TLD). The endpoint is public and does not require a token:

curl https://app.xcloud.eu/api/dns/v1/prices

The response contains one entry per extension in data:

Attribute Description
tld Extension without the dot, e.g. de
registration_fee Price for the registration
transfer_fee Price for the transfer to xCloud
annual_fee Annual price
restore_fee Price for restoring a deleted domain

Prices are returned as strings with six decimal places, or as null if no price is set for the extension.