Skip to main content
A webhook pushes the Risk Score and the risk signals behind it to your backend the moment ShieldLabs finishes scoring an identification. Register one or more endpoints per domain, verify the signature on the X-Shield-Signature header, and choose the action for each case in your backend. This page is the how-to. The Webhooks API reference has the exact field-by-field payload schema.
Webhooks are optional. If you do not register an endpoint, identifications still run and still count against your plan, and their results are in the analytics dashboard and the History API. An endpoint is what makes results real-time.

Register an endpoint

You can configure up to 10 endpoints per domain. Each endpoint has its own name, HTTPS URL, and a unique signing secret (whsec_…). Manage them in the analytics dashboard under Integration > Webhooks.
Integration > Webhooks for example.com in the analytics dashboard: 2 endpoints (limit 10), each Delivering with a masked whsec_ signing secret and its last delivery 2m ago; row action icons Pause, Edit, Test, Verify, Rotate secret and Delete; the Test result Delivered HTTP 200 in 184 ms; and the Verify a signature sample for Node.js.Integration > Webhooks for example.com in the analytics dashboard in the dark theme: 2 endpoints (limit 10), each Delivering with a masked whsec_ signing secret and its last delivery 2m ago; row action icons Pause, Edit, Test, Verify, Rotate secret and Delete; the Test result Delivered HTTP 200 in 184 ms; and the Verify a signature sample for Node.js.

Integration > Webhooks in the analytics dashboard: each endpoint has its own signing secret, Verify and Test.

1

Open Integration

In the analytics dashboard, open Integration > Webhooks and select your domain. Integration describes the screen.
2

Add an endpoint

Click Add endpoint, give it a name and a public HTTPS URL on a route your backend controls, and save. ShieldLabs generates a dedicated signing secret (whsec_…) for that endpoint.
3

Verify it

Use Verify to send a signed webhook.ping. When your endpoint answers with a 2xx, its status changes from Waiting for the first event to Delivering. Use Test on the endpoint to send a sample identification.scored delivery at any time; the row shows the result, for example “Delivered HTTP 200 in 84 ms”. The test sample always carries the same request_id and a null user_hid, so a handler that deduplicates on request_id processes it once.
Each endpoint signs with its own whsec_… secret. Copy the secret from the endpoint row and store it as a backend-only environment variable, never in the browser or in a URL. Rotating a secret invalidates the old one immediately.
Pause an endpoint and resume it later without losing its configuration; while it is Paused it receives no deliveries. Each row shows the endpoint’s status and its Last delivery. Rotate secret issues a new whsec_… for one endpoint and invalidates the old one.

What a delivery looks like

ShieldLabs sends a POST with Content-Type: application/json to each enabled endpoint. The body is a signed envelope (snake_case), and the signature travels in the X-Shield-Signature header.
Correlate the delivery to the original identification by data.request_id. The full field list is in the Webhooks API reference. This identification is masked: data.public_ip is a US proxy exit (203.0.113.42) while data.local_ip is the address the browser itself reports, in Germany (198.51.100.23). The two addresses differ, so data.detection_flags.ip_mismatch is true. ShieldLabs returns both IPs so you can compare them; compare the two countries rather than branching on the flag. The difference is informational and adds nothing to the Risk Score, which here comes from the proxy, datacenter and abuser signals.
Branch on data.risk_score, signal name slugs and detection_flags. A slug can appear more than once with a partial weight, so never assume one entry per slug.

Tie deliveries to your users

Each identification.scored delivery is one identification, the event layer under your users. Store data.user_hid (your hashed User HID, or "anonymous" for a checkAnonymous call), data.device_id, data.visitor_id and the IPs in data.public_ip and data.local_ip with your own records, so you can act on the user and not only on this identification. The History API returns every identification of one user, device, visitor or IP by user_hid, device_id, visitor_id or ip. High-Risk Events are available in the analytics dashboard, the API and webhooks; when one arrives for a user, act on the account.

One webhook per identification

Each identification produces exactly one scored webhook per enabled endpoint, joined by request_id. ShieldLabs waits for follow-up network checks when they apply to the identification. The webhook is sent as soon as those checks finish, or at most about 10 seconds after the check, whichever comes first. If a follow-up never arrives, you still get one webhook with the best Risk Score available at that point. Typical timing:
  • Identifications without a follow-up network check: about 300 ms after the check.
  • Chrome with a follow-up network check: usually within a few seconds, at most about 10 seconds.
Do not hold your UX waiting for the webhook. The snippet’s onInitialized handler receives { status, requestID } as soon as the check starts: use the requestID as the join key, and treat the webhook (or the History API) as the scored result for server-side decisions.

Verify the signature

Every delivery is signed. The X-Shield-Signature header is the hex-encoded HMAC-SHA256 of the raw request body, keyed with that endpoint’s signing secret, prefixed with sha256=:
To verify: read the raw request body bytes exactly as received, compute HMAC-SHA256 over those bytes with the endpoint secret, hex-encode the result, prefix it with sha256=, and compare it to the X-Shield-Signature header using a constant-time comparison. Reject the request (respond 401) on a mismatch, and do not process the body. Under Integration > Webhooks in the analytics dashboard, Verify a signature has the same check ready to copy in six languages.
HMAC the raw request body bytes as received. Re-serializing the parsed JSON can reorder keys or change spacing, which changes the bytes you hash, so the signature will not match.

Delivery guarantees

Webhook delivery is at-most-once.
There are no retries and the send has a ~1 second timeout. If your endpoint is slow, down, or returns a non-2xx response, that delivery is dropped and not resent. Do not rely on webhooks for guaranteed delivery: for guaranteed reads, use the History API. It returns one identification by request_id, or every identification of one user, device, visitor or IP by user_hid, device_id, visitor_id or ip (also by session_id and cookie_id).
A practical handler pattern:
1

Verify

Constant-time compare the X-Shield-Signature header. Reject with 401 on a mismatch.
2

Acknowledge

Return 200 immediately once the signature is valid and the body is parsed.
3

Deduplicate

Upsert on request_id. A redelivery for the same id should be a no-op.
4

Act

Store the identification against data.user_hid and data.device_id, then choose the action for each case (allow, step up, review or block) from data.risk_score and data.signals.

Next steps

Webhooks API reference

Field-by-field payload schema, timing, and signature details.

Acting on results

Choose the action for each band: allow, step up, review or block.

History API

Read one identification, or every identification of one user, for guaranteed reads.

API keys

Where your Public Key, Private API Key and Secret Key come from.