Siteverify referenceNew

Request and response fields, error codes, idempotency, remoteip check and rate limits of the OpenWAF Verify siteverify endpoints.

On this page

Your server calls siteverify to check a token produced by the OpenWAF Verify widget. There are two endpoints with the same semantics:

EndpointUse it when
POST https://<your host>/__waf/challenge/siteverifyYour backend can reach a name proxied by OpenWAF. Any name served by the edge can verify a token of any widget, even of another zone.
POST https://dash.openwaf.dev/v1/challenge/siteverifyYour backend calls out directly. No API token is needed: the widget secret authenticates the call.

A token is single use across both: one spent on the edge is spent on the control plane too, and the other way round.

Request

The body is application/x-www-form-urlencoded or, with Content-Type: application/json, a JSON object. At most 8192 bytes.

FieldRequiredMeaning
secretyesThe widget secret (ows_…).
responseyesThe token from the owaf-verify-response field (v1.…, at most 2048 characters).
remoteipnoThe visitor's IP address. When given, it must be in the same IPv4 /24 or IPv6 /64 as the browser that solved the challenge.
idempotency_keynoUp to 128 characters. See Idempotency.

Response

The answer is JSON and always 200, except a malformed body (400), a method other than POST (405) and a rate limit (429 with Retry-After).

{"success": true,
 "challenge_ts": "2026-10-28T10:15:03Z",
 "hostname": "example.com",
 "action": "signup",
 "cdata": "order-1234",
 "error-codes": [],
 "metadata": {"mode": "managed", "interactive": false, "risk": "low"}}
FieldMeaning
successtrue only for a valid, unspent token of this widget.
challenge_tsWhen the token was issued (RFC 3339, UTC).
hostnameHost name the widget was rendered on. Check that it is yours.
actionThe widget's data-action. Check that it matches the form.
cdataThe widget's data-cdata, if any.
error-codesEmpty on success; otherwise one of the codes below.
metadata.modeWidget mode: managed, non_interactive or invisible.
metadata.interactivetrue when the visitor went through the «I'm a person» step.
metadata.risklow, or high when interaction was required.
metadata.result_with_testing_keytrue when a test secret answered.

A failed check answers {"success": false, "error-codes": ["…"]}.

Error codes

CodeMeaning
missing-input-secretsecret is empty.
invalid-input-secretThe secret is malformed or isn't a current secret of the widget that issued the token.
missing-input-responseresponse is empty.
invalid-input-responseThe token is malformed, has a bad signature or belongs to an unknown widget.
timeout-or-duplicateThe token expired (300 seconds) or was already spent.
remoteip-mismatchremoteip is not in the network the token is bound to.
bad-requestThe body is not a valid form or JSON object, is too large, or the method is not POST.
rate-limitedToo many calls; wait for Retry-After.
internal-errorSingle use could not be checked right now. Retry; the token was not spent.

Idempotency

A token is spent by the first successful call, so a retry after a network error would get timeout-or-duplicate. Send an idempotency_key to make retries safe: a call with the same secret, response and key within 300 seconds returns the first answer without spending anything again.

The key is remembered by the verifier that answered, not shared: the edge node that served the first call, or the control plane. Retry against the same endpoint. internal-error answers are never remembered.

Rate limits

EndpointLimits
Your host (/__waf/challenge/siteverify)1000 calls per minute per widget secret, plus the edge's per-IP limit.
Control plane600 calls per minute per client IP and 1000 per widget secret.

The widget's own endpoints on your host are limited too: 60 challenges and 20 solutions per minute per visitor IP.

Calls to avoid

  • Calling siteverify from the browser: the secret must stay on your server. The edge endpoint sends no CORS headers.
  • Accepting a form when siteverify could not be reached. Treat any answer other than "success": true as a failure.
  • Reusing a token after a failed form submission: call owafVerify.reset(id) for a new one.

Last updated:

↑↓ to navigate↵ to openesc to close