POST to your server when a gate request or an Agents Ask proposal reaches an outcome. You add endpoints in the BotShield Console, verify each delivery with the svix library, deduplicate, and answer with a 2xx quickly. Webhooks carry results only. No personal data about the human is included.
How a delivery works
Add an endpoint
1
Open the Webhooks tab
In the BotShield Console, go to Settings → Developer Tools → Webhooks. The tab embeds a webhooks portal for your organization. Use Open in new tab if you want more room.
2
Add your endpoint URL
Add an endpoint and enter a public
https:// URL on your server, for example https://api.meridianairlines.example/webhooks/botshield.3
Choose event types
Subscribe the endpoint to the event types you need. An endpoint with no filter receives every event type, including types added later. An endpoint with a filter receives only the types you selected, so add a new type to the filter when you want it. See Webhook events for the list.
4
Copy the signing secret
Open the endpoint and copy its signing secret (it starts with
whsec_). Store it as a server-side secret. Each endpoint has its own secret.5
Send an example event
Use the portal’s testing view to send an example of any event type to your endpoint, then check the result in the endpoint’s delivery log.
Webhook endpoints belong to your organization, not to one environment. An endpoint receives events from both your Development and Production gates and agents. Every gate event carries a top-level
environment field, "development" or "production". Branch on it before you act, so a test verification never changes production data. Agents Ask events have no environment field. Match their request_id against the records you created.Verify the signature
Every delivery carries three headers:
Verify with the
svix library and your endpoint’s signing secret. Verify against the raw request body. If a framework parses the JSON and you re-serialize it, the bytes change and verification fails.
Respond fast
Return any2xx as soon as the signature checks out and the event is recorded. Do slow work (database writes beyond the dedupe record, calls to other services, email) after you respond. A response that is not 2xx, including a 3xx redirect, or that is slow to arrive counts as a failed attempt. Aim to respond within a few seconds.
Retries
A failed attempt is retried automatically, several times, with increasing delay between attempts. Retries continue for hours, not minutes, so a short outage on your side does not lose events. Every attempt, with its response code and body, is listed under the endpoint in the portal. After the last attempt the message is marked failed for that endpoint, and you can replay it from the portal at any time. An endpoint that keeps failing for days can be disabled automatically. Re-enable it in the portal once it is healthy, then replay what it missed.Deduplicate
Retries and manual replays mean your endpoint can receive the same event more than once. Make processing idempotent by recording a key per event before you act on it.
The
svix-id header also works as a transport-level key: it is identical on every retry of one message.
Do not depend on webhooks alone
Webhook delivery is best-effort relative to the verification itself. BotShield never delays or fails a verification because a webhook could not be queued, so a webhook can be late or, rarely, missing. BotShield sends a gate event when something happens to a request on the phone:- The human confirms.
gate.human_verifiedis sent. - The BotShield app reports a failure, for example the person cancels the biometric prompt, or a request that was opened fails.
gate.unavailableis sent withfailed_atand areason. Webhook events lists the values. - The person opens the request after its five-minute window has passed.
gate.unavailableis sent withexpired_at.
- Recent Presence fast path. When a returning human passes instantly and the widget reports
via: "precheck", nogate.human_verifiedwebhook is sent for that pass. Confirm it from your server right away withGET /verification/status, which reportsstatus: "pass"for 60 seconds after the check. - Requests nobody opens. A request that the person never opens on their phone expires with no event. Closing the widget’s modal without scanning the QR code, or ignoring the push, ends this way.
gate.unavailable.
For any flow where the outcome matters (checkout, account changes, agent actions), also check the result from your server:
- BotShield Gate: call
GET /verification/statuswith therequest_id. See Verify on your server. - Agents Ask: long-poll
GET /agentlink/check-statuswith yourrequest_id. See Propose an action.
gate.human_verified, and no completed or pass status by its expires_at, as Unavailable.
Next steps
Webhook events
Payloads and field tables for every event type.
Verify on your server
Confirm a gate result without waiting for a webhook.
Proof of Resolution
Verify the signed JWT carried by resolution events.
Keys and environments
Where your keys live in the Console.
