DomainProof
Guides

The hosted verification page

What verificationUrl is, what data backs it, and when to redirect to it vs. building your own UI on the Frontend API directly.

Every domain claim returns a verificationUrl — a link you can hand to whoever needs to publish the DNS record, without building any verification UI of your own:

{
  "verificationUrl": "https://domainproof.dev/verify/f4h5k6m7n2p3q5r6s7t2u3v4w5"
}

The trailing segment is the claim's frontendToken: a 128-bit unguessable credential, distinct from the claim's own id, generated once when the domain is claimed. It authorizes exactly three things on exactly that one claim — reading its status, triggering a rate-limited recheck, and reading its event timeline — with no session and no API key involved.

What it's for

Redirect an end user (the person who actually controls DNS for the domain — often not the same person as your own logged-in user) straight to verificationUrl and they'll see the record to publish, its current status, and a way to trigger a recheck once they've done it. This is the fast path: no UI to build, no polling to wire up yourself.

If the domain's nameservers resolve to Cloudflare, the page also offers a one-click "Add this record for me" button instead of the manual instructions — see Cloudflare one-click DNS setup below.

Building your own UI instead

If you'd rather render the same information inside your own product, the page's data comes from three Frontend API endpoints, scoped by the same frontendToken:

curl https://frontend.api.domainproof.dev/frontend/verifications/f4h5k6m7n2p3q5r6s7t2u3v4w5
{
  "domain": "example.com",
  "mode": "live",
  "status": "pending",
  "projectName": "Acme",
  "provider": "unknown",
  "records": [
    {
      "label": "_domainproof-challenge.example.com",
      "type": "TXT",
      "value": "domainproof-verify=n4p3q5r6s7t2u3v4w5x6y2z3a4"
    }
  ],
  "check": { "outcome": "not_found", "checkedAt": "2026-07-22T10:00:05.000Z" },
  "updatedAt": "2026-07-22T10:00:05.000Z"
}

records[].label's _domainproof-challenge/domainproof-verify= prefix is this guide's placeholder, not a fixed string — it's derived from the claiming project's own name (here, projectName would actually derive a different prefix; don't pattern-match on this example's literal text).

Notice what's absent: no account id, no project id, no key material — only what's safe to show the person on the other end of this link, whether or not they're someone you trust with your own API key. provider is one of "cloudflare", "godaddy", "vercel", "route53", or "unknown", detected from the domain's nameservers — "unknown" for anything without a detector match, including every .test sandbox domain. The hosted page uses it to link to a matching DNS setup guide next to the manual record instructions, falling back to a generic any-provider guide when provider is "unknown".

curl -X POST https://frontend.api.domainproof.dev/frontend/verifications/f4h5k6m7n2p3q5r6s7t2u3v4w5/check

Runs the same check POST /v1/domains/:id/verify does — same transitions, same events — and returns the identical shape as the read above. This is rate limited (at most once every 15 seconds, and 20 times per hour, per token — 429 rate_limited beyond that), since it's exposed with no other credential than the link itself.

curl https://frontend.api.domainproof.dev/frontend/verifications/f4h5k6m7n2p3q5r6s7t2u3v4w5/events

A cursor-paginated timeline of the same event types Webhooks covers, with domainId/projectId/domain stripped from each entry.

An unknown token, or a released domain's now-defunct token, both 404 identically ({ "error": { "code": "not_found" } }) — there's no second factor to distinguish "wrong token" from "right token, wrong something else."

Cloudflare one-click DNS setup

When provider is "cloudflare", the hosted page can write the challenge's TXT record on the domain owner's behalf instead of asking them to do it by hand:

  1. GET /frontend/verifications/:token/cloudflare/authorize redirects to Cloudflare's OAuth consent screen.
  2. The domain owner grants (or denies) read-only zone access plus DNS edit access on Cloudflare.
  3. Cloudflare redirects to GET /frontend/cloudflare/callback, which creates the exact TXT record the manual instructions already show, then triggers the standard verify path and redirects back to the hosted page with ?cloudflare=<outcome> (success, denied, no_matching_zone, record_create_failed, exchange_failed, or not_found).

The authorize route is token-scoped like the rest of this plane; callback isn't (an OAuth redirect URI has to be a fixed, exact-match URL, so it can't carry the token in its own path) — it verifies the signed state round-tripped through Cloudflare's redirect instead, which carries the same claim binding. Both 404 with not_configured if the environment has no Cloudflare OAuth client set up. This flow is only reachable through the hosted page itself — there's nothing to wire up if you're building your own UI on the Frontend API directly, beyond gating your own "add it for me" affordance on provider === "cloudflare".

Which one should you use?

Redirecting to verificationUrl is the fastest way to ship — nothing to build, and it stays current as the page evolves. Build directly on the Frontend API when you want the verification step to feel native inside your own product instead of a redirect — or, if you're building in React, @domainproof/react gives you that same native feel as a drop-in <DomainVerification /> component instead of hand-rolled calls to this plane. All three read from the same data, so you can start with the redirect and move to your own UI later without changing anything about how domains are claimed or checked.

The hosted verification page — DomainProof docs