veil

Complete an account proof

Complete a proof using the same key that started it.

POST/v1/links/{state}/complete
Access
Signed request
Client
Local or server client

Parameters#

NameLocationMeaning
stateRequiredPathThe private 64-character hexadecimal state returned by start.

JSON body#

NameLocationMeaning
codeJSONEmail confirmation code for email proofs.
returnCodeJSONOne-time callback return code for OAuth.

This call also needs the common signed-request headers, nonce and timestamp. Prepare and sign request.json before using the example. Request signing

Request examples#

cURL
curl -fsS -X POST \
  -H "X-Public-Key: $PUBLIC_KEY" \
  -H "X-Signature: $REQUEST_SIGNATURE" --json @request.json \
  "https://veil.rip/v1/links/PROOF_STATE/complete"

Response#

HTTP 200application/json

The proof state, with a grant when verified. Include the grant in a new signed card version to publish the account link.

FieldTypeMeaning
statestringPrivate proof-session state.
identityintegerTarget card number.
platformstringPlatform identifier.
methodstringProof method.
statusstringpending, returned, verified, used or expired.
expiresAtstringSession expiry time.
subjectstring | nullVerified normalised account identifier.
displaystring | nullVerified display name.
grantstring | nullSingle-use signed grant after verification, until used.
grantExpiresAtstring | nullGrant expiry time.
challengestring | nullPublic challenge instructions.
claimedstring | nullAccount requested at the start, for methods that require it.
holderinteger | nullCurrent card holding the verified account.
waitDaysinteger | nullTakeover cancellation period if another card holds it.
takeoverOpenboolean | nullWhether a takeover is already open.

Response headers#

HeaderValueMeaning
Content-Typeapplication/jsonUse the declared media type before choosing a parser.
DateHTTP dateServer time. Compare it with your clock when signing requests.
Retry-Afterinteger seconds, on rate_limited or busyWait at least this long before retrying. Other 429/503 codes do not necessarily include it.

Usage notes#

  • DNS and Fediverse use only nonce and timestamp in the body. The server checks the record or backlink.

Errors#

API errors return an error object. Use error.code for program logic; error.message is English text for people and can change. Response and error guide

HTTPCodeMeaning and next step
400invalid_json

The body is not a JSON object, contains duplicate keys or has trailing data.

Send one valid JSON object with Content-Type application/json.

400invalid_parameter

A path, query or body field is missing, malformed or outside its accepted range.

Correct the field named in error.message before sending again.

403key_mismatch

The signing key does not have authority over this card or operation.

Use an active device key with the required owner or member role.

404not_found

The requested card, key, alias, code or resource was not found.

Check the identifier. Temporary codes and previews may already have expired or been used.

409proof_pending

The OAuth sign-in has not returned yet.

Finish the provider sign-in in the browser that started it.

410proof_expired

The temporary account-proof session has expired.

Start a new linking session.

413payload_too_large

The request body exceeds this endpoint’s byte limit.

Reduce the encoded body. API-key management accepts at most 8 KiB.

422proof_failed

The provider, code or published proof did not verify.

Read error.message, correct the proof and retry while the session remains valid.

422proof_not_found

The requested DNS record, website file or profile backlink is not visible yet.

Publish the exact challenge and allow for propagation before checking again.

429too_many_attempts

This proof session exceeded its wrong-code attempt limit.

Start a new proof session. This code is distinct from quota rate_limited.

503provider_not_configured

The selected proof provider is unavailable on this server.

Read /v1/platforms for available methods.

Shared request errors
HTTPCodeMeaning and next step
400invalid_nonce

The signed-request nonce has an invalid format.

Generate a fresh 16 to 128-character nonce using letters, digits, _ or -.

400invalid_signature_alg

X-Signature-Alg is unsupported.

Use a supported signing algorithm and matching key/signature encoding.

400invalid_timestamp

The signed-request timestamp is not Unix milliseconds.

Use an integer Unix timestamp in milliseconds and sign the corrected request.

401invalid_signature

The HTTP request signature does not verify.

Sign the exact method, path and body bytes, without changing them afterwards.

401missing_signature

The request requires a device signature and its headers are missing or unreadable.

Send X-Public-Key, X-Signature and the signed-request metadata. An API key does not replace a device signature.

401stale_timestamp

The signed timestamp is outside the accepted clock window.

Compare the server Date header with your clock, then generate a fresh nonce, timestamp and signature.

403key_banned

The device signing key is suspended by Veil staff.

Use an authorised active key or ask staff to review the restriction.

409replayed_nonce

The signing key already used this nonce.

Check whether the previous request succeeded before making a fresh signed request.

429rate_limited

An anonymous, signed-request or API-key allowance was exceeded.

Wait at least Retry-After seconds. Share a queue and retry budget across your workers.

500internal_error

An unexpected server error occurred. Internal details are not returned.

Retry reads with bounded backoff. Check write results before sending a new signed operation.

503busy

The application has reached a temporary concurrency or database limit.

Wait Retry-After seconds and retry with bounded backoff.