request_id ties the three steps together.
You normally do not call
rest.shieldlabs.ai yourself. The JS snippet posts to it automatically. Your server-side work is to receive the webhook and, when you need a guaranteed read or the account behind an identification, query the History API.The three steps
1
Snippet posts signals (browser → rest.shieldlabs.ai)
Load the current snippet from the official CDN. It generates a per-call request ID (a client UUID) and posts collected signals to
rest.shieldlabs.ai. The response is an acknowledgment (the client IP as a JSON string), not the Risk Score.2
Server scores asynchronously (about 300 ms)
The server computes the Risk Score. The webhook carries the breakdown in
data.signals as { name, weight } (stable slugs, for example antidetect_browser). History API rows carry the same breakdown in score_details, a JSON string of { Value, Description } entries.3
The result is delivered (webhook and History API)
The server pushes one final webhook per identification (typically about 300 ms after the check; at most about 10 seconds when follow-up network checks run). You can also read the result any time from the History API by
request_id, and every identification of the account by user_hid.Step 1: The identification POST (acknowledgment, not a Risk Score)
The JS snippet calls ingest for you. Load it fromhttps://cdn.shieldlabs.ai. Do not self-host, mirror, bundle or pin copies of the snippet.
{requestID}is a client-generated UUID, unique per identification. It is the join key across the snippet call, the webhook and History.publicKeyis your per-domain Public Key. It is safe to expose in the browser.
200). It confirms the signals were received and the identification counted. The Visitor ID, Device ID and Risk Score are computed on the server and delivered in Step 3.
In the browser, the snippet surfaces the requestID through optional onInitialized, so you can correlate client and server records. The ingest HTTP body stays a receipt and is not copied onto that object.
checkAnonymous and checkAuthenticatedUser run at most one identification every five minutes for the same user. A call inside that window posts nothing, counts nothing, and its onInitialized handler receives { status: "not_initialized" }. On a multi-page site, a full page load in the only open tab can start a new visit with its own identification. forceCheckAnonymous and forceCheckAuthenticatedUser run an identification every time, keep the current Session ID and restart the five-minute window. The snippet install guide has the full method list and framework examples.
Step 2: Why scoring is asynchronous
Scoring runs on the server after ingest acknowledges the identification and typically takes about 300 ms. When follow-up network checks run, ShieldLabs waits for them for at most about 10 seconds, then delivers one final webhook per identification.Step 3: Receiving the result
You get the result two ways. Use both: the webhook for low latency, the History API as the guaranteed read.Webhook (push)
The serverPOSTs the result to each enabled webhook endpoint once per identification, joined by request_id:
The webhook body is a signed snake_case envelope. The signature travels in the
X-Shield-Signature header. The example below is shortened; every field is in WebhookEvent.
Verify
X-Shield-Signature on the raw request body, keyed with that endpoint’s whsec_… signing secret (not the domain Secret Key). See the verification recipe.request_id and use the History API for anything that must not be missed. Full payload and delivery guarantees are in Webhooks; copy-paste verification handlers in Node, Go and Python are in the webhook setup guide.
History API (read)
You can read the scored result for any request ID from the History API. This is the authoritative, pull-based path and the right choice when you cannot risk a dropped webhook.{ data, total } envelope of identifications, newest first. Search by user_hid to read an account’s identifications, or by device_id, visitor_id, ip, request_id, session_id or cookie_id. History reads never use your included identifications. The full schema is in the Server API reference.
The request ID lifecycle
The request ID is the single value that lets you stitch the asynchronous pieces together:Snippet call
Minted in the browser as a UUID when the snippet runs. Returned to your page in the
onInitialized callback.Webhook
Echoed back as
request_id. One scored POST per identification and endpoint.History
Queryable as the
request_id search type to read the stored identification any time.requestID early, record risk_score and signals idempotently when the webhook arrives, and fall back to GET /api/v1/history/request_id/{requestID} on account.shieldlabs.ai if it has not arrived within about 10 seconds. The full reliability pattern, with signature verification, is in Webhooks.
From the identification to the account
Each identification carries the keys of the identities it belongs to:user_hid (your account), device_id, visitor_id, public_ip and local_ip. Pass a hashed User HID with checkAuthenticatedUser on every signed-in page. Users, account-level risk and all four High-Risk Events are built on it.
To see an account as a whole, read its identifications from the History API by user_hid. The worst band across them is the account’s risk, and the Device IDs, Visitor IDs and public IPs in those rows are what the account is linked to. Devices, visitors and public IPs read the same way by device_id, visitor_id and ip. High-Risk Events (Multi-accounting, Account sharing, Impossible travel and Account takeover) are detected on your users, each at Medium or High confidence, and are available in the analytics dashboard, the API and webhooks.
The same account, with its band, its High-Risk Events and each linked device, visitor and IP with the band of the identifications it shares with the account, is on the user’s card in the analytics dashboard (User, device, visitor and IP cards).
What you do with the result
The Risk Score lands in the 0 to 100 range (999 marks a rate-limited identification) and falls into three Risk Score bands: Trusted 0-29, Suspicious 30-59, Dangerous 60-100. The payload carries the number, so map it to a band in your backend. At signup, login, checkout or a payout, read the Risk Score of this identification together with the account behind it. The Risk Score and risk signals of this identification remain the input at that moment. When a High-Risk Event arrives for a user, or when you review it in the analytics dashboard, act on the account. Read a high Risk Score together with its named risk signals (signals on the webhook, score_details on the History API) and the user’s history. A real customer on a corporate VPN can reach the Suspicious band; the signals show why, and you choose the action for each case: allow, step up, review or block. The per-band playbook gives starting policies and worked examples.
Next steps
Webhooks
Full payload,
X-Shield-Signature verification, and at-most-once delivery.Server API
History search, the account read, and the Management API profile.
Users, devices, visitors and IPs
The five identities each identification links to, and the risk each one carries.
Identifiers
User HID, Device ID, Visitor ID, and the per-call request ID, Session ID and Cookie ID.