Docs

Claim and verify a domain

Three calls: create a claim, publish one DNS TXT record, and verify it. Every example below is the real shape of the /api/v1 response for the step it sits next to.

Every route

ActionMethodRoute
createDomainClaimPOST/api/v1/claims
getDomainClaimGET/api/v1/claims/:id
listDomainClaimsGET/api/v1/claims
checkDnsRecordPOST/api/v1/claims/:id/check
verifyAndClaimDomainPOST/api/v1/claims/:id/verify
releaseDomainClaimDELETE/api/v1/claims/:id

Every request needs a signed-in session, which is checked before anything else, including the request body. Every response, success or failure, has one shape: { success: true, data } or { success: false, error }. The discriminant is always success, never ok.

For the full request and response shape of every route, see the API reference. It is a browsable OpenAPI spec, generated from the same definitions these routes run on, so it cannot drift from what the API actually does.

1. Create a claim

Call POST /api/v1/claims with the domain you want to prove you own. One call creates the claim, with status pending_dns, and returns the exact DNS record to publish. There is no second call to fetch the instructions.

A claim is good for 7 days from creation. If it isn't verified by then, it expires and a fresh POST /api/v1/claims starts a new one with a new token.

2. Add the TXT record

The response from step 1 includes a record object with three fields:

Name
_vouch-challenge.example.com
Type
TXT
Value
vouch-domain-verify=M6KqqqYx3NNo0v4qkZz2Pmw8XrhvS4BQ5y6k5AKa6_E
Important: The most common mistake is a doubled suffix

Most DNS providers already append your domain to whatever you type into a record's Name/Host field. Paste the full name we gave you — _vouch-challenge.example.com — into that field, and the record you actually publish ends up at _vouch-challenge.example.com.example.com, not where we're looking. Enter only the label before your domain (_vouch-challenge, with the domain left for the provider to add) and you're fine.

When that happens you get DNS_RECORD_WRONG_HOSTNAME rather than a plain "not found": your token was there, just one level too deep. The error's evidence field carries both hostnames, the one queried and the one where the token turned up, so you can show the difference directly.

Check the record

POST /api/v1/claims/:id/check runs a live DNS lookup and tells you what it found, without touching the claim. Use it to confirm a record is in place, or to show someone why theirs isn't working yet. The claim stays pending_dns even when the record matches. Only /verify advances it.

3. Verify

POST /api/v1/claims/:id/verify runs the same lookup as /check and, on a match, advances the claim to verified and stamps verifiedAt.

It is a POST rather than a GET because it changes something. Browsers, proxies and link prefetchers all treat GET as safe to issue on their own, and none of them should be able to start a verification you didn't ask for.

Still propagating

DNS propagation isn't instant, and a record you just saved may take anywhere from seconds to an hour to become visible. Calling /verify before then returns DNS_NO_TXT_FOUND, which means nothing is wrong yet. Keep calling /verify on a widening interval until it succeeds or the claim expires, rather than treating the first miss as a failure.

Read success, not the status code

/check and /verify return HTTP 200 when the lookup ran fine but the record isn't published yet, has the wrong value, or is sitting at a doubled hostname. The request was handled correctly; the answer just happens to be no.

Six codes arrive this way: DNS_NO_TXT_FOUND, DNS_TOKEN_MISMATCH, DNS_RECORD_WRONG_HOSTNAME, DNS_NXDOMAIN, DNS_LOOKUP_TIMEOUT, and DNS_LOOKUP_FAILED. The doubled-suffix response above is one of them: 200 OK, with "success": false in the body.

Read success in the response body, not the status code. The status tells you whether the request was handled. Only success tells you whether the thing you asked for happened.

A 4xx means something different: the request itself cannot succeed, whatever DNS says. Creating a claim for a domain another account has already verified returns DOMAIN_ALREADY_CLAIMED with a genuine 409 Conflict, because no DNS record could make that request valid.

After verifying

GET /api/v1/claims/:id is a plain read. It performs no DNS lookup and changes nothing, so you can call it as often as you like. GET /api/v1/claims lists every claim your account can see, and DELETE /api/v1/claims/:id releases one you no longer want.

A verified claim stays verified. Vouch does not re-check the record afterwards, so removing it will not revoke the claim, and releasing the domain is the only way to give it up.

Every error code

Every error carries a code, a plain-language message and remediation, an isRetriable flag, and a fault. The last two answer different questions. isRetriable asks whether the same call could succeed later; fault asks who has to do something about it. DNS_TOKEN_MISMATCH is retriable, but only once the record is corrected. DNS_LOOKUP_TIMEOUT is retriable and needs nobody: it clears on its own.

Show all 18 error codes
CodeWhat it meansRetriableFault
VALIDATION_FAILEDThe request body failed schema validation.NoUser
DOMAIN_INVALID_FORMATThe domain string isn't a valid hostname.NoUser
DOMAIN_IS_PUBLIC_SUFFIXThe domain is a shared suffix (e.g. co.uk) nobody can claim alone.NoUser
DOMAIN_ALREADY_CLAIMEDAnother account already verified this domain.NoUser
CLAIM_NOT_FOUNDThe claim id doesn't resolve to a row this org can see.NoUser
CLAIM_NOT_PENDINGThe claim's current status doesn't accept this transition.NoUser
CLAIM_ALREADY_VERIFYINGA verification attempt for this claim is already in flight.YesExternal
CLAIM_EXPIREDThe claim's 7-day window closed before it was verified.NoUser
DNS_NO_TXT_FOUNDNo TXT record was found at the expected name yet.YesExternal
DNS_TOKEN_MISMATCHA TXT record was found at the right name, but its value doesn't match.YesUser
DNS_RECORD_WRONG_HOSTNAMEThe token was found, but at a doubled-suffix hostname instead of the one queried.YesUser
DNS_NXDOMAINThe domain doesn't resolve at all.NoUser
DNS_LOOKUP_TIMEOUTThe DNS resolver didn't respond in time.YesExternal
DNS_LOOKUP_FAILEDThe DNS resolver returned an error.YesExternal
RATE_LIMITEDToo many requests — see the Retry-After header.YesExternal
UNAUTHENTICATEDNo session was presented at all.NoUser
UNAUTHORIZEDA session was presented, but it isn't this claim's org.NoUser
INTERNAL_ERRORAn unhandled exception on our side, not a decision anyone made.YesSystem