Skip to main content
BotShield sends six event types today: two for BotShield Gate and four for Agents Ask. A seventh, account.unlinked, arrives with BotShield 3.0 for Trusted Accounts. Every payload is a JSON object with a type field that names the event. Payloads never identify the human. To connect an event to your own records, use request_id and the metadata you supplied when you created the request. Set up delivery and signature verification first in the Webhooks overview.
Payloads can gain fields over time. Ignore fields you do not recognize.
Check your endpoint’s event filter. An endpoint that is subscribed to specific event types receives only those types. It does not receive a new type, or a type that replaced a retired one, until you add it. The portal keeps showing a retired type on an endpoint that was subscribed to it, but BotShield no longer sends that type. An endpoint with no filter receives every type. Open the endpoint under Settings → Developer Tools → Webhooks and select the current types.

Retired event names

These names are no longer sent. If your code or your endpoint filter still holds one, replace it.

BotShield Gate events

Gate events refer to a verification request created with POST /sdk/create-verification-link. The request_id is the one that call returned (req_ followed by 32 hex characters).

Fields every gate event shares

About metadata. The object holds the metadata you passed to POST /sdk/create-session and to POST /sdk/create-verification-link, and nothing else. When both of your calls set the same key, the create-verification-link value is used. When you pass no metadata, the payload has no metadata key.
Reserved keys. BotShield keeps its own request context under these names, so a key of yours with one of these names is dropped from the webhook: auth_mode, auth_source, consumer_api_token_id, link_on_verify, webhook_url, return_url, environment, sdk_type, scope, scope_id, parent_request_id, gate_type, age_threshold, age_verdict, age_source, account_classes. With 3.0 these names are reserved as well: notarize, notarize_fallback_reason, trusted, first_time, failure_code. Pick other names, such as booking_ref or surface in the examples below.
When a gate event is sent. A request produces at most one gate.human_verified, sent when the human completes the confirmation on the phone. gate.unavailable is sent when the BotShield app reports a failure (for example the person cancels the biometric prompt), when a request that was opened fails, and when the person opens a request after it has expired. A request the person never opens sends no event, and neither does a Recent Presence pass that the widget reports with via: "precheck". Treat “no verified result by expires_at” as Unavailable, and poll GET /verification/status when you need the outcome of a request nobody opened. See Do not depend on webhooks alone.

gate.human_verified

Sent when the human completes the confirmation in the BotShield app for this request.
See Token and webhooks for how trusted and first_time relate to Trusted Accounts.
On an Age Gate, gate.human_verified tells you a human completed the confirmation. It does not tell you the age threshold was met, and the payload does not carry the age result. The event is sent even when the age result is unavailable. Read age_verdict from GET /verification/status, or the age_over claim in the attestation token, before you unlock anything. See Age Gate.

gate.unavailable

Sent when a request that reached the phone ends without a verified result. There are two shapes. Tell them apart by which timestamp is present: failed_at or expired_at. Both are terminal for that request_id. To try again, create a new request.
Sent when the BotShield app reports that the confirmation did not succeed, for example because the person cancelled the biometric prompt, and when a request that was opened fails on BotShield’s side. reason says which.
reason values: Use reason to choose what you tell the user and what you log. user_denied is a person’s choice, so offer another try without comment. device_lock_required calls for a line of help about turning on a screen lock. platform_declined and internal_error call for a retry or your alternative path. Every gate.unavailable is terminal for its request_id, so keep the action blocked in all four cases. Handle a value you do not recognize as internal_error.

Trusted Accounts events

An endpoint created before September 28, 2026 with a filter of specific event types must add account.unlinked to receive it. Endpoints that receive all event types need no change.

account.unlinked

Sent when a Trusted Account binding ends, because the person unlinked in the BotShield app or your organization revoked in the Console.
The payload has no request_id, no event_id and no environment field. It never carries your platform-user-ref, an email address or a BotShield ID. See Token and webhooks for what each by and reason pair means.

Agents Ask events

Agents Ask events refer to a proposal your agent created with POST /agentlink/inquire. Here request_id is the UUID your agent supplied as its idempotency key, so you can match events to your own records without storing anything BotShield generated. Agents Ask payloads do not echo partner metadata. On resolution events, metadata holds the BotShield context fields listed below.

agents_ask.card.proposed

Sent when BotShield accepts a proposal and sends it to the human. It is informational. The outcome arrives later as one of the agents_ask.resolution.* events.

agents_ask.resolution.confirmed

Sent when the human confirms the action with their device biometric. It carries the Proof of Resolution, a signed JWT. Verify proof_token before your agent acts. See Proof of Resolution.

agents_ask.resolution.denied

Sent when the human denies the action. A denial is also a signed outcome, so the payload has the same shape as a confirmation, with outcome set to denied and a proof_token whose verdict claim reads denied. Stand the agent down.
The fields match agents_ask.resolution.confirmed, except type is agents_ask.resolution.denied and outcome is denied.

agents_ask.resolution.expired

Sent when the proposal reaches its ttl_at before the human acts. No Proof of Resolution is issued. Treat it as “no answer in time”, not as a denial, and stand the agent down. To ask again, send a new proposal with a new request_id.
A proposal your agent cancels with POST /agentlink/cancel sends no webhook. Your agent already knows the outcome from the cancel response.

Next steps

Webhooks overview

Endpoints, signature verification, retries, and deduplication.

Result states

What Verified and Unavailable mean for your product.

Proof of Resolution

Verify proof_token against the BotShield JWKS.

Trusted Accounts

The trust fields and the unlink event that arrive with 3.0.