CNF API · v1

A predictable REST API for managing DNS zones and records on c.nf. Every operation is restricted to the user who owns the calling API key.

Overview

Version 1 exposes DNS operations for zones you have claimed through DNS Management. Create and revoke tokens under Account → API keys.

The same trust boundary as the console. The API uses the console’s verified-zone ownership check. A zone available to your console account is available to your API key; every other zone returns 403 forbidden_zone.
For a machine-readable endpoint catalogue, request GET /v1?format=json or send Accept: application/json to /v1.

Base URL and versioning

https://api.c.nf/v1

All endpoints are below /v1. Breaking changes will use a new major-version path. Backwards-compatible fields and endpoints may be added to v1 and will be recorded in the changelog.

Authentication and scopes

Every request that reads or changes account data must include a Bearer token. Tokens begin with cnf_ followed by 32 hexadecimal characters.

curl https://api.c.nf/v1/dns/zones \
  -H "Authorization: Bearer cnf_a1b2c3d4e5f60718293a4b5c6d7e8f90"

Tokens are stored as one-way hashes and the complete value is shown only when it is created. If a token is lost, revoke it and create another.

Use the Authorization header only. Basic authentication and query-string tokens are not accepted. A token in a URL can leak through logs, referrer headers and browser history.

Each token carries scopes. A token with no configured scope receives dns:read by default. Write scope also permits reads.

ScopeGrants
dns:read List zones and records, and read individual records.
dns:write Read, create, update and delete DNS records and zone claims.
* All scopes. Use this only when it is genuinely required.

A mismatch returns 403 forbidden_scope and includes both required_scopes and token_scopes for diagnosis.

Per-user isolation

A Bearer token resolves to exactly one user_id. Every zone handler checks that user’s verified claim before any read or write. There is no administrator override, impersonation path or cross-user bypass.

Even a token with * can act only on zones verified by its own user. Attempting to use another account’s zone returns 403 forbidden_zone.

Response envelope

Successful response

{
  "success": true,
  "data": { "zones": [] },
  "request_id": "req_abc123def456abcd"
}

Error response

{
  "success": false,
  "error": "forbidden_zone",
  "message": "zone not verified for this account (or does not exist)",
  "request_id": "req_abc123def456abcd",
  "zone": "example.com"
}

request_id appears on every response and is safe to record with client-side logs.

Error codes

HTTPError codeMeaning
400 bad_request A required field is missing, the body is malformed or an input is invalid.
401 unauthorized The Bearer token is missing, invalid or revoked.
403 forbidden_scope The token does not have the required scope.
403 forbidden_zone The zone is not verified for this account, or it does not exist.
404 not_found The requested zone, record or user was not found.
405 method_not_allowed This endpoint does not support the HTTP method.
502 upstream_error The DNS provider returned an error.

Zone verification

When POST /v1/dns/zones claims a zone, the server chooses one of these paths:

  1. Instant verification (method: "auto") — Used when no other account has claimed the zone. The claim is immediately verified. Records become authoritative only after the owner points the registrar’s NS delegation to the returned nameservers.
  2. TXT verification — Used for a contested claim. Add the returned TXT value at _cnfdns-verify.<zone>, then call POST /v1/dns/zones/{zone}/verify.
  3. Legacy NS verification — Older pending claims may use verify_method: "ns". Verification compares the zone’s live NS answers with its expected nameserver set.
Why instant verification is safe. Records stored by c.nf are inert until the domain owner changes the authoritative NS delegation at the registrar. A first claim alone cannot make those records serve.
Reverse zones The same model applies to in-addr.arpa and ip6.arpa. Claim the zone, obtain expected_ns, and submit those delegation targets to the relevant registry.

Verification is idempotent. Repeating it for a verified claim returns verified: true without changing the claim.

Record types and limits

Supported record types

A, AAAA, CNAME, MX, TXT, NS, SRV, CAA, PTR, SPF.

priority is required for MX and SRV and ignored for other types. ttl must be between 60 and 2,592,000 seconds.

Endpoints

MethodPathScopePurpose
GET /v1 Public Return the version catalogue as JSON, or redirect browsers to this reference.
GET /v1/account/me dns:read Return the token owner and granted scopes.
GET /v1/dns/zones dns:read List the caller’s verified or pending zone claims.
POST /v1/dns/zones dns:write Create a zone claim and return its verification method.
GET /v1/dns/zones/{zone} dns:read Read one zone claim and its current verification material.
POST /v1/dns/zones/{zone}/verify dns:write Run the claim’s DNS proof check.
DELETE /v1/dns/zones/{zone} dns:write Revoke the caller’s claim without deleting DNS records.
GET /v1/dns/zones/{zone}/records dns:read List records in a verified zone.
POST /v1/dns/zones/{zone}/records dns:write Create a record in a verified zone.
GET /v1/dns/zones/{zone}/records/{id} dns:read Read one record by its identifier.
PATCH /v1/dns/zones/{zone}/records/{id} dns:write Update only the supplied fields of a record.
DELETE /v1/dns/zones/{zone}/records/{id} dns:write Delete one record.

Request fields

Create a zone claim

FieldTypeRequirementNotes
zone string Required Apex zone, lowercased by the server. Reverse zones are accepted.

Create a DNS record

FieldTypeRequirementNotes
type string Required One of A, AAAA, CNAME, MX, TXT, NS, SRV, CAA, PTR or SPF.
host string Optional Subdomain only. Use an empty string or @ for the apex.
value string Required Record content, such as an IP address, target name or text value.
ttl integer Optional Between 60 and 2,592,000 seconds. The default is 3,600.
priority integer Optional Required for MX and SRV; ignored for other types.

Update a DNS record

FieldTypeRequirementNotes
host string Optional Replacement subdomain.
value string Optional Cannot be empty after merging with the current record.
ttl integer Optional Between 60 and 2,592,000 seconds.
priority integer Optional Used only for MX and SRV.
Record type cannot be changed with PATCH. Delete the record and create another to change its type.

Examples

List zones

curl -H "Authorization: Bearer $CNF_TOKEN" \
  https://api.c.nf/v1/dns/zones

Claim a zone

curl -X POST https://api.c.nf/v1/dns/zones \
  -H "Authorization: Bearer $CNF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"zone":"example.com"}'

Verify a zone

curl -X POST https://api.c.nf/v1/dns/zones/example.com/verify \
  -H "Authorization: Bearer $CNF_TOKEN"

Create a record

curl -X POST https://api.c.nf/v1/dns/zones/example.com/records \
  -H "Authorization: Bearer $CNF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"A","host":"api","value":"192.0.2.10"}'

Quick start

  1. Create an API key with dns:write at Account → API keys, and copy it when it is shown.
  2. Store the token in a protected environment variable on your machine.
  3. Call GET /v1/account/me to confirm the token and its scopes.
  4. Claim a zone. The response says whether verification is instant, TXT-based or legacy NS-based.
  5. Complete the returned DNS proof when one is required, then call the verify endpoint.
  6. Create, read, update or delete records only after the claim is verified.

Changelog

v1.1.0 — zone management

  • Added API-based zone claims, including instant and TXT verification paths.
  • Added single-zone status and idempotent verification endpoints.
  • Added claim revocation without implicit record deletion.
  • Added verified, pending and all filters to the zone list.

v1.0.0 — initial release

  • Added Bearer-token authentication and per-user zone isolation.
  • Added read and write scopes.
  • Added account, zone-list and record-management endpoints.
  • Added a consistent response envelope with a request identifier.