Skip to main content
Your server learns about a Secured Account in two ways: the attestation token that the widget hands to your page, and the webhook events that BotShield sends to your endpoint. Use the token for the decision in the request. Use the webhooks to keep your records current, because an account can be unlinked when the person is not on your site. Every payload on this page carries an opaque handle or a request ID. None carries an email address, a name, a device, a BotShield ID, or any account on another platform.

The token

The attestation token is the same signed JWT that BotShield Gate issues. See Verify on your server for the format, the issuer, the 120-second lifetime and the published keys. Secured Accounts adds three claims. A token without trusted_since is never trusted. Do not read a missing date as “secured since the beginning”.

Verify with the API

POST /sdk/verify-token needs no API key. Pass your organization ID as audience and a token issued for any other organization is rejected.
The result sits under data.data. An error arrives with HTTP 200 as data.error. An invalid or expired token is a normal result with valid: false and a reason. replayed: true means this token was checked before. A token is meant to be checked once. From November 15, 2026, a second check returns valid: false.

Verify locally

This example uses jose.

What to check

  1. The token is valid and not expired.
  2. organization_id is your organization.
  3. request_id is the one your page sent with the token.
  4. trusted is true.
The token does not name the account. Your server knows which account it is because your signed-in user sent the token. Bind the result to that session.

When there is no token

GET /verification/status reports the same result by request_id. After the request completes, the response carries trusted and first_time. For a refused or failed request, reason is already_trusted, rebind_requires_prior_id or unavailable.

Webhook events

Set up delivery and signature verification first. See the Webhooks overview.
Check your endpoint’s event filter. An endpoint that is subscribed to specific event types receives only those types. It does not receive account.secured or account.unlinked until you add them. An endpoint with no filter receives every type. Open your endpoint in the Console under Settings, Developer Tools, Webhooks, and add the new types.
account.secured and account.unlinked have no environment field, and the handle is the same in Development and Production. One endpoint receives both environments. If you test with Development keys against the endpoint your production system reads, account events from your tests arrive there too.

account.secured

Sent once when a person secures an account: a new link, not a later pass on the same link.
The event does not carry your platform-user-ref. To connect it to your user, match it to the gate.human_verified event for the same confirmation, which carries the same handle and the request_id your page received, or search the Console registry by your user reference.

account.unlinked

Sent when a link ends. The next confirmation on that account is a first-time confirmation again.

gate.human_verified

Secured Accounts adds three fields to this event.
The other fields are described in Webhook events. (census is the API’s name for BotShield Gate.)

gate.unavailable

A request to secure an account that does not complete ends with this event.
A request produces one terminal event. Use request_id as your idempotency key.

Keep your records current

Do not rely on webhooks alone. A later gate pass on the account reports the current trusted value in its token. See Staying in sync for the whole pattern.