DomainProof
Reference

API reference

Every endpoint on the Public and Frontend planes, plus the error shape every non-2xx response uses.

Base URL for the Public API: https://api.domainproof.dev. Base URL for the Frontend API: https://frontend.api.domainproof.dev. Every non-2xx response from either plane follows the same shape — see Errors below. This document is hand-maintained alongside the api's own OpenAPI 3.1 schema (GET /v1/openapi.json), which covers the same two planes machine-readably; the summary tables below link out to a full parameters + response breakdown for each endpoint.

Public API (/v1/*)

Authenticated with a project API key (Authorization: Bearer dp_test_... / dp_live_...) — see Authenticate with an API key. Rate limited per key: 100 requests per rolling minute, returning 429 rate_limited with a Retry-After header once exceeded.

MethodPathDescription
POST/v1/domainsClaims a domain and issues a challenge. Accepts an optional external_id.
GET/v1/domainsCursor-paginated list of domains claimed by the key's project/mode. Filterable by external_id and/or domain.
GET/v1/domains/:idGets a claimed domain and its current verification record instructions.
DELETE/v1/domains/:idReleases a domain claim.
POST/v1/domains/:id/verifyRuns the DNS check for a claim and returns the updated domain plus the check's outcome. Safe to call repeatedly.
POST/v1/domains/:id/regenerateIssues a fresh challenge for a pending or failed domain, restarting verification.
GET/v1/domains/:id/eventsCursor-paginated timeline of events published for a domain.
POST/v1/component-sessionsMints a short-lived, single-use session token for a drop-in component to claim one domain on your behalf.

See Claim and verify a domain for the full walkthrough, and How verification works for what each status and check outcome means.

Domain — the object every endpoint below returns, either alone ({ domain: Domain }) or as a page ({ domains: Domain[], nextCursor }):

FieldTypeDescription
idstringThe claim's unique id — the value every :id path parameter below expects.
domainstringThe claimed hostname.
modestringtest or live.
statusstringOne of not_started, pending, verified, temporarily_failed, failed — see How verification works.
external_idstring | nullThe id passed to POST /v1/domains, or null if none was set.
createdAtstringISO 8601 timestamp of the claim.
updatedAtstringISO 8601 timestamp of the domain's last change.
verifiedAtstring | nullISO 8601 timestamp of the last passing check, or null if never verified. Updates on every passing check, not just the first.
verificationUrlstringThe hosted verification page URL for this claim — embeds its frontendToken.
recordsarrayThe DNS record(s) that prove ownership — see Domain.records[] below.

Domain.records[] — one entry per record to add (currently always exactly one, the ownership TXT record):

FieldTypeDescription
typestringThe DNS record type, e.g. TXT.
namestringThe record name/host to create.
valuestringThe record value/content.
purposestringAlways "ownership".
descriptionstringPlain-language instructions for adding the record.
statusstringMirrors the parent domain's status.

Check — one verification attempt, returned as check alongside domain by POST /v1/domains/:id/verify:

FieldTypeDescription
outcomestringOne of found, wrong_value, not_found, unreachable, expired.
checkedAtstringISO 8601 timestamp of this check.
expectedstringPresent only on wrong_value — the record value DomainProof expected to find.
detectedstring[]Present only on wrong_value — the value(s) actually found at the record name.
explanationstringPresent on not_found, unreachable, and expired — plain-language copy safe to show whoever published the record.

Event — one entry in a domain's timeline, returned by GET /v1/domains/:id/events:

FieldTypeDescription
idstringThe event's unique id.
typestringe.g. domain.claimed, domain.verified — see Event types for the full list.
modestringtest or live.
payloadobjectEvent-specific detail — see Delivery payload for its shape.
createdAtstringISO 8601 timestamp.

POST /v1/domains

Claims a domain for the authenticated key's project and mode, issuing a fresh verification challenge.

NameInTypeRequiredDescription
domainbodystringYesThe hostname to claim (1–253 characters).
external_idbodystringNoCorrelates the claim with your own end user (1–256 characters).

Returns 201 with { domain: Domain }.

StatusCode
400invalid_request, sandbox_requires_test_mode
401invalid_api_key
409domain_already_claimed
429rate_limited

GET /v1/domains

Cursor-paginated list of domains claimed by the key's project and mode. Filterable by external_id and/or domain.

NameInTypeRequiredDescription
limitqueryintegerNoMax results per page, 1–100 (default 20) — see Pagination.
cursorquerystringNoOpaque cursor from a previous page's nextCursor — see Pagination.
external_idquerystringNoFilter to domains claimed under this id (1–256 characters).
domainquerystringNoFilter to an exact hostname match (1–253 characters).

Returns 200 with { domains: Domain[], nextCursor: string \| null }.

StatusCode
400invalid_request
401invalid_api_key
429rate_limited

GET /v1/domains/:id

Gets a claimed domain and its current verification record instructions.

NameInTypeRequiredDescription
idpathstringYesThe domain claim's id.

Returns 200 with { domain: Domain }.

StatusCode
401invalid_api_key
404not_found
429rate_limited

DELETE /v1/domains/:id

Releases a domain claim.

NameInTypeRequiredDescription
idpathstringYesThe domain claim's id.

Returns 200 with { domain: Domain } — the now-released domain.

StatusCode
401invalid_api_key
404not_found
429rate_limited

POST /v1/domains/:id/verify

Runs the DNS check for a claim and returns the updated domain plus the check's outcome. Safe to poll — re-runs the check every time it's called.

NameInTypeRequiredDescription
idpathstringYesThe domain claim's id.

Returns 200 with { domain: Domain, check: Check }.

StatusCode
401invalid_api_key
404not_found
429rate_limited

POST /v1/domains/:id/regenerate

Issues a fresh challenge for a pending or failed domain, restarting verification.

NameInTypeRequiredDescription
idpathstringYesThe domain claim's id.

Returns 200 with { domain: Domain } — carrying the fresh challenge.

StatusCode
401invalid_api_key
404not_found
409invalid_status
429rate_limited

GET /v1/domains/:id/events

Cursor-paginated timeline of events published for a domain (claimed, checks, transitions), oldest first.

NameInTypeRequiredDescription
idpathstringYesThe domain claim's id.
limitqueryintegerNoMax results per page, 1–100 (default 20) — see Pagination.
cursorquerystringNoOpaque cursor from a previous page's nextCursor — see Pagination.

Returns 200 with { events: Event[], nextCursor: string \| null }.

StatusCode
400invalid_request
401invalid_api_key
404not_found
429rate_limited

POST /v1/component-sessions

Mints a short-lived, single-use session token (expires in an hour if never spent) for a drop-in component to claim one domain on the caller's behalf, without handing the component the api key itself. The component spends it once against POST /frontend/component-sessions/:sessionToken/claim below.

NameInTypeRequiredDescription
externalIdbodystringNoThe end user about to claim a domain, in your own id scheme (1–255 characters).

Returns 201 with { sessionToken: string, expiresAt: string }.

StatusCode
400invalid_request
401invalid_api_key
429rate_limited

Frontend API (/frontend/*)

No session, no API key — each domain claim carries its own unguessable frontendToken (embedded in verificationUrl) that authorizes exactly these routes for that one claim. See The hosted verification page.

MethodPathDescription
GET/frontend/verifications/:tokenReads a claim's status, record instructions, and last check outcome.
POST/frontend/verifications/:token/checkRuns the DNS check for a claim (rate limited: 1 per 15 seconds, 20 per hour) and returns the same shape as the GET above.
GET/frontend/verifications/:token/eventsCursor-paginated timeline of events for a domain, with no account/project ids in the payload.
GET/frontend/verifications/:token/cloudflare/authorizeStarts the Cloudflare one-click DNS setup flow — redirects to Cloudflare's OAuth consent screen.
GET/frontend/cloudflare/callbackThe Cloudflare OAuth callback; redirects back to the hosted verification page with an outcome.
POST/frontend/component-sessions/:sessionToken/claimSpends a component session (rate limited: 10 per hour, per session token) to claim a domain; returns the claim plus its own frontendToken.

Verification — the object every endpoint below returns, the hosted page's own view of a claim (no account id, project id, or key material):

FieldTypeDescription
domainstringThe claimed hostname.
modestringtest or live.
statusstringOne of not_started, pending, verified, temporarily_failed, failed.
projectNamestringThe claiming project's display name.
providerstringcloudflare, godaddy, vercel, route53, or unknown — the DNS provider detected from the domain's nameservers; cloudflare gates the one-click setup button.
recordsarray{ label, type, value }[] — the DNS record(s) to add.
checkobject | nullnull until the first check ever runs; otherwise { outcome, checkedAt, expected?, detected? } — the same outcomes as the Public API's Check, but without explanation.
updatedAtstringISO 8601 timestamp.

ClaimResult — every Verification field above, plus:

FieldTypeDescription
frontendTokenstringThe new claim's token — the credential for every other Frontend API route above.

FrontendEvent — one entry in a domain's timeline, the Frontend API's stripped-down view of Event above:

FieldTypeDescription
idstringThe event's unique id.
typestringe.g. domain.claimed, domain.verified.
modestringtest or live.
createdAtstringISO 8601 timestamp.
outcomestringPresent only on domain.check_failed events.

GET /frontend/verifications/:token

Reads a claim's status, record instructions, detected DNS provider, and last check outcome by its frontendToken.

NameInTypeRequiredDescription
tokenpathstringYesThe domain claim's frontendToken — the sole credential for this plane.

Returns 200 with a Verification.

StatusCode
404not_found

POST /frontend/verifications/:token/check

Runs the DNS check for a claim (rate limited: 1 per 15 seconds, 20 per hour, per token) and returns the same shape as the GET above.

NameInTypeRequiredDescription
tokenpathstringYesThe domain claim's frontendToken — the sole credential for this plane.

Returns 200 with a Verification, after the check runs.

StatusCode
404not_found
429rate_limited

GET /frontend/verifications/:token/cloudflare/authorize

Redirects to Cloudflare's OAuth consent screen with a PKCE code_challenge and a signed state binding the request to this one claim — see Cloudflare one-click DNS setup.

NameInTypeRequiredDescription
tokenpathstringYesThe domain claim's frontendToken — the sole credential for this plane.

Returns 302, redirecting to Cloudflare's consent screen — no JSON body on success.

StatusCode
404not_found, not_configured

GET /frontend/verifications/:token/events

Cursor-paginated timeline of events published for a domain, with no account/project ids in the payload.

NameInTypeRequiredDescription
tokenpathstringYesThe domain claim's frontendToken — the sole credential for this plane.
limitqueryintegerNoMax results per page, 1–100 (default 20) — see Pagination.
cursorquerystringNoOpaque cursor from a previous page's nextCursor — see Pagination.

Returns 200 with { events: FrontendEvent[], nextCursor: string \| null }.

StatusCode
400invalid_request
404not_found

POST /frontend/component-sessions/:sessionToken/claim

Claims a domain through the exact same path POST /v1/domains uses (same validation, same conflict/sandbox rules), with the project/mode and external_id carried over from the session. Rate limited: 10 attempts per hour, per session token.

NameInTypeRequiredDescription
sessionTokenpathstringYesThe single-use token minted by POST /v1/component-sessions.
domainbodystringYesThe hostname to claim (1–253 characters).

Returns 201 with a ClaimResult.

StatusCode
400invalid_request, sandbox_requires_test_mode
404not_found
409domain_already_claimed
429rate_limited

GET /frontend/cloudflare/callback

Verifies state, exchanges the code, finds the matching zone, creates the TXT record, and triggers the standard verify path, then redirects to the hosted verification page. Not nested under /verifications/:token — the signed state round-tripped through Cloudflare's own redirect carries the claim binding instead, since a registered redirect_uri has to be a fixed, exact-match URL.

NameInTypeRequiredDescription
codequerystringNoThe OAuth authorization code, present on a successful grant.
statequerystringNoThe signed state minted by the authorize redirect, verified before anything else runs.
errorquerystringNoCloudflare's OAuth error code, present when the domain owner declines the consent screen.

Returns 302, redirecting to the hosted verification page with ?cloudflare=<outcome> (success, denied, no_matching_zone, record_create_failed, exchange_failed, or not_found) — even on failure, as long as state itself is trustworthy.

StatusCode
400invalid_request
404not_configured

Dashboard API (/dashboard/*)

Authenticated by your login session — this is what the DomainProof dashboard itself calls, covering project management, API key management, and webhook endpoint management. It's not meant to be called directly by an integration; everything an integration needs is on the Public and Frontend planes above, and it isn't part of the generated OpenAPI document either — see the README's endpoints table for its full route list.

Pagination

Every list endpoint is cursor-paginated the same way: ?limit= (default 20, max 100) and ?cursor=, the opaque nextCursor from the previous page. nextCursor is null once there's nothing left to fetch.

Errors

Every non-2xx response follows the same shape:

{ "error": { "code": "not_found", "message": "Domain not found" } }

Named codes you'll actually see (each endpoint above lists which of these it can return):

CodeStatusMeaning
invalid_request400The request body or query parameters didn't parse, or the domain you're trying to claim is malformed.
sandbox_requires_test_mode400You tried to claim a .test sandbox domain with a live-mode key.
invalid_api_key401The presented key is missing, malformed, unknown, revoked, or wrong — these are deliberately indistinguishable from each other.
not_found404The resource doesn't exist, or belongs to a different project/key than the one authenticating the request — those two cases are also deliberately indistinguishable.
not_configured404A feature that needs environment configuration isn't set up here — currently only the Cloudflare one-click DNS setup flow.
domain_already_claimed409You tried to claim a domain your project already has claimed in this mode.
invalid_status409You tried to regenerate a challenge for a domain that isn't currently pending or failed.
rate_limited429You've exceeded the rate limit for this key or token. The response carries a Retry-After header (seconds) telling you when to try again.

Any request can also fail with 500 internal_error on an unhandled server error — not tied to any one endpoint, so it's omitted from each endpoint's own table above.

API reference — DomainProof docs