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.
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 withPOST /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.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.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.
- Failure
- Expiry
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 withPOST /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.
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.
