Errors

Every error carries an HTTP status, a JSON body with a human-readable detail and, for most of them, a stable code in the X-Error-Code header. Branch on the code; show the detail.

Error shape

The body is always {"detail": …}. The machine-readable part travels in a response header, so the body stayed the same for every client when the codes arrived:

response · 409 Conflict
HTTP/2 409
content-type: application/json
x-error-code: zone_exists_in_org

{"detail":"Domain already added to this organization"}

Branch on the code, not the message

detail is written for people and follows the Accept-Language header of the request: English by default, Portuguese with pt. Its wording may change at any time; X-Error-Code does not.

Errors without a code

When the body fails schema validation the status is 422, there is no X-Error-Code, and detail is an array pinpointing each offending field:

response · 422 Unprocessable Content
{
  "detail": [
    {
      "type": "greater_than_equal",
      "loc": ["body", "ttl"],
      "msg": "Input should be greater than or equal to 60",
      "input": 30,
      "ctx": { "ge": 60 }
    }
  ]
}

A few domain rules also answer 422 without a code, with a single message in detail: record content that does not match its type, a CNAME that would share its name with other records, and the zone and record limits of your plan. 502 from the DNS backend has no code either. Branch on the status and the endpoint there.

Rate limits

Every zone, record and Monitor route counts toward an hourly ceiling per organization, set by your plan. Over it the API answers 429 with X-Error-Code: rate_limited and a Retry-After header in seconds. Three expensive routes also have a per-minute ceiling of their own: import (10), ownership verification (12) and delegation checks (12).

response · 429 Too Many Requests
HTTP/2 429
retry-after: 1873
x-error-code: rate_limited

{"detail":"Too many attempts. Wait and try again."}

Code reference

Authentication and access

codeStatusWhen
not_authenticated401No Authorization header.
api_key_invalid401The token starts with qfx_ but is malformed.
api_key_revoked401Unknown or revoked API key.
invalid_token401Invalid or expired session token. Also what an API key gets on session-only routes (DNSSEC, key management).
api_key_scope_missing403The key lacks the scope for this route; detail names the scopes that would work.
role_admin_required403A session user without the owner or admin role tried a write.
org_suspended403A write in an organization suspended by the platform. Reads keep working.
org_not_found404The organization does not exist, or the key belongs to another one.
rate_limited429Plan or per-route ceiling reached. Wait Retry-After seconds.

Zones and records

codeStatusWhen
zone_not_found404The zone does not exist in your organization, or is outside the key's allowed_zone_ids.
zone_exists_in_org409The domain was already added to this organization.
zone_active_elsewhere409The domain is active in another organization.
verify_email_first_zones403A session user with an unverified e-mail tried to create a zone.
zone_import_input_required422Import without bind_file or "quick_scan": true.
zone_file_too_large422BIND file over 1 MB.
zone_file_invalid422BIND file that does not parse.
record_not_found404The record does not exist in this zone.
—422Record content, CNAME coexistence or plan limit. No code: read detail.
—502The DNS backend rejected the operation. Nothing was persisted: safe to retry.

DNSSEC

codeStatusWhen
dnssec_zone_inactive409The zone is not active yet: delegate it first.
dnssec_already_enabled409DNSSEC is already enabled on this zone.
dnssec_not_enabled409Disabling or removing a key on a zone that is not signed.
dnssec_enable_first409Rotating before enabling.
dnssec_remove_ds_first422Disabling without {"confirm": true}.
dnssec_last_key422Removing the only key: disable DNSSEC instead.
dnssec_key_not_found404Unknown key_id.

Monitor

codeStatusWhen
watched_domain_not_found404The watched domain does not exist in your organization.
watched_domain_exists409Your organization already watches this domain.
watched_domain_invalid422Not a valid domain name.
monitor_quota_reached422Watched-domain quota reached. Nothing in the batch was added.
watched_domain_txt_missing409The verification TXT is not visible yet.
monitor_check_invalid422Unknown check_kind.
monitor_check_invalid409Accepting a check that was never measured.
certificate_domain_unverified409Adding hosts to a domain that has not proven ownership.
certificate_host_not_found404The host is not in this domain's inventory.
certificate_host_exists409The host is already in the inventory.
certificate_host_invalid422Not a name strictly below the watched domain (wildcards and the apex are rejected).
certificate_host_not_public422The host does not resolve to a public IP.
certificate_quota_reached422Certificate-host quota for this domain reached.
service_profile_unknown422Profile outside the catalog (web, webmail, email).
service_check_exists409The profile is already on for this host.
service_check_not_found404The profile is not on for this host.
service_quota_reached422Service-check quota for this domain reached.

Retrying safely

502 means the DNS backend refused the operation before anything was persisted: retry with backoff. 429 tells you how long to wait in Retry-After. Other 4xx errors are yours to fix: retrying the same request fails the same way.