DNS
Zones and records at your DNS providers, templates and dynamic IP sources. Record changes are recorded in the zone history and can be undone.
Common tasks
Only in the app: Adding or deleting zones and moving a zone to another provider.
How it works in the app: help article.
Scopes: Read with dns.read or monitoring.read, change with dns.write. See Tokens and scopes.
DNS 4 · DNS changes 1 · DNS templates 3 · DNS zones 26 · Dynamic IPs 4
Every endpoint below takes a token in the Authorization: Bearer header and answers JSON. Errors use the shared format in Errors. Base address: https://api.infranest.io/api.
DNS
Plan moving records to another server
Shows what moving A/AAAA records from one server to another would change: each record with its new value, the records left out and why, and the monitors that would follow. Send from_server_id or record_ids, and optionally to_server_id; changes nothing.
/organizations/{organization}/dns/address-movedns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Query parameters
from_server_idintegeroptionalThe server being left. Send this or record_ids.
record_idsstringoptionalComma-separated A/AAAA record ids, as the cross-zone search returns them.
to_server_idintegeroptionalThe server to move to; without it the records are listed with no new values.
Request
curl "https://api.infranest.io/api/organizations/42/dns/address-move" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns/address-move" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns/address-move', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns/address-move",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns/address-move', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns/address-move", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"from": {
"server": {
"id": 1,
"name": "web-01"
},
"addresses": [
"198.51.100.10",
"2001:db8:1c1e:5a10::/64"
]
},
"to": null,
"targets": [
{
"id": 13,
"name": "analytics-01",
"ipv4": "192.0.2.80",
"ipv6": null,
"ipv6_basis": null
},
{
"id": 3,
"name": "api-01",
"ipv4": "198.51.100.20",
"ipv6": "2001:db8:1c1e:5a20::1",
"ipv6_basis": "default"
}
],
"records": [
{
"record_id": 26,
"zone_id": 5,
"zone_name": "northwind-docs.com",
"zone_provider": "cloudflare",
"name": "@",
"fqdn": "northwind-docs.com",
"type": "A",
"value": "198.51.100.10",
"ttl": 300,
"proxied": true,
"family": 4,
"new_value": null,
"excluded": "read_only",
"aliases": [
{
"zone_id": 5,
"record_id": 28,
"fqdn": "api.northwind-docs.com"
}
]
},
{
"record_id": 27,
"zone_id": 5,
"zone_name": "northwind-docs.com",
"zone_provider": "cloudflare",
"name": "www",
"fqdn": "www.northwind-docs.com",
"type": "A",
"value": "198.51.100.10",
"ttl": 300,
"proxied": true,
"family": 4,
"new_value": null,
"excluded": "read_only",
"aliases": []
}
],
"monitors": {
"follow": [],
"by_name": []
},
"ttl": {
"long": 0,
"longest": null,
"threshold": 3600,
"lowered": 300
}
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List DNS host moves
The 200 most recent moves of a domain’s DNS to another DNS host, newest first, each with its domain, target connection, current state and timestamps.
/organizations/{organization}/dns/movesdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dns/moves" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns/moves" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns/moves', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns/moves",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns/moves', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns/moves", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"data": []
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get the DNS overview
A summary across all your DNS zones: what needs your attention, grouped by cause, the recent changes, where your DNS is hosted and what is worth tidying up. Changes nothing.
/organizations/{organization}/dns/overviewdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dns/overview" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns/overview" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns/overview', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns/overview",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns/overview', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns/overview", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"summary": {
"state": "needs_you",
"critical": false,
"zones": 13,
"needing_you": 2,
"fine": 11,
"live_match": 12,
"live_checked": 12,
"records": 88,
"hosts": 6,
"last_synced_at": "2026-01-15T11:53:00+00:00"
},
"needs": [
{
"key": "advisor:6:dnssec",
"cause": "advisor",
"critical": false,
"subject": "northwind.de",
"zones": [
{
"id": 6,
"name": "northwind.de"
}
],
"params": [],
"since": null,
"due_at": null,
"route": "/dns/6",
"finding": {
"id": "dnssec",
"status": "error",
"variant": "error"
},
"halt": null,
"attention_keys": [
"dnssec_broken:9"
]
},
{
"key": "advisor:3:cf_ssl_mode",
"cause": "advisor",
"critical": false,
"subject": "northwind-app.dev",
"zones": [
{
"id": 3,
"name": "northwind-app.dev"
}
],
"params": [],
"since": null,
"due_at": null,
"route": "/dns/3",
"finding": {
"id": "cf_ssl_mode",
"status": "error",
"value": "flexible"
},
"halt": null,
"attention_keys": [
"dns_zone_finding:3:cf_ssl_mode"
]
}
],
"decided": [],
"changes": {
"from": "2026-01-08T12:00:00+00:00",
"entries": [
{
"key": "audit:2",
"kind": "audit",
"action": "dns_record.created",
"at": "2026-01-15T07:00:00+00:00",
"zones": [
{
"id": null,
"name": ""
}
],
"actor": {
"type": "user",
"name": "Sam Okoye"
},
"outside": false,
"count": 1,
"params": {
"name": "@",
"type": ""
},
"batch": null,
"writable": false,
"provider": null
},
{
"key": "audit:8",
"kind": "audit",
"action": "dns_record.updated",
"at": "2026-01-12T11:00:00+00:00",
"zones": [
{
"id": null,
"name": ""
}
],
"actor": {
"type": "user",
"name": "Tom Bakker"
},
"outside": false,
"count": 1,
"params": {
"name": "@",
"type": ""
},
"batch": null,
"writable": false,
"provider": null
}
],
"truncated": false
},
"hosts": [
{
"key": "cred:2",
"name": "Cloudflare DNS",
"provider": "cloudflare",
"credential_id": 2,
"zones": 5,
"connected": true,
"halt": null
},
{
"key": "cred:1",
"name": "Hetzner — Production",
"provider": "hetzner",
"credential_id": 1,
"zones": 2,
"connected": true,
"halt": null
}
],
"domains_without_zone": 5,
"recommendations": []
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Search records across all zones
Finds DNS records in any of your zones that match the q query parameter, by name, value or address, grouped by zone with the reason each one matched. A query shorter than two characters returns nothing with too_short set; at most 100 records are returned, with truncated set when there were more.
/organizations/{organization}/dns/searchdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dns/search" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns/search" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns/search', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns/search",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns/search', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns/search", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"query": "",
"too_short": true,
"min_length": 2,
"total": 0,
"truncated": false,
"zones": [],
"address_record_ids": []
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
DNS changes
Undo a DNS change
Undoes one recorded change (change_id) or every change in a batch (batch_id), even across zones, pushing each one to the DNS provider. A change whose record has changed since, or that a dynamic IP keeps up to date, is skipped with its reason; answers 404 when nothing matches.
/organizations/{organization}/dns-changes/undodns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Bodyapplication/json
change_idintegeroptionalThe change to undo. Send this or batch_id.
batch_idstringoptionalEvery change in this batch, undone as one.
Request
curl -X POST "https://api.infranest.io/api/organizations/42/dns-changes/undo" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"change_id":42,"batch_id":"9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c"}'$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
$body = @'
{
"change_id": 42,
"batch_id": "9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c"
}
'@
Invoke-RestMethod -Method Post -Uri "https://api.infranest.io/api/organizations/42/dns-changes/undo" -Headers $headers -ContentType "application/json" -Body $body<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.infranest.io/api/organizations/42/dns-changes/undo', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
'json' => [
'change_id' => 42,
'batch_id' => '9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c',
],
]);
echo $response->getBody();import os
import requests
response = requests.post(
"https://api.infranest.io/api/organizations/42/dns-changes/undo",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
json={
"change_id": 42,
"batch_id": "9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-changes/undo', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"change_id": 42,
"batch_id": "9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c"
}),
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
req, err := http.NewRequest("POST", "https://api.infranest.io/api/organizations/42/dns-changes/undo", strings.NewReader(`{"change_id":42,"batch_id":"9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c"}`))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "idempotency_in_progress",
"message": "A request with this Idempotency-Key is still being handled.",
"hint": "Wait a few seconds, then send the same request with the same key again to get its result.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#idempotency_in_progress",
"reference": "k3j9x0b2qa"
}{
"code": "validation_failed",
"message": "The change id field is required.",
"hint": "Fix the fields listed in \"errors\" and send the request again.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#validation_failed",
"errors": {
"change_id": [
"The change id field is required."
]
},
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
409 idempotency_in_progress
422 validation_failed idempotency_key_reused
429 rate_limited
No success response is recorded for this endpoint yet.
DNS templates
List DNS templates
The built-in DNS templates followed by your organization’s custom templates, each with its records, plus the TTL values a template can use.
/organizations/{organization}/dns-templatesdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dns-templates" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-templates" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-templates', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-templates",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-templates', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-templates", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"builtin": [
{
"id": null,
"slug": "google-workspace",
"name": "Google Workspace",
"description": "MX records, SPF, and DMARC starter for Google Workspace email.",
"is_builtin": true,
"records": [
{
"type": "MX",
"name": "@",
"value": "aspmx.l.google.com.",
"ttl": 3600,
"priority": 1
},
{
"type": "MX",
"name": "@",
"value": "alt1.aspmx.l.google.com.",
"ttl": 3600,
"priority": 5
}
]
},
{
"id": null,
"slug": "microsoft-365",
"name": "Microsoft 365",
"description": "MX record, SPF, Autodiscover CNAME, and DMARC starter for Microsoft 365. The MX is filled in from the domain.",
"is_builtin": true,
"records": [
{
"type": "MX",
"name": "@",
"value": "{zone_dashed}.mail.protection.outlook.com.",
"ttl": 3600,
"priority": 0
},
{
"type": "TXT",
"name": "@",
"value": "v=spf1 include:spf.protection.outlook.com -all",
"ttl": 3600
}
]
}
],
"custom": [
{
"id": 1,
"organization_id": 1,
"name": "Google Workspace mail",
"description": "MX, SPF and DKIM for a domain that sends mail through Google Workspace.",
"slug": "google-workspace-mail",
"is_builtin": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"deleted_at": null,
"purge_after": null,
"purge_reminder_sent_at": null,
"deleted_by_id": null,
"deleted_by_label": null,
"deletion_reason": null,
"records": [
{
"id": 1,
"dns_template_id": 1,
"type": "MX",
"name": "@",
"value": "aspmx.l.google.com",
"ttl": 3600,
"priority": 1,
"proxied": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z"
},
{
"id": 2,
"dns_template_id": 1,
"type": "MX",
"name": "@",
"value": "alt1.aspmx.l.google.com",
"ttl": 3600,
"priority": 5,
"proxied": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z"
}
]
}
],
"ttl_options": [
1,
30
]
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Apply a template to a zone
Creates or updates the template’s records in the zone and pushes each change to the DNS provider; with overwrite=false, records that already exist with the same type and name are left alone. dry_run=1 returns the changes without making any. A CAA record that would block a certificate’s renewal is refused with 409 unless confirm_caa_impact is sent.
/organizations/{organization}/dns-templates/{templateKey}/apply/{zone}dns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
templateKeyintegerrequiredThe template key's ID.
zoneintegerrequiredThe zone's ID.
Query parameters
dry_runbooleanoptionalAnswer with the changes this would make, and make none.
Bodyapplication/json
overwriteboolean, nullableoptionalconfirm_caa_impactboolean, nullableoptionalApply even though a template CAA record blocks a certificate's renewing CA — after a 409.
Request
curl -X POST "https://api.infranest.io/api/organizations/42/dns-templates/1/apply/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"overwrite":true,"confirm_caa_impact":true}'$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
$body = @'
{
"overwrite": true,
"confirm_caa_impact": true
}
'@
Invoke-RestMethod -Method Post -Uri "https://api.infranest.io/api/organizations/42/dns-templates/1/apply/1" -Headers $headers -ContentType "application/json" -Body $body<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.infranest.io/api/organizations/42/dns-templates/1/apply/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
'json' => [
'overwrite' => true,
'confirm_caa_impact' => true,
],
]);
echo $response->getBody();import os
import requests
response = requests.post(
"https://api.infranest.io/api/organizations/42/dns-templates/1/apply/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
json={
"overwrite": True,
"confirm_caa_impact": True,
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-templates/1/apply/1', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"overwrite": true,
"confirm_caa_impact": true
}),
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
req, err := http.NewRequest("POST", "https://api.infranest.io/api/organizations/42/dns-templates/1/apply/1", strings.NewReader(`{"overwrite":true,"confirm_caa_impact":true}`))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"added": 1,
"overwritten": 0,
"failed": 0,
"error": null,
"skipped": 2,
"skipped_reason": "The A record \"@\" needs a value the template cannot know ({server_ipv4}). Add it yourself. The AAAA record \"@\" needs a value the template cannot know ({server_ipv6}). Add it yourself.",
"message": "Applied Static Site: 1 records added, 0 updated. 2 skipped: The A record \"@\" needs a value the template cannot know ({server_ipv4}). Add it yourself. The AAAA record \"@\" needs a value the template cannot know ({server_ipv6}). Add it yourself."
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "idempotency_in_progress",
"message": "A request with this Idempotency-Key is still being handled.",
"hint": "Wait a few seconds, then send the same request with the same key again to get its result.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#idempotency_in_progress",
"reference": "k3j9x0b2qa"
}{
"code": "validation_failed",
"message": "The overwrite field is required.",
"hint": "Fix the fields listed in \"errors\" and send the request again.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#validation_failed",
"errors": {
"overwrite": [
"The overwrite field is required."
]
},
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
409 idempotency_in_progress
422 validation_failed idempotency_key_reused
429 rate_limited
Preview applying a template
Shows each of the template’s records for the zone with its status (such as add, overwrite or unchanged), the existing value it would replace and the TTL the zone will actually get. templateKey is a built-in template’s slug or a custom template’s id; changes nothing.
/organizations/{organization}/dns-templates/{templateKey}/preview/{zone}dns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
templateKeyintegerrequiredThe template key's ID.
zoneintegerrequiredThe zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-templates/1/preview/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-templates/1/preview/1" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-templates/1/preview/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-templates/1/preview/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-templates/1/preview/1', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-templates/1/preview/1", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
[
{
"type": "A",
"name": "@",
"value": "{server_ipv4}",
"ttl": 3600,
"priority": null,
"proxied": false,
"unfilled": "{server_ipv4}",
"status": "skipped",
"existing_value": null,
"reason": "The A record \"@\" needs a value the template cannot know ({server_ipv4}). Add it yourself."
},
{
"type": "AAAA",
"name": "@",
"value": "{server_ipv6}",
"ttl": 3600,
"priority": null,
"proxied": false,
"unfilled": "{server_ipv6}",
"status": "skipped",
"existing_value": null,
"reason": "The AAAA record \"@\" needs a value the template cannot know ({server_ipv6}). Add it yourself."
}
]{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
DNS zones
List DNS zones
Your DNS zones, paginated with page and per_page (up to 200), each with its provider, record count, tags and delegation state. Filter with search, credential, tag or status (active, stale, drift for records changed at the provider, or security) and sort with sort (zone, provider, records, synced) and dir; meta.counts holds the totals.
/organizations/{organization}/dns-zonesdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"current_page": 1,
"data": [
{
"id": 11,
"organization_id": 1,
"domain_id": 20,
"name": "northwind-api.dev",
"provider": "openprovider",
"provider_zone_id": "zone-210",
"status": "pending",
"last_synced_at": "2026-01-15T10:24:00.000000Z",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"provider_credential_id": 7,
"ns_live": [
"ns1.openprovider.nl",
"ns2.openprovider.be"
],
"ns_checked_at": "2026-01-15T11:53:00.000000Z",
"security_status": "info",
"ns_mismatch_since": null,
"provider_modified_at": null,
"price_monthly": "0.00",
"price_source": "manual",
"price_currency": "EUR",
"price_amount": "0.0000",
"billing_interval": "monthly",
"interval_count": 1,
"live_mismatch_count": 0,
"live_checked_at": "2026-01-15T11:53:00.000000Z",
"live_mismatch_since": null,
"dnssec_pending": null,
"dnssec_pending_since": null,
"provider_settings": null,
"provider_settings_checked_at": null,
"default_ttl": null,
"registrar_name": null,
"registrar_checked_at": null,
"records_count": 7,
"custom_fields": [],
"delegation": {
"state": "matches",
"reason": null,
"live": [
"ns1.openprovider.nl",
"ns2.openprovider.be"
],
"dnssec": null,
"belongs_to": "openprovider",
"checked_at": "2026-01-15T11:53:00.000000Z"
},
"can_sign": false,
"sync_halt": null,
"provider_credential": {
"id": 7,
"organization_id": 1,
"provider": "openprovider",
"label": "Openprovider — Reseller",
"enabled": true,
"verified_at": "2025-10-15T12:00:00.000000Z",
"last_synced_at": "2026-01-15T11:48:00.000000Z",
"reconnect_required_at": null,
"refused_at": null
},
"tags": []
},
{
"id": 3,
"organization_id": 1,
"domain_id": 3,
"name": "northwind-app.dev",
"provider": "cloudflare",
"provider_zone_id": "zone-202",
"status": "active",
"last_synced_at": "2026-01-15T11:48:00.000000Z",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"provider_credential_id": 2,
"ns_live": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
],
"ns_checked_at": "2026-01-15T11:53:00.000000Z",
"security_status": "error",
"ns_mismatch_since": null,
"provider_modified_at": null,
"price_monthly": "4.00",
"price_source": "manual",
"price_currency": "EUR",
"price_amount": "4.0000",
"billing_interval": "monthly",
"interval_count": 1,
"live_mismatch_count": 0,
"live_checked_at": "2026-01-15T11:53:00.000000Z",
"live_mismatch_since": null,
"dnssec_pending": null,
"dnssec_pending_since": null,
"provider_settings": {
"ssl": "flexible",
"min_tls_version": "1.0",
"security_header": {
"strict_transport_security": {
"enabled": false
}
},
"always_use_https": "off",
"development_mode": "on",
"development_mode_expires_at": "2026-01-15T14:00:00.000000Z"
},
"provider_settings_checked_at": "2026-01-15T11:40:00.000000Z",
"default_ttl": null,
"registrar_name": null,
"registrar_checked_at": null,
"records_count": 8,
"custom_fields": [],
"delegation": {
"state": "matches",
"reason": null,
"live": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
],
"dnssec": null,
"belongs_to": "cloudflare",
"checked_at": "2026-01-15T11:53:00.000000Z"
},
"can_sign": true,
"sync_halt": null,
"provider_credential": {
"id": 2,
"organization_id": 1,
"provider": "cloudflare",
"label": "Cloudflare DNS",
"enabled": true,
"verified_at": "2025-10-15T12:00:00.000000Z",
"last_synced_at": "2026-01-15T11:48:00.000000Z",
"reconnect_required_at": null,
"refused_at": null
},
"tags": []
}
],
"first_page_url": "http://localhost/api/organizations/1/dns-zones?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http://localhost/api/organizations/1/dns-zones?page=1",
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://localhost/api/organizations/1/dns-zones?page=1",
"label": "1",
"page": 1,
"active": true
}
],
"next_page_url": null,
"path": "http://localhost/api/organizations/1/dns-zones",
"per_page": 25,
"prev_page_url": null,
"to": 13,
"total": 13,
"meta": {
"locked_ids": [],
"counts": {
"total": 13,
"active": 12,
"stale": 0,
"drift": 0,
"security": 4
},
"integrations": [
{
"id": 2,
"provider": "cloudflare",
"label": "Cloudflare DNS"
},
{
"id": 4,
"provider": "dynadot",
"label": "Dynadot Registrar"
}
],
"tags": [],
"custom_fields": []
}
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get a DNS zone
One zone with all its records, its provider connection, whether record changes can be pushed to the provider, its delegation and DNSSEC details, and findings such as records pointing at released addresses. Opening it may queue fresh nameserver and live-DNS checks in the background.
/organizations/{organization}/dns-zones/{dnsZone}dns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"id": 1,
"organization_id": 1,
"domain_id": 1,
"name": "northwind.com",
"provider": "cloudflare",
"provider_zone_id": "zone-200",
"status": "active",
"last_synced_at": "2026-01-15T11:48:00.000000Z",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"provider_credential_id": 2,
"ns_live": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
],
"ns_checked_at": "2026-01-15T11:53:00.000000Z",
"security_status": "warn",
"ns_mismatch_since": null,
"provider_modified_at": null,
"price_monthly": "4.00",
"price_source": "manual",
"price_currency": "EUR",
"price_amount": "4.0000",
"billing_interval": "monthly",
"interval_count": 1,
"live_mismatch_count": 0,
"live_checked_at": "2026-01-15T11:53:00.000000Z",
"live_mismatch_since": null,
"dnssec_pending": null,
"dnssec_pending_since": null,
"provider_settings": {
"ssl": "strict",
"min_tls_version": "1.2",
"security_header": {
"strict_transport_security": {
"enabled": true
}
},
"always_use_https": "on",
"development_mode": "off"
},
"provider_settings_checked_at": "2026-01-15T11:40:00.000000Z",
"default_ttl": null,
"registrar_name": null,
"registrar_checked_at": null,
"records_count": 10,
"sync_halt": null,
"records": [
{
"id": 1,
"dns_zone_id": 1,
"type": "A",
"name": "@",
"value": "198.51.100.10",
"ttl": 300,
"priority": null,
"provider_record_id": "rec-200-0",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": true,
"live_values": null,
"live_checked_at": null,
"value_is_placeholder": false,
"live_mismatch": null
},
{
"id": 2,
"dns_zone_id": 1,
"type": "A",
"name": "www",
"value": "198.51.100.10",
"ttl": 300,
"priority": null,
"provider_record_id": "rec-200-1",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": true,
"live_values": null,
"live_checked_at": null,
"value_is_placeholder": false,
"live_mismatch": null
}
],
"provider_credential": {
"id": 2,
"organization_id": 1,
"provider": "cloudflare",
"label": "Cloudflare DNS",
"verified_at": "2025-10-15T12:00:00.000000Z",
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"enabled": true,
"enabled_capabilities": null,
"disabled_capabilities": null,
"error_email_enabled": true,
"disabled_operations": null,
"alerts_muted_until": null,
"spending_capacity": null,
"spending_checked_at": null,
"scope_check": null,
"scope_checked_at": null,
"removing_at": null,
"last_synced_at": "2026-01-15T11:48:00.000000Z",
"reconnect_required_at": null,
"account_identity": null,
"refused_at": null,
"refusal_checked_at": null
},
"tags": [],
"record_types": [
"A",
"AAAA"
],
"ttl_options": [
1,
120
],
"can_push_records": false,
"delegation": {
"state": "matches",
"reason": null,
"live": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
],
"dnssec": false,
"belongs_to": "cloudflare",
"checked_at": "2026-01-15T11:53:00.000000Z"
},
"proxiable_record_types": [],
"can_sign": true,
"signing_reason": null,
"dnssec_plan": {
"shape": "sign_then_paste",
"dns": {
"provider": "cloudflare",
"can_sign": true,
"exposes_key": true,
"reason": null
},
"registrar": {
"provider": "Namecheap",
"known": true,
"can_publish": false,
"help_url": null,
"fields": [
"key_tag",
"flags"
]
}
},
"managed_hostnames": [],
"posture": [
{
"id": "cf_ssl_mode",
"status": "pass",
"value": "strict"
},
{
"id": "cf_min_tls",
"status": "pass",
"value": "1.2"
}
],
"dangling_addresses": [],
"unclaimed_cnames": [],
"live": {
"mismatches": 0,
"checked_at": "2026-01-15T11:53:00.000000Z",
"mismatch_since": null
}
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get a zone’s advisor findings
Findings about the zone’s stored records, such as missing or weak email protection, with the snapshot those findings were judged from. Changes nothing.
/organizations/{organization}/dns-zones/{dnsZone}/advisordns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/advisor" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/advisor" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/advisor', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/advisor",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/advisor', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/advisor", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"snapshot": {
"spf": [
"v=spf1 include:_spf.northwind.com ~all"
],
"dmarc": [
"v=DMARC1; p=quarantine; rua=mailto:dmarc@northwind.example"
],
"dkim_selectors": [],
"mx": [
"mx.northwind.com"
],
"null_mx": false,
"mail_provider": null,
"mx_cname_targets": [],
"apex_a": [
"198.51.100.10"
],
"apex_aaaa": [],
"apex_cname": [],
"caa": [
"0 issue \"letsencrypt.org\""
],
"caa_required": {
"certificates": [
{
"id": 1,
"subject": "northwind.com",
"issuer": "Let's Encrypt (R3)",
"ids": [
"letsencrypt.org"
],
"wildcard": false,
"renews_itself": true,
"provider": false
},
{
"id": 13,
"subject": "cdn.northwind.com",
"issuer": "Let's Encrypt (R3)",
"ids": [
"letsencrypt.org"
],
"wildcard": false,
"renews_itself": true,
"provider": false
}
],
"expected": [],
"fronts": [
{
"key": "cloudflare",
"label": "Cloudflare",
"ids": [
"letsencrypt.org",
"pki.goog"
],
"publishes_own": true
}
]
},
"wildcard_a": [],
"wildcard_aaaa": [],
"cf_exposed_ips": [
"198.51.100.10"
],
"mta_sts": [],
"bimi": [],
"apex_ns": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
],
"ns_matches_provider": true,
"mail_subdomains_missing_spf": []
},
"findings": [
{
"id": "ns",
"status": "pass",
"variant": "pass",
"values": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
]
},
{
"id": "dnssec",
"status": "pass",
"variant": "pass"
}
]
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Preview CAA protection for a zone
Shows the CAA records that would be added, each of the zone’s certificates and whether it stays renewable, or why a change is refused. live=1 also checks each record against public DNS. Changes nothing.
/organizations/{organization}/dns-zones/{dnsZone}/caa-protectiondns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Query parameters
livebooleanoptionalAlso check each record to add against public DNS.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/caa-protection" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/caa-protection" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/caa-protection', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/caa-protection",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/caa-protection', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/caa-protection", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"zone_id": 1,
"zone": "northwind.com",
"route": "here",
"where": {
"label": "Cloudflare",
"url": null
},
"records": [
{
"key": "kept:7",
"action": "none",
"type": "CAA",
"name": "@",
"value": "0 issue \"letsencrypt.org\"",
"current": null,
"record_id": null,
"reason": "caa_kept",
"params": []
},
{
"key": "issue:globalsign.com",
"action": "create",
"type": "CAA",
"name": "@",
"value": "0 issue \"globalsign.com\"",
"current": null,
"record_id": null,
"reason": "caa_cert",
"params": {
"subject": "shop.northwind.com"
}
}
],
"refusals": [],
"certificates": [
{
"id": 1,
"subject": "northwind.com",
"issuer": "Let's Encrypt (R3)",
"ca": "letsencrypt.org",
"wildcard": false,
"state": "stays_renewable"
},
{
"id": 13,
"subject": "cdn.northwind.com",
"issuer": "Let's Encrypt (R3)",
"ca": "letsencrypt.org",
"wildcard": false,
"state": "stays_renewable"
}
],
"fronts": [
{
"label": "Cloudflare",
"ids": [
"letsencrypt.org",
"pki.goog"
],
"publishes_own": true
}
]
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List a zone’s recorded changes
The zone’s record changes from its last 50 operations, newest first, grouped by the operation that made each batch, each with whether it can still be undone or restored to. Also returns a merged timeline and how many days of changes your plan keeps.
/organizations/{organization}/dns-zones/{dnsZone}/changesdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/changes" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/changes" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/changes', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/changes",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/changes', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/changes", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"batches": [],
"timeline": [
{
"kind": "audit",
"entry": {
"id": 21,
"action": "dns_zone.exported",
"meta": {
"zone": "northwind.com",
"format": "bind",
"records": 10
},
"user_id": 1,
"created_at": "2026-01-15T12:00:00.000000Z"
}
}
],
"tracked_since": null,
"writable": false,
"retention_days": 90,
"dynamic_ip_retention_days": 7
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List records in a zone
Every record in the zone, ordered by type and name, with the dynamic IP source that keeps it up to date, if any. Filter by record type with type.
/organizations/{organization}/dns-zones/{dnsZone}/dns-recordsdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
[
{
"id": 1,
"dns_zone_id": 1,
"type": "A",
"name": "@",
"value": "198.51.100.10",
"ttl": 300,
"priority": null,
"provider_record_id": "rec-200-0",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": true,
"live_values": null,
"live_checked_at": null,
"value_is_placeholder": false,
"live_mismatch": null,
"dynamic_ip_target": null
},
{
"id": 88,
"dns_zone_id": 1,
"type": "A",
"name": "archive",
"value": "198.51.100.11",
"ttl": 300,
"priority": null,
"provider_record_id": "demo-certificate-archive",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": false,
"live_values": [
"198.51.100.11"
],
"live_checked_at": "2026-01-15T11:53:00.000000Z",
"value_is_placeholder": false,
"live_mismatch": false,
"dynamic_ip_target": null
}
]{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Create a record
Creates a record in the zone and pushes it to the DNS provider; dry_run=1 returns the change without making it. Refused with 422 when the zone is read-only or an A/AAAA value is an address your fleet released, and with 409 when an apex CAA record would block a certificate’s renewal, unless confirm_caa_impact is sent.
/organizations/{organization}/dns-zones/{dnsZone}/dns-recordsdns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Query parameters
dry_runbooleanoptionalAnswer with the change this would make, and make none.
Bodyapplication/json
typestringrequiredOne of: A, AAAA, CNAME, MX, TXT, NS, SOA, SRV, CAA, PTR, ALIAS, CERT, DNSKEY, DS, HTTPS, LOC, …
namestring, nullableoptionalMust not be greater than 255 characters.
valuestringrequiredMust not be greater than 65535 characters.
ttlintegeroptionalMust be at least 60. Must not be greater than 86400.
priorityinteger, nullableoptionalMust be at least 0. Must not be greater than 65535.
proxiedbooleanoptionalcommentstring, nullableoptionalMust not be greater than 500 characters.
confirm_caa_impactbooleanoptionalSave an apex CAA record even though it blocks the CA a certificate renews from — send after a 409 caa_blocks_renewal.
Request
curl -X POST "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"type":"A","name":"api","value":"192.0.2.20","ttl":3600}'$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
$body = @'
{
"type": "A",
"name": "api",
"value": "192.0.2.20",
"ttl": 3600
}
'@
Invoke-RestMethod -Method Post -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records" -Headers $headers -ContentType "application/json" -Body $body<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
'json' => [
'type' => 'A',
'name' => 'api',
'value' => '192.0.2.20',
'ttl' => 3600,
],
]);
echo $response->getBody();import os
import requests
response = requests.post(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
json={
"type": "A",
"name": "api",
"value": "192.0.2.20",
"ttl": 3600,
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"type": "A",
"name": "api",
"value": "192.0.2.20",
"ttl": 3600
}),
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
req, err := http.NewRequest("POST", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records", strings.NewReader(`{"type":"A","name":"api","value":"192.0.2.20","ttl":3600}`))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"id": 91,
"dns_zone_id": 14,
"type": "A",
"name": "api",
"value": "192.0.2.20",
"ttl": 3600,
"priority": null,
"provider_record_id": null,
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": false,
"live_values": null,
"live_checked_at": null,
"value_is_placeholder": false,
"live_mismatch": null
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "idempotency_in_progress",
"message": "A request with this Idempotency-Key is still being handled.",
"hint": "Wait a few seconds, then send the same request with the same key again to get its result.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#idempotency_in_progress",
"reference": "k3j9x0b2qa"
}{
"code": "validation_failed",
"message": "The type field is required.",
"hint": "Fix the fields listed in \"errors\" and send the request again.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#validation_failed",
"errors": {
"type": [
"The type field is required."
]
},
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
409 idempotency_in_progress
422 validation_failed idempotency_key_reused
429 rate_limited
Get a record
One record in the zone. Answers 404 when the record belongs to another zone.
/organizations/{organization}/dns-zones/{dnsZone}/dns-records/{record}dns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
recordintegerrequiredThe record's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"id": 1,
"dns_zone_id": 1,
"type": "A",
"name": "@",
"value": "198.51.100.10",
"ttl": 300,
"priority": null,
"provider_record_id": "rec-200-0",
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": true,
"live_values": null,
"live_checked_at": null,
"value_is_placeholder": false,
"live_mismatch": null
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Update a record
Changes the record and pushes the change to the DNS provider; dry_run=1 returns the change without making it. Refused with 422 when the zone is read-only or a new A/AAAA value is an address your fleet released, and with 409 when an apex CAA change would block a certificate’s renewal, unless confirm_caa_impact is sent.
/organizations/{organization}/dns-zones/{dnsZone}/dns-records/{record}dns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
recordintegerrequiredThe record's ID.
Query parameters
dry_runbooleanoptionalAnswer with the change this would make, and make none.
Bodyapplication/json
typestringoptionalOne of: A, AAAA, CNAME, MX, TXT, NS, SOA, SRV, CAA, PTR, ALIAS, CERT, DNSKEY, DS, HTTPS, LOC, …
namestring, nullableoptionalMust not be greater than 255 characters.
valuestringoptionalMust not be greater than 65535 characters.
ttlintegeroptionalMust be at least 60. Must not be greater than 86400.
priorityinteger, nullableoptionalMust be at least 0. Must not be greater than 65535.
proxiedbooleanoptionalcommentstring, nullableoptionalMust not be greater than 500 characters.
confirm_caa_impactbooleanoptionalSave an apex CAA change even though it blocks the CA a certificate renews from — send after a 409 caa_blocks_renewal.
Request
curl -X PUT "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"type":"A","name":"www","value":"192.0.2.11","ttl":3600}'$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
$body = @'
{
"type": "A",
"name": "www",
"value": "192.0.2.11",
"ttl": 3600
}
'@
Invoke-RestMethod -Method Put -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1" -Headers $headers -ContentType "application/json" -Body $body<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('PUT', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
'json' => [
'type' => 'A',
'name' => 'www',
'value' => '192.0.2.11',
'ttl' => 3600,
],
]);
echo $response->getBody();import os
import requests
response = requests.put(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
json={
"type": "A",
"name": "www",
"value": "192.0.2.11",
"ttl": 3600,
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"type": "A",
"name": "www",
"value": "192.0.2.11",
"ttl": 3600
}),
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
req, err := http.NewRequest("PUT", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1", strings.NewReader(`{"type":"A","name":"www","value":"192.0.2.11","ttl":3600}`))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"id": 89,
"dns_zone_id": 14,
"type": "A",
"name": "www",
"value": "192.0.2.11",
"ttl": 3600,
"priority": null,
"provider_record_id": null,
"deleted_at": null,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"comment": null,
"proxied": false,
"live_values": null,
"live_checked_at": null,
"value_is_placeholder": false,
"live_mismatch": null
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "validation_failed",
"message": "The type field is required.",
"hint": "Fix the fields listed in \"errors\" and send the request again.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#validation_failed",
"errors": {
"type": [
"The type field is required."
]
},
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
422 validation_failed
429 rate_limited
Delete a record
Deletes the record at the DNS provider first, then in InfraNest; if the provider refuses, the record is kept and 502 is returned. Supports dry_run=1, and confirm_caa_impact=1 after a 409 for an apex CAA record whose removal would block a certificate’s renewal.
/organizations/{organization}/dns-zones/{dnsZone}/dns-records/{record}dns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
recordintegerrequiredThe record's ID.
Query parameters
confirm_caa_impactbooleanoptionalDelete an apex CAA record even though that blocks the CA a certificate renews from — send after a 409 caa_blocks_renewal.
dry_runbooleanoptionalAnswer with the change this would make, and make none.
Request
curl -X DELETE "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Delete -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('DELETE', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.delete(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.status_code)const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("DELETE", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
No content.{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
429 rate_limited
Set a record’s label
Sets or clears a note on what the record is for (label, up to 120 characters; empty or null clears it). Kept in InfraNest only and never written to the DNS provider; a labelled record is never proposed as a leftover.
/organizations/{organization}/dns-zones/{dnsZone}/dns-records/{record}/labeldns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
recordintegerrequiredThe record's ID.
Bodyapplication/json
labelstring, nullableoptionalWhat the record is for, up to 120 characters. Empty or null clears it.
Request
curl -X PUT "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/label" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"label":"Main website"}'$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
$body = @'
{
"label": "Main website"
}
'@
Invoke-RestMethod -Method Put -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/label" -Headers $headers -ContentType "application/json" -Body $body<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('PUT', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/label', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
'json' => [
'label' => 'Main website',
],
]);
echo $response->getBody();import os
import requests
response = requests.put(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/label",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
json={
"label": "Main website",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/label', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"label": "Main website"
}),
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
req, err := http.NewRequest("PUT", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/label", strings.NewReader(`{"label":"Main website"}`))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"label": "Main website",
"points_at": {
"kind": "custom",
"label": "Main website",
"url": null,
"flagged": false,
"since": null,
"provider": null,
"note": null,
"ours": false
}
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "validation_failed",
"message": "The label field is required.",
"hint": "Fix the fields listed in \"errors\" and send the request again.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#validation_failed",
"errors": {
"label": [
"The label field is required."
]
},
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
422 validation_failed
429 rate_limited
Check a record’s propagation
Asks public DNS resolvers for the record and reports, per resolver, whether its stored value is live yet; tier=fast uses only the quicker resolvers. With landing=1 it instead reports whether the record’s latest change (or the one in change_id) has taken effect, and landing is null when nothing changed recently.
/organizations/{organization}/dns-zones/{dnsZone}/dns-records/{record}/propagationdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
recordintegerrequiredThe record's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/propagation" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/propagation" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/propagation', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/propagation",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/propagation', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/dns-records/1/propagation", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
No success response is recorded for this endpoint yet.
Get a zone’s DNSSEC status
Reads the zone’s DNSSEC state from its DNS provider, including the DS details to add at your registrar when signing is waiting on that step. status is null when the provider cannot sign the zone or its state could not be read.
/organizations/{organization}/dns-zones/{dnsZone}/dnssecdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/dnssec" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/dnssec" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/dnssec', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/dnssec",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/dnssec', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/dnssec", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"status": null
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Preview email protection for a zone
Shows each email record (such as SPF and DMARC) as it is and as it would be, or why a change is refused. mode is required: protect for a domain that receives mail, no_mail for one that sends none; live=1 also checks against public DNS. Changes nothing.
/organizations/{organization}/dns-zones/{dnsZone}/email-protectiondns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Query parameters
modestringrequiredprotect (a domain that receives mail) or no_mail.
providerstringoptionalA mail provider key, when MX names nobody with an SPF include.
livebooleanoptionalAlso check each record against public DNS, and look for the DKIM key there.
dkim_targetstringoptionalMicrosoft 365: the selector1 CNAME target as Defender shows it.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/email-protection?mode=protect" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/email-protection?mode=protect" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/email-protection?mode=protect', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/email-protection?mode=protect",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/email-protection?mode=protect', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/email-protection?mode=protect", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"zone_id": 1,
"zone": "northwind.com",
"mode": "no_mail",
"route": "here",
"where": {
"label": "Cloudflare",
"url": null
},
"provider": null,
"providers": [],
"records": [],
"refusals": [
{
"key": "mode",
"reason": "receives_mail",
"params": []
}
],
"lookups": null,
"dkim": null
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get a zone’s audit history
The 50 most recent audit-log entries for the zone and its records, deleted records included, newest first.
/organizations/{organization}/dns-zones/{dnsZone}/historydns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/history" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/history" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/history', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/history",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/history', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/history", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
[
{
"id": 21,
"action": "dns_zone.exported",
"meta": {
"zone": "northwind.com",
"format": "bind",
"records": 10
},
"user_id": 1,
"created_at": "2026-01-15T12:00:00.000000Z"
}
]{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List leftover records in a zone
Records the zone appears to have outgrown, such as old verification codes, addresses that stopped answering and duplicates, each with its reason and evidence. writable is false when InfraNest cannot change records in this zone.
/organizations/{organization}/dns-zones/{dnsZone}/leftoversdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/leftovers" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/leftovers" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/leftovers', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/leftovers",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/leftovers', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/leftovers", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"items": [],
"writable": false,
"unanswered_days": 90
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Compare stored records with live DNS
Records whose stored value differs from what public resolvers last answered, each with both values, the likely cause and whether taking the live value is offered. Also returns the zone’s delegation state; changes nothing.
/organizations/{organization}/dns-zones/{dnsZone}/live-mismatchdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/live-mismatch" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/live-mismatch" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/live-mismatch', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/live-mismatch",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/live-mismatch', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/live-mismatch", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"zone": "northwind.com",
"checked_at": "2026-01-15T11:53:00.000000Z",
"delegation": {
"state": "matches",
"reason": null,
"live": [
"nina.ns.cloudflare.com",
"rick.ns.cloudflare.com"
],
"dnssec": null,
"belongs_to": "cloudflare",
"checked_at": "2026-01-15T11:53:00.000000Z"
},
"can_push": false,
"records": []
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List outside changes to a zone
Records changed at the DNS provider outside InfraNest that nobody has decided on yet, newest first, each with what the record was and is now. writable says whether your earlier value can be put back for this zone.
/organizations/{organization}/dns-zones/{dnsZone}/outside-changesdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/outside-changes" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/outside-changes" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/outside-changes', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/outside-changes",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/outside-changes', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/outside-changes", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"changes": [],
"writable": false
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Preview what a record value would point at
Says what a record of the given type with the given value (both required) would point at, before you save it. points_at is null when nothing is recognised.
/organizations/{organization}/dns-zones/{dnsZone}/points-atdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Query parameters
typestringrequiredThe record type.
valuestringrequiredThe value being typed.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/points-at?type=A&value=203.0.113.7" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/points-at?type=A&value=203.0.113.7" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/points-at?type=A&value=203.0.113.7', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/points-at?type=A&value=203.0.113.7",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/points-at?type=A&value=203.0.113.7', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/points-at?type=A&value=203.0.113.7", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"points_at": {
"kind": "server",
"label": "web-01",
"url": "/servers/1",
"flagged": false,
"since": null,
"provider": "Hetzner",
"note": null,
"ours": false
}
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get what a zone’s records point at
For each record in the zone, keyed by record id: what it points at (one of your servers, a recognised service, a connection or a released address) and what uses it, such as monitors, certificates and status pages. Records with neither are left out; long_ttl is the TTL above which InfraNest calls a TTL long.
/organizations/{organization}/dns-zones/{dnsZone}/record-contextdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/record-context" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/record-context" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/record-context', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/record-context",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/record-context', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/record-context", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"records": {
"1": {
"points_at": {
"kind": "server",
"label": "web-01",
"url": "/servers/1",
"flagged": false,
"since": null,
"provider": "Hetzner",
"note": {
"key": "proxied",
"params": {
"provider": "Cloudflare"
}
},
"ours": false
},
"used_by": [
{
"type": "monitor",
"id": 1,
"label": "Marketing site",
"sublabel": "https://northwind.com",
"url": "/monitoring/1",
"meta": {
"type": "http",
"last_status": "up",
"last_checked_at": "2026-01-15T11:58:00.000000Z"
}
},
{
"type": "monitor",
"id": 4,
"label": "Checkout flow",
"sublabel": "https://northwind.com/checkout",
"url": "/monitoring/4",
"meta": {
"type": "http",
"last_status": "down",
"last_checked_at": "2026-01-15T11:58:00.000000Z"
}
}
]
},
"2": {
"points_at": {
"kind": "server",
"label": "web-01",
"url": "/servers/1",
"flagged": false,
"since": null,
"provider": "Hetzner",
"note": {
"key": "proxied",
"params": {
"provider": "Cloudflare"
}
},
"ours": false
},
"used_by": [
{
"type": "monitor",
"id": 8,
"label": "www → apex redirect",
"sublabel": "https://www.northwind.com",
"url": "/monitoring/8",
"meta": {
"type": "redirect",
"last_status": "up",
"last_checked_at": "2026-01-15T11:58:00.000000Z"
}
},
{
"type": "certificate",
"id": 1,
"label": "northwind.com",
"sublabel": "2026-03-30",
"url": "/certificates/1",
"meta": []
}
]
}
},
"long_ttl": 3600
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Check whether an address left your fleet
Says whether the address query parameter (required) belongs to one of your servers, or is an address one of your servers held and released. A record cannot be pointed at a released address.
/organizations/{organization}/dns-zones/{dnsZone}/released-addressdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Query parameters
addressstringrequiredThe A/AAAA value being typed.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/released-address?address=203.0.113.7" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/released-address?address=203.0.113.7" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/released-address?address=203.0.113.7', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/released-address?address=203.0.113.7",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/released-address?address=203.0.113.7', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/released-address?address=203.0.113.7", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"server": {
"id": 1,
"name": "web-01"
},
"released": null
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Preview restoring a zone to an earlier point
Shows what putting every record back the way it was just after the batch batch_id (required) would change. Changes nothing; answers 404 when that batch is not in the zone’s history.
/organizations/{organization}/dns-zones/{dnsZone}/restore-previewdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Query parameters
batch_idstringrequiredThe batch to restore to.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/restore-preview?batch_id=9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/restore-preview?batch_id=9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/restore-preview?batch_id=9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/restore-preview?batch_id=9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/restore-preview?batch_id=9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/restore-preview?batch_id=9b2c6a1e-3f7d-4a8e-9c1b-2d3e4f5a6b7c", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
No success response is recorded for this endpoint yet.
Check a zone’s live DNS security
Resolves the zone’s SPF, DMARC, DKIM, MX, CAA, MTA-STS, BIMI, NS, address and DNSSEC answers from public DNS right now, with findings judged by the same rules as the stored-record advisor. Reflects what the internet answers, not what InfraNest stores.
/organizations/{organization}/dns-zones/{dnsZone}/security-checkdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/security-check" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/security-check" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/security-check', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/security-check",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/security-check', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/security-check", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"zone": "northwind.com",
"checked_at": "2026-01-15T12:00:00.000000Z",
"dnssec": {
"state": "unknown",
"ds": [],
"validates": null
},
"spf": [],
"dmarc": [],
"dkim": [],
"mx": [],
"a": [],
"aaaa": [],
"caa": [],
"wildcard_a": [],
"wildcard_aaaa": [],
"mta_sts": [],
"bimi": [],
"ns": [],
"ns_matches_provider": null,
"snapshot": {
"spf": [],
"dmarc": [],
"dkim_selectors": [],
"mx": [],
"null_mx": false,
"mail_provider": null,
"mx_cname_targets": [],
"apex_a": [],
"apex_aaaa": [],
"apex_cname": [],
"caa": [],
"wildcard_a": [],
"wildcard_aaaa": [],
"cf_exposed_ips": [],
"mta_sts": [],
"bimi": [],
"apex_ns": [],
"ns_matches_provider": null,
"mail_subdomains_missing_spf": []
},
"findings": [
{
"id": "dnssec",
"status": "pass",
"variant": "pass"
},
{
"id": "no_mail",
"status": "info",
"variant": "info",
"fix": {
"type": "no_mail"
}
}
]
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List server addresses for records
Your servers that have an address, each with the IPv4 and IPv6 address an A or AAAA record should hold to reach it. Where a server holds a whole IPv6 block, basis says how the address was chosen.
/organizations/{organization}/dns-zones/{dnsZone}/server-addressesdns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/server-addresses" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/server-addresses" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/server-addresses', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/server-addresses",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/server-addresses', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/server-addresses", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"data": [
{
"id": 13,
"name": "analytics-01",
"status": "stopped",
"ipv4": {
"address": "192.0.2.80",
"basis": "exact"
},
"ipv6": null
},
{
"id": 3,
"name": "api-01",
"status": "running",
"ipv4": {
"address": "198.51.100.20",
"basis": "exact"
},
"ipv6": {
"address": "2001:db8:1c1e:5a20::1",
"basis": "default",
"block": "2001:db8:1c1e:5a20::/64"
}
}
]
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Sync a zone from its provider
Queues a fresh import of the zone’s records from its DNS provider and answers with queued: true. Refused with 422 for a zone with no linked provider connection; manual syncs are limited per hour by your plan.
/organizations/{organization}/dns-zones/{dnsZone}/syncdns.writeChanges dataAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl -X POST "https://api.infranest.io/api/organizations/42/dns-zones/1/sync" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Post -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/sync" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.infranest.io/api/organizations/42/dns-zones/1/sync', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.post(
"https://api.infranest.io/api/organizations/42/dns-zones/1/sync",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/sync', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("POST", "https://api.infranest.io/api/organizations/42/dns-zones/1/sync", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.write scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.write",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "idempotency_in_progress",
"message": "A request with this Idempotency-Key is still being handled.",
"hint": "Wait a few seconds, then send the same request with the same key again to get its result.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#idempotency_in_progress",
"reference": "k3j9x0b2qa"
}{
"code": "validation_failed",
"message": "The name field is required.",
"hint": "Fix the fields listed in \"errors\" and send the request again.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#validation_failed",
"errors": {
"name": [
"The name field is required."
]
},
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
409 idempotency_in_progress
422 validation_failed idempotency_key_reused
429 rate_limited
No success response is recorded for this endpoint yet.
Download a zone as a BIND file
Returns the zone’s records as a BIND master file, in plain text, as an attachment named after the zone. The export is recorded in your audit log.
/organizations/{organization}/dns-zones/{dnsZone}/zone-filedns.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dnsZoneintegerrequiredThe DNS zone's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dns-zones/1/zone-file" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dns-zones/1/zone-file" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dns-zones/1/zone-file', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dns-zones/1/zone-file",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dns-zones/1/zone-file', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dns-zones/1/zone-file", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
The zone in BIND format.
Content-Type: text/plain{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the dns.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "dns.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Dynamic IPs
List dynamic IP sources
Your dynamic IP sources (connections whose address changes, such as a home or office line), ordered by label, each with its tags and the DNS records and firewalls it keeps up to date.
/organizations/{organization}/dynamic-ipsmonitoring.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dynamic-ips" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dynamic-ips" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dynamic-ips', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dynamic-ips",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dynamic-ips', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dynamic-ips", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
[
{
"id": 1,
"organization_id": 1,
"label": "Office — Amsterdam",
"current_ipv4": null,
"current_ipv6": null,
"ipv4_updated_at": null,
"ipv6_updated_at": null,
"last_used_at": null,
"created_by": 1,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"observed_ipv4": null,
"observed_ipv6": null,
"alert_on_change": true,
"firewall_strict": false,
"firewall_fail_closed": false,
"country_code": null,
"city": null,
"asn": null,
"asn_org": null,
"is_hosting": false,
"silent_notified_at": null,
"public_id": null,
"last_attempt_at": null,
"last_attempt_result": null,
"last_attempt_ip": null,
"last_attempt_agent": null,
"agent_server_id": null,
"targets_count": 0,
"tags": [],
"targets": []
}
]{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the monitoring.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "monitoring.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get a dynamic IP source
One source with its targets, its 20 most recent address events and the server agent it follows, if any. The update token is never included; can_reveal says whether it can be shown again.
/organizations/{organization}/dynamic-ips/{dynamicIp}monitoring.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dynamicIpintegerrequiredThe dynamic IP's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dynamic-ips/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dynamic-ips/1" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dynamic-ips/1', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dynamic-ips/1",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dynamic-ips/1', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dynamic-ips/1", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"id": 1,
"organization_id": 1,
"label": "Office — Amsterdam",
"current_ipv4": null,
"current_ipv6": null,
"ipv4_updated_at": null,
"ipv6_updated_at": null,
"last_used_at": null,
"created_by": 1,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"observed_ipv4": null,
"observed_ipv6": null,
"alert_on_change": true,
"firewall_strict": false,
"firewall_fail_closed": false,
"country_code": null,
"city": null,
"asn": null,
"asn_org": null,
"is_hosting": false,
"silent_notified_at": null,
"public_id": null,
"last_attempt_at": null,
"last_attempt_result": null,
"last_attempt_ip": null,
"last_attempt_agent": null,
"agent_server_id": null,
"targets_count": 0,
"targets": [],
"events": [],
"agent_server": null,
"can_reveal": false
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the monitoring.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "monitoring.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
Get a dynamic IP source’s history
Address changes, what each one updated or failed to update, and changes to the source’s settings, newest first. Drawn from your audit log, so it ages out with your plan’s audit retention.
/organizations/{organization}/dynamic-ips/{dynamicIp}/historymonitoring.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
dynamicIpintegerrequiredThe dynamic IP's ID.
Request
curl "https://api.infranest.io/api/organizations/42/dynamic-ips/1/history" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dynamic-ips/1/history" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dynamic-ips/1/history', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dynamic-ips/1/history",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dynamic-ips/1/history', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dynamic-ips/1/history", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
{
"entries": [],
"truncated": false
}{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the monitoring.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "monitoring.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited
List servers a dynamic IP can follow
Your servers with an InfraNest agent, which a dynamic IP source can take its address from, most recent agent check-in first.
/organizations/{organization}/dynamic-ips/agent-serversmonitoring.readRead onlyAuthorizationheader
AuthorizationstringrequiredBearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.
Path parameters
organizationintegerrequiredYour organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.
Request
curl "https://api.infranest.io/api/organizations/42/dynamic-ips/agent-servers" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"$headers = @{
Authorization = "Bearer $env:INFRANEST_TOKEN"
Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/dynamic-ips/agent-servers" -Headers $headers<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/dynamic-ips/agent-servers', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
'Accept' => 'application/json',
],
]);
echo $response->getBody();import os
import requests
response = requests.get(
"https://api.infranest.io/api/organizations/42/dynamic-ips/agent-servers",
headers={
"Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
"Accept": "application/json",
},
)
response.raise_for_status()
print(response.json())const response = await fetch('https://api.infranest.io/api/organizations/42/dynamic-ips/agent-servers', {
headers: {
Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
Accept: 'application/json',
},
})
console.log(response.status, await response.text())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/dynamic-ips/agent-servers", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
req.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}Response
[
{
"id": 3,
"name": "api-01",
"agent_last_seen_at": "2026-01-15T11:57:00+00:00"
}
]{
"code": "unauthenticated",
"message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
"hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#unauthenticated",
"reference": "k3j9x0b2qa"
}{
"code": "scope_missing",
"message": "This API token does not have the monitoring.read scope.",
"hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#scope_missing",
"required": "monitoring.read",
"reference": "k3j9x0b2qa"
}{
"code": "not_found",
"message": "Nothing exists at this address, or you cannot see it.",
"hint": "Check the IDs in the address. A resource in another organization answers as not found.",
"retryable": false,
"docs_url": "https://developers.infranest.io/errors#not_found",
"reference": "k3j9x0b2qa"
}{
"code": "rate_limited",
"message": "This token made more than 300 requests in a minute.",
"hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
"retryable": true,
"docs_url": "https://developers.infranest.io/errors#rate_limited",
"limit_key": "api_requests_per_minute",
"limit": 300,
"retry_after": 12,
"reference": "k3j9x0b2qa"
}401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited