The two states
You see the same two states on every surface:
(
census in the event name is the API’s name for BotShield Gate.)
The botshield:census-status event fires after the widget’s first check. On a Human Gate, unavailable at that point usually means “a confirmation on the phone is needed”, and the widget continues to the QR code or deep link. Wait for botshield:success or botshield:failure before you decide anything.
Why there is no score
A score asks you to pick a threshold and to accept that some humans fall under it. BotShield does not estimate how human a session looks. It asks a human to confirm on their own device, and reports whether that happened. That is also why there is no “bot” verdict. BotShield can observe that a human confirmed. It cannot observe that a human is absent. A person who declines, walks away, or has an unsupported phone looks the same as an automated client that never finishes. BotShield reports Unavailable in all of those cases and leaves the policy decision to you.What Unavailable covers
Agate.unavailable webhook comes in two shapes.
A failure carries failed_at and a reason. It is sent when the BotShield app reports that the confirmation did not succeed, and when a request that was opened fails on BotShield’s side.
environment tells you which environment the request ran in. metadata holds only the keys you supplied when you created the request, and is left out when you supplied none. See Webhook events.
An expiry carries expired_at and no reason. A verification request expires five minutes after it is created. The expiry event is sent when the human opens the request after that point.
reason is there to help you choose the right message: a person who cancelled needs a retry button, and a phone with no screen lock needs a line of help. All four values are still the same result state, Unavailable.
The widget reports its own reason on botshield:failure. expired and failed are user outcomes. origin_not_allowed (your page’s origin is not on the site key’s list), invalid_site_key (the site key is unknown or revoked), and gate_not_found (the key’s environment has no Active gate with that gate key) are configuration errors on your side. The separate botshield:cancel event, with request_id in its detail, fires when the person closes the widget’s modal before finishing. The widget goes back to idle, and no result state is reported, because the person can start again. See Web component for the full list.
The verification status enum
GET /verification/status?request_id=… returns a status field. It needs no API key, and it returns its payload directly under data.
On an Age Gate (Beta),
status: "completed" means a human confirmed. It does not mean the age threshold was met. Read age_verdict as well. See Age Gate.Design for Unavailable
Unavailable is an ordinary outcome for real people. Phones run out of battery, people change their minds, and some people do not have the BotShield app yet. Offer, never punish.- Offer a way forward. Let the user try again, or route them to the path you already had: a waiting room, your existing challenge, manual review, or a later attempt.
- Do not label the user. Avoid copy such as “bot detected” or “verification failed, access denied”. Prefer “We couldn’t confirm you just now.”
- Do not penalize the account. Do not lock, flag, or downgrade a user because a verification was Unavailable.
- Reward Verified instead. Give verified humans the better path: earlier access, a skipped challenge, a reserved allocation. The gate then pulls people in rather than pushing them out.
- Keep retries cheap. A new attempt creates a new request. Nothing carries over from the Unavailable one.
BotShield ID
Why returning humans pass Recent Presence gates instantly.
Webhook events
Full payloads for
gate.human_verified and gate.unavailable.Web component
Widget events and failure reasons.
Device security
What happens when the user’s phone has no screen lock.
