DNSCove

DNSCove control-plane API (1.0)

Download OpenAPI specification:

JSON API for the DNSCove control-plane: magic-link auth, zones and records, delegation/parity checks, snapshot publish, Route53 import, and scoped machine tokens for automation (ACME DNS-01, per-zone record management, and tenant-wide provisioning).

Two credential types:

  • Session — established by POST /api/auth/exchange (magic-link) or POST /api/webauthn/login/finish (passkey) and ended by POST /api/auth/logout. Delivered as an HttpOnly Set-Cookie, never in a response body or an Authorization header (see the sessionCookie security scheme below). Full access to the tenant's zones for as long as the cookie is valid and not revoked — see SECURITY.md for the revocation model. Because a browser attaches this cookie to requests automatically, every mutating request that carries it must also pass an Origin/Sec-Fetch-Site check and echo a matching X-CSRF-Token header; see API.md for the cookie attributes and the CSRF rule. Used by the console.

  • Scoped machine token (dnsc_<id>_<secret>) — sent as Authorization: Bearer <token>. Never a browser credential, so it is exempt from the CSRF/Origin checks above (see API.md). Carries one scope:

    • acme-dns01 — bound to one zone; may call only POST /api/zones/{id}/acme-challenge on that zone.
    • zone-admin — bound to one zone; full record CRUD + publish + delegation/parity + ACME on that zone. No zone lifecycle or token minting.
    • tenant-admin — tenant-wide (no zone binding): create/list/delete zones, act on any zone, publish, and mint/revoke tokens. Root-equivalent — prefer a narrower scope when one zone is enough.

    An endpoint that doesn't accept a token's scope answers 403.

Guides: the Quickstart walks a zone from create to live over the API; Migrating from Route 53 imports a hosted zone in one call; cert-manager issues Let's Encrypt certificates on Kubernetes; and Terraform / OpenTofu and CloudFormation manage DNS as code.

auth

Request a magic-link sign-in token

Request Body schema: application/json
required
email
required
string <email>

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com"
}

Response samples

Content type
application/json
{
  • "sent": true,
  • "dev_token": "string",
  • "dev_link": "string"
}

Exchange a magic-link token for a session

Consumes a single-use magic-link token and starts a console session. The session itself is never returned in the body — it is delivered as an HttpOnly Set-Cookie (see the Set-Cookie response header below and sessionCookie in securitySchemes), alongside a second, non-HttpOnly dnscove.csrf cookie the console echoes back as X-CSRF-Token on subsequent mutating requests. See API.md for the cookie attributes and the CSRF rule.

Request Body schema: application/json
required
token
required
string

Responses

Request samples

Content type
application/json
{
  • "token": "string"
}

Response samples

Content type
application/json
{
  • "email": "string",
  • "tenant": "string"
}

End the console session

Revokes the session's jti (see SECURITY.md) and clears both the session and dnscove.csrf cookies. Deliberately NOT gated behind an active session: a missing, malformed, expired, or already-revoked cookie all take the same path to the same 204 as a live session being logged out, so the endpoint can never be used to probe whether a session exists. When the request DOES carry a live session cookie it must still satisfy the same Origin/Sec-Fetch-Site and X-CSRF-Token double-submit checks as any other mutating route (see API.md) — those checks run ahead of this handler, not inside it.

header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

passkeys

Begin passkey registration (returns PublicKeyCredentialCreationOptions)

Requires a live console session (cookie) — never accepts a scoped machine token, so passkeys always bind to a signed-in human.

Authorizations:
sessionCookie
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{ }

Finish passkey registration

Authorizations:
sessionCookie
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
required
object

the browser's attestation PublicKeyCredential

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "error": {
    }
}

List the caller's registered passkeys

Authorizations:
sessionCookie

Responses

Response samples

Content type
application/json
{
  • "credentials": [
    ]
}

Remove a passkey

Authorizations:
sessionCookie
path Parameters
id
required
string
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Begin passwordless passkey sign-in (returns assertion options)

Responses

Response samples

Content type
application/json
{ }

Finish passkey sign-in — starts a console session

Same cookie-not-body session delivery as POST /api/auth/exchange (see its description) — the console has one session model, not two.

Request Body schema: application/json
required
object

the browser's assertion PublicKeyCredential

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "email": "string",
  • "tenant": "string"
}

zones

List zones

Authorizations:
sessionCookiescopedToken

Responses

