Documentation menuServer verification

Server verification

Reference for POST /v1/siteverify: request, response, every reason with what to do, HTTP statuses, retries and failure policy.

On this page

Request #

HTTP
POST /v1/siteverify HTTP/1.1
Host: blockalt.click
Authorization: Bearer ba_sk_…
Content-Type: application/json

{
  "token": "<the token from the browser>",
  "hostname": "example.com",
  "action": "signup",
  "remoteip": "203.0.113.7",
  "idempotencyKey": "signup-7d1f0c2a"
}
FieldRequiredRules
tokenYesString up to 4,096 characters. Send it as received, including an empty string: an unpaid or paused site answers success: true for it
hostnameYesThe hostname of the page that got the token: lower case, letters, digits, dots and hyphens, no scheme or port. Fix it on the server
actionYesThe action you are about to perform, as configured for the site: 1–64 of A–Z a–z 0–9 _ -. Fix it per route on the server
remoteipNoThe visitor's IP address as your server sees it. Enables the wrong_client check
idempotencyKeyNo1–64 of A–Z a–z 0–9 _ -. Makes a retry of the same call safe

The body is JSON of at most 8 KiB. Unknown fields are refused with invalid_request, so typos do not go unnoticed. The secret key goes only in the Authorization header.

Response #

JSON
{ "success": true, "reason": "accepted", "id": "5b0b6f0e-5d0c-4bd2-9f7e-0d3c2a1e9b77" }
FieldMeaning
successtrue only for accepted, unpaid and paused. Perform the action only when it is true
reasonWhy; see reasons below. New reasons may be added: treat an unknown reason with success: false as a refusal
idA unique id for this decision. Log it, and quote it when you contact support

An accepted token also returns its action, hostname and issuedAt (ISO 8601), and answers for test keys carry test: true. More fields may appear; ignore those you do not use.

Reasons #

success: true — perform the action

accepted
The token is valid, fresh and unused for this site, hostname and action. It is now consumed.
Perform the action.
unpaid
The workspace is priced, has used its free allowance for the month and has no credit left, so the action was not checked. Not billed.
Perform the action. Top up to restore protection.
paused
Protection is paused for the site, so the action was not checked. Not billed.
Perform the action. Resume protection in the console when ready.

success: false — refuse the action

invalid_token
The token is missing, malformed, forged or signed by an unknown key.
Refuse. Check that your form or request actually carries the token field.
expired_token
The token is older than 120 seconds.
Refuse and ask for a new attempt. Request tokens at submit time, not at page load.
already_used
The token was already accepted once: a replay or a double submission.
Refuse. Use a new token per attempt; use idempotencyKey for safe retries.
wrong_site
The token belongs to another site.
Refuse. Check that the secret key and the page's site key belong to the same site.
wrong_hostname
The token was issued on another hostname, or the hostname is no longer an origin of the site.
Refuse. Send the page's hostname, in lower case, without port.
wrong_action
The token was issued for another action, or the action is no longer configured.
Refuse. Use the same action name in the browser and on the server.
wrong_client
You sent remoteip and the token was earned from a different network (IPv4 address or IPv6 /64).
Refuse. If legitimate users hit this, stop sending remoteip for that action.
stale_config
The site's published configuration changed after the token was issued.
Refuse and let the visitor retry; it happens briefly after publishing or rolling back.
rule_blocked
Your rules block this action, or block all issuance (lockdown).
Refuse. Expected while the rule is active.
suspended
BlockAlt support suspended the site or its workspace for abuse. Every verification is refused, whatever the token, and nothing is charged.
Refuse. The console shows the note from support; reply to support's email to resolve it.

HTTP errors — nothing was verified

invalid_secret
HTTP 401: the secret key is wrong or was revoked.
Fix the key on your server. Every action is refused until you do.
invalid_request
HTTP 400, 413 or 415: the request is malformed, too large or not sent as JSON.
Fix the request; see the field rules below.
rate_limited
HTTP 429: too many verification requests. Tokens BlockAlt did not issue for the site (forged, malformed, expired, replayed, and empty ones unless the site is paused or unpaid) have a small budget of their own, so a flood of them is refused without touching your site's budget.
Refuse or queue; retry after the Retry-After delay if present.
service_unavailable
HTTP 503: a temporary failure on our side.
Apply your failure policy. Retry only with an idempotencyKey.
policy_unavailable
HTTP 503: the site's protection policy could not be loaded.
Apply your failure policy and contact support if it persists.

During issuance in the browser, the script can also be refused with origin_denied, domain_unverified, action_denied, site_disabled (unknown site key), suspended (configuration and tokens of a suspended site), challenge_required, invalid_proof, challenge_expired or challenge_used. Those never reach your server: the page sees them as browser errors carrying the reason.

HTTP statuses #

HTTP statusBodyMeaning
200{success, reason, id}A decision: success may be true or false
400reason: "invalid_request"Missing or malformed fields, unknown fields, invalid JSON
401reason: "invalid_secret"Missing, wrong or revoked secret key
413reason: "invalid_request"Body larger than 8 KiB
415reason: "invalid_request"Body not sent as application/json
429reason: "rate_limited"Too many requests; may carry Retry-After
503reason: "service_unavailable" or "policy_unavailable"Temporary failure
Other, timeout, network error—Treat as service_unavailable

Retries and idempotency #

A token works once. If your call times out after BlockAlt consumed the token, a plain retry returns already_used. To retry safely, send an idempotencyKey: when a call with that key accepted the token, repeating the same key with the same token, within the token's lifetime, returns that accepted decision again (with the same id) instead of already_used, and it is billed once. Refusals are not stored: a retry of a refused token is checked again from scratch.

  • Generate the key on your server for each attempt, for example from your request id.
  • Retry at most once, on a timeout, a network error or a 5xx, with the same key and token.
  • Never retry a decision: success: false with a 200 is final for that token.

remoteip and wrong_client #

Each token remembers the network it was earned from (an IPv4 address, or an IPv6 /64), as a keyed hash. When you send remoteip, BlockAlt compares the two and refuses with wrong_client when they differ, so a token solved on one machine cannot be spent from another.

Send it only when your server sees the real visitor address (behind a proxy, take it from the header your proxy sets, never from arbitrary X-Forwarded-For). Legitimate visitors can change network between getting the token and submitting: mobile handovers, carrier NAT, VPNs, or a browser that reaches BlockAlt over IPv6 and your site over IPv4. Watch the decision log for wrong_client before you rely on it for an action.

Timeouts and failure policy #

  • Use a short timeout, about 3 seconds. Do not follow redirects.
  • Fail closed by default: on a timeout, network error or 5xx, refuse the action and ask the visitor to try again. The official SDK does this.
  • For low-risk actions you may fail open (accept without a decision), but log it and alert on it, so an outage is not mistaken for quiet traffic.
  • Verify on every attempt. Never cache a decision or reuse a token across requests.

Security checklist #

  • Keep the secret key out of the browser, logs and source control; rotate it from the console if it leaks.
  • Fix hostname and action on the server.
  • Accept the action only on success === true, never on HTTP 200 alone.
  • Verify before any side effect: before creating the account, sending the email or charging the card.

Something unclear or missing on this page? Write to support@blockalt.click.