curl --request POST \
--url https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership \
--header 'Authorization: Bearer <token>'import requests
url = "https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"zone": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"crn": "crn:dns::my-account:zone/example.com",
"name": "example.com",
"description": "Public zone for the marketing site",
"nameservers": [
"028t5cy4tqkff.triton.dns.basaltic.cloud",
"028t5cy4tqkff.proteus.dns.basaltic.cloud"
],
"soa": {
"primary_ns": "028t5cy4tqkff.triton.dns.basaltic.cloud",
"admin_email": "hostmaster@basaltic.sh",
"refresh": 10800,
"retry": 3600,
"expire": 604800,
"minimum": 3600
},
"visibility": "public",
"dnssec": {
"enabled": true,
"ksk_key_tag": 12345,
"zsk_key_tag": 54321,
"algorithm": 13,
"ds_records": [
{
"key_tag": 12345,
"algorithm": 13,
"digest_type": 2,
"digest": "B8FA03AA1BB49CC702C32C5C3FE6061BBDD0808371F43A28A8E8BE2738CC9861",
"rdata": "12345 13 2 B8FA03AA1BB49CC702C32C5C3FE6061BBDD0808371F43A28A8E8BE2738CC9861"
}
]
},
"tags": {
"environment": "production",
"team": "backend"
},
"ownership": {
"verified": false,
"verified_at": "2026-01-15T09:30:00Z",
"checked_at": "2026-04-14T09:30:00Z",
"recheck_deadline": "2026-04-28T09:30:00Z"
}
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "IDEMPOTENCY_KEY_REUSED",
"message": "This Idempotency-Key was already used with a different request payload",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Verify zone ownership
Check the domain’s delegation and, if it names this zone, start serving it.
A zone created with no existing zone above it on the platform does not
resolve until ownership is proved. Set the zone’s nameservers at your
registrar, then call this. There is no record to publish: those names
carry a label unique to this zone, so delegating to them requires
control of the domain at its registrar — which is what stops someone
claiming a subdomain of a domain they do not own and sitting in your
subtree, and what stops an abandoned delegation from being redeemed by
whoever next creates the name.
A zone created underneath a zone you already own inherits its proof and is served immediately; calling this on one returns success with nothing to do. Idempotent, so a client may poll it.
This is also the call that clears a failing re-proof. Ownership is
re-confirmed periodically, and a zone whose re-confirmation is failing
carries an ownership.recheck_deadline; on such a zone this runs the
check for real and clears the deadline when it passes. So unlike the
already-verified case above, a verified zone can still answer 400 here.
curl --request POST \
--url https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership \
--header 'Authorization: Bearer <token>'import requests
url = "https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://dns.basaltic.sh/v1/zones/{zone_id}/verify-ownership")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"zone": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"crn": "crn:dns::my-account:zone/example.com",
"name": "example.com",
"description": "Public zone for the marketing site",
"nameservers": [
"028t5cy4tqkff.triton.dns.basaltic.cloud",
"028t5cy4tqkff.proteus.dns.basaltic.cloud"
],
"soa": {
"primary_ns": "028t5cy4tqkff.triton.dns.basaltic.cloud",
"admin_email": "hostmaster@basaltic.sh",
"refresh": 10800,
"retry": 3600,
"expire": 604800,
"minimum": 3600
},
"visibility": "public",
"dnssec": {
"enabled": true,
"ksk_key_tag": 12345,
"zsk_key_tag": 54321,
"algorithm": 13,
"ds_records": [
{
"key_tag": 12345,
"algorithm": 13,
"digest_type": 2,
"digest": "B8FA03AA1BB49CC702C32C5C3FE6061BBDD0808371F43A28A8E8BE2738CC9861",
"rdata": "12345 13 2 B8FA03AA1BB49CC702C32C5C3FE6061BBDD0808371F43A28A8E8BE2738CC9861"
}
]
},
"tags": {
"environment": "production",
"team": "backend"
},
"ownership": {
"verified": false,
"verified_at": "2026-01-15T09:30:00Z",
"checked_at": "2026-04-14T09:30:00Z",
"recheck_deadline": "2026-04-28T09:30:00Z"
}
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "IDEMPOTENCY_KEY_REUSED",
"message": "This Idempotency-Key was already used with a different request payload",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}dns:VerifyZoneOwnership. See DNS permissions for the full list, what each one covers, and an example policy.Authorizations
An OAuth 2.0 bearer token, sent as Authorization: Bearer <token>.
This is the recommended way to authenticate.
Get one by exchanging a service account's access key pair at
POST /v1/oauth/token with grant_type=client_credentials. It is the
standard client-credentials grant, so any OAuth-aware library will
obtain and refresh it for you.
curl -s -u "$KEY_ID:$SECRET" -d grant_type=client_credentials \
https://iam.basaltic.sh/v1/oauth/token
Tokens last an hour by default. The same access key pair is separately your AWS SigV4 credential for the S3-compatible object endpoint, which speaks nothing else.
Headers
Optional client-generated key that makes a create replay-safe. Retrying a request with the same key returns the original outcome verbatim instead of creating a duplicate resource. Reusing a key with a different request body is rejected (422); a request whose key is still being processed returns 409. Records are honored for 24 hours. Use a UUID or similarly unique token.
255Path Parameters
DNS Zone ID
Response
Ownership verified; the zone now resolves
Show child attributes
Show child attributes