Response samples

Content type
application/json
{
  • "zones": [
    ]
}

Create a zone

Authorizations:
sessionCookiescopedToken
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
required
origin
required
string

Responses

Request samples

Content type
application/json
{
  • "origin": "example.com"
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "tenant_id": "string",
  • "origin": "string",
  • "status": "PENDING_VERIFICATION",
  • "ns": [
    ],
  • "serial": 0,
  • "published_serial": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Get a zone

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70

Responses

Response samples

Content type
application/json
{
  • "id": "string",
  • "tenant_id": "string",
  • "origin": "string",
  • "status": "PENDING_VERIFICATION",
  • "ns": [
    ],
  • "serial": 0,
  • "published_serial": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Delete a zone

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Check parent NS delegation (activates the zone when delegated)

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70

Responses

Response samples

Content type
application/json
{
  • "delegation": {
    },
  • "status": "PENDING_VERIFICATION"
}

Prove domain ownership via the `_dnscove-challenge` TXT and activate the zone

Checks for the _dnscove-challenge.<origin> TXT record whose value is dnscove-site-verification=<zone id> on the domain's current public DNS. On success the zone advances PENDING_VERIFICATION → ACTIVE and is published immediately — so the zone starts serving on ns1/ns2 before you move nameservers (a no-downtime cutover). The exact record to publish is returned by zone create, Route 53 import, and any unverified response (verification). Session, zone-admin (this zone), or tenant-admin.

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "status": "PENDING_VERIFICATION",
  • "verified": true,
  • "served": true,
  • "publish_error": "string",
  • "verification": {
    },
  • "challenge": { }
}

Compare served records against public DNS ("safe to delete the old zone")

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70

Responses

Response samples

Content type
application/json
{
  • "parity": {
    },
  • "safe_to_delete": true,
  • "status": "PENDING_VERIFICATION"
}

records

List record sets

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70

Responses

Response samples

Content type
application/json
{
  • "rrsets": [
    ]
}

Create, upsert, or delete a record set

Session, zone-admin (this zone), or tenant-admin. A record set is owned by (name, type): CREATE claims that pair and fails with 409 RRSetAlreadyExists if it is taken, while UPSERT replaces whatever is there — including every value it currently answers. Use CREATE when you mean "add a record" so a collision is reported instead of overwritten.

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
required
action
string
Default: "UPSERT"
Enum: "CREATE" "UPSERT" "DELETE"
name
required
string
type
required
string
Enum: "A" "AAAA" "CNAME" "TXT" "MX" "SRV" "CAA" "NS" "PTR" "ALIAS"

Standard RR types plus DNSCove's apex-safe ALIAS — the record other providers call ANAME and Route 53 calls an alias record. There is no separate ANAME type: ALIAS is the apex-flattening record. It takes exactly one FQDN target (a CDN, load balancer, or any hostname), flattened to A/AAAA at publish time and kept fresh. ALIAS is valid at the apex and can't coexist with A/AAAA/CNAME at the same name. The apex SOA and apex NS are managed and can't be edited.

ttl
integer
Default: 300
records
Array of strings

Responses

Request samples

Content type
application/json
{
  • "action": "CREATE",
  • "name": "www.example.com.",
  • "type": "A",
  • "ttl": 300,
  • "records": [
    ]
}

Response samples

Content type
application/json
{
  • "status": "PENDING",
  • "serial": 0
}

tokens

List a zone's tokens (session or tenant-admin; never returns secrets)

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70

Responses

Response samples

Content type
application/json
{
  • "tokens": [
    ]
}

Mint a per-zone token (session or tenant-admin)

Mints an acme-dns01 or zone-admin token bound to this zone. Returns the plaintext token exactly once — store it securely; it is unrecoverable afterward. tenant-admin is not a per-zone scope; mint it at POST /api/tokens.

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
name
string
scope
string
Default: "acme-dns01"
Enum: "acme-dns01" "zone-admin"
expires_in_days
integer

0 = no expiry

Responses

Request samples

Content type
application/json
{
  • "name": "cert-manager webhook",
  • "scope": "acme-dns01",
  • "expires_in_days": 0
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "zone_id": "string",
  • "scope": "acme-dns01",
  • "name": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "revoked": true,
  • "token": "dnsc_1a2b3c4d5e6f7a8b_…"
}

Revoke a per-zone token (session or tenant-admin)

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70
tid
required
string
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

List every token in the tenant (session or tenant-admin; no secrets)

Authorizations:
sessionCookiescopedToken

Responses

Response samples

Content type
application/json
{
  • "tokens": [
    ]
}

Mint a token of any scope (session or tenant-admin)

Mints tenant-admin (default, tenant-wide) or a zone-bound scope when a zone_id in the caller's tenant is supplied. Returns the plaintext token exactly once.

Authorizations:
sessionCookiescopedToken
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
name
string
scope
string
Default: "tenant-admin"
Enum: "tenant-admin" "zone-admin" "acme-dns01"
zone_id
string

required for zone-bound scopes; ignored for tenant-admin

expires_in_days
integer

0 = no expiry

Responses

Request samples

Content type
application/json
{
  • "name": "terraform",
  • "scope": "tenant-admin",
  • "zone_id": "string",
  • "expires_in_days": 0
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "zone_id": "string",
  • "scope": "acme-dns01",
  • "name": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "revoked": true,
  • "token": "dnsc_1a2b3c4d5e6f7a8b_…"
}

Revoke any token in the tenant by id (session or tenant-admin)

Authorizations:
sessionCookiescopedToken
path Parameters
tid
required
string
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

acme

Present or clean up an ACME DNS-01 challenge TXT

Adds (PRESENT) or removes (CLEANUP) one value on the _acme-challenge TXT record and republishes to the edge. Accepts a session or an acme-dns01 / zone-admin token bound to this zone, or a tenant-admin token. Values merge, so multiple challenges at the same name (SAN/wildcard) coexist.

Authorizations:
sessionCookiescopedToken
path Parameters
id
required
string
Example: Z1a2b3c4d5e6f70
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
required
action
required
string
Enum: "PRESENT" "CLEANUP"
name
required
string
value
required
string

base64url key authorization

Responses

Request samples

Content type
application/json
{
  • "action": "PRESENT",
  • "name": "_acme-challenge.canary.lab.dnscove.com.",
  • "value": "string"
}

Response samples

Content type
application/json
{
  • "status": "ok",
  • "action": "string",
  • "changed": true,
  • "published": true,
  • "serial": 0
}

ops

Liveness probe

Responses

Response samples

Content type
application/json
{
  • "status": "ok"
}

Compile and ship a full snapshot to every edge node

Session, zone-admin, or tenant-admin. A zone-admin uses this to make its bound-zone record changes live; the publish operation compiles and ships the full snapshot. Responses to zone-admin tokens report only the bound zone's confirmation state and do not expose other zone origins. If another active zone is already unpublished when the request starts, it returns 409 PublishBlocked.

Authorizations:
sessionCookiescopedToken
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Responses

Response samples

Content type
application/json
{
  • "published": true,
  • "zones": 0,
  • "nodes": [
    ]
}

Import a zone from `aws route53 list-resource-record-sets` JSON

Creates a zone from a pasted Route 53 export and imports every convertible record set (apex SOA/NS are dropped and managed by DNSCove; ALIAS records convert to DNSCove ALIAS; routing-policy record sets are skipped). AWS credentials are never sent — you run the CLI locally and post the JSON. The new zone is PENDING_VERIFICATION and not served yet; the response returns the _dnscove-challenge TXT to publish (verification), which you prove via POST /api/zones/{id}/verify to go live. Session or tenant-admin.

Authorizations:
sessionCookiescopedToken
header Parameters
X-CSRF-Token
string

Required on this mutating request only when it is authenticated by the sessionCookie cookie — echo the value of the non-HttpOnly dnscove.csrf cookie set alongside the session (double-submit; see API.md#authentication). Not marked required: true here because a scopedToken (machine bearer) caller never sends this header and is exempt from the check entirely — see the scopedToken security scheme description. A session-cookie request missing or mismatching this header gets 401; see the Unauthorized response and POST /api/auth/logout's 401 for the concrete shape. POST /api/stripe/webhook and /blog/* are exempt from this requirement even though they are mutating, because neither is ever a browser request carrying our cookies (HMAC signature and static API key respectively — see API.md#authentication).

Request Body schema: application/json
required
json
required
string

raw Route53 JSON

Responses

Request samples

Content type
application/json
{
  • "json": "string"
}

Response samples

Content type
application/json
{
  • "zone": {
    },
  • "imported": 0,
  • "skipped": [
    ],
  • "ns": [
    ],
  • "verification": {
    }
}