The product
What is ShieldLabs?
What is ShieldLabs?
What problems does it solve?
What problems does it solve?
- Accounts run by one person: multi-accounting and account farms, plus referral, promo and bonus abuse where one person poses as many users.
- Accounts used by someone else: account sharing, impossible travel and account takeover on your signed-in users.
- Risky users at sensitive moments such as signup, login, payment or withdrawal, when a VPN, proxy, Tor or an anti-detect browser masks who is behind the account.
- Traffic quality by source: the share of risky identifications per paid or organic channel, campaign and referrer, so you pay for real users.
- Trusted returning users and devices, to cut friction for a known-good device.
Keys and setup
Which key goes where?
Which key goes where?
- Public Key goes in the snippet URL as
?publicKey=.... It identifies your domain and is safe to expose in the browser. - Private API Key (
sec_…) is backend only, sent as a Bearer token to the History API onaccount.shieldlabs.ai. - Secret Key is backend only, for the Management API on
api.shieldlabs.ai, which reads your profile and the remaining included volume on your account. - Webhook signing secret (
whsec_…) is backend only, one per endpoint. Use it to verify theX-Shield-Signatureheader on incoming webhooks.
What counts as an identification?
What counts as an identification?
checkAnonymous, checkAuthenticatedUser or a forceCheck* variant. The Risk Score, its risk signals and the webhook for that check all belong to that one identification.Within one visit (while a page of your site stays open in the browser, across route changes in a single-page app and across open tabs), 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. The forceCheck* variants run an identification every time.Reading results later through the History API is free, and so are webhook delivery, the analytics dashboard and its exports, and reading your profile. When your account reaches its included volume, the identification request returns HTTP 402 until the billing cycle resets or you change plan. On Free, the 5,000 identifications are one time. Billing & Plans has the full breakdown.My installation is not working. How do I troubleshoot?
My installation is not working. How do I troubleshoot?
Confirm the snippet loads
cdn.shieldlabs.ai/snippet.js?publicKey=... loads with a 200, with the correct Public Key for this domain. A wrong or missing key, or a typo in the import URL, stops everything.Confirm the function runs
checkAnonymous() or checkAuthenticatedUser(hashedUserId)) after the import resolves. Nothing is sent until one of them runs. Inside one visit (another tab of your site still open, or route changes in a single-page app) a repeat call within five minutes posts nothing, so use forceCheckAnonymous() while you test. In the analytics dashboard, Integration > Install shows Pending with Recheck until the first identification arrives, then says identifications are arriving from your domain (during onboarding, the Verify step has Check installation).

Integration > Install in the analytics dashboard: pick your stack, open the snippet for anonymous visitors or authenticated users, and check that identifications are arriving.
Clear CSP and ad-block
Webhook not arriving?
X-Shield-Signature check rejects the payload, you will see no usable result. There are no retries, so verify the HMAC over the raw request body with the endpoint’s whsec_… secret.Identifiers and scoring
What are users, devices, visitors and IPs?
What are users, devices, visitors and IPs?
- User (User HID): your account, the hashed or pseudonymous id you pass with
checkAuthenticatedUser. Users, account-level risk and all four High-Risk Events are built on it. Anonymous checks send"anonymous"(webhook:user_hid). - Device (Device ID): the durable device, computed on the server. It holds through cleared cookies, incognito mode and IP changes; another browser is another Device ID (webhook:
device_id). - Visitor (Visitor ID): one device plus one cookie, so it changes when cookies or storage are cleared (webhook:
visitor_id). - Public IP and Local IP: the public address with its country, and the address the browser itself reports, which can differ behind a VPN or proxy (webhook:
public_ip,local_ip).
request_id), the join key your webhook handler is idempotent on; the Session ID (session_id) for one browsing session; and the Cookie ID (cookie_id), the first-party browser id. Users, devices, visitors and IPs covers the model, and the Identifiers page covers how each one is built and how long it lasts.Why is my Risk Score 0?
Why is my Risk Score 0?
search_bot) and sets its Risk Score to 0. Treat a permanent 0 as a problem only if you see it on all real production traffic, which points at a setup issue; in that case, work through the installation troubleshooting above.What do the Risk Score bands mean and where do I draw the line?
What do the Risk Score bands mean and where do I draw the line?
999, the rate-limit marker written while a visitor IP is banned; it can appear on a webhook (data.risk_score) or a History row (score). Treat any value above 100 as that marker and route the action it belongs to to review, before your Risk Score logic runs: if (data.risk_score > 100) return routeToReview(data.request_id);. The browser receives an HTTP 429 at the gateway.Why do I see no users or High-Risk Events?
Why do I see no users or High-Risk Events?
checkAuthenticatedUser on every signed-in page. Identifications from checkAnonymous carry "anonymous" and link to devices, visitors and IPs only.Once your signed-in pages send a User HID, ShieldLabs builds each user with its band and its linked devices, visitors and IPs, and detects the four High-Risk Events on your users. By default, Multi-accounting fires from 3 accounts on one visitor (one device plus one cookie) and Account sharing from 4 devices on one account; both thresholds are configurable. See snippet setup.Reading your results
Do I need a webhook?
Do I need a webhook?
Analytics dashboard or History API: which do I use?
Analytics dashboard or History API: which do I use?
- The analytics dashboard is where you investigate at the account level: every user, device, visitor and public IP has a card with its band and its linked devices, visitors, accounts and IPs, and a user’s card also shows its High-Risk Events, next to Overview, Analytics and an Export of up to 10,000 identifications to CSV. Viewing and exporting are free.
- The History API reads the event layer from code:
GET https://account.shieldlabs.ai/api/v1/history/{type}/{value}with a Bearer Private API Key returns identification rows as JSON, newest first, wheretypeisip,visitor_id,device_id,user_hid,request_id,session_idorcookie_id. To read everything one account did, useuser_hid/{value}and page withlimit(up to 100) andoffset. Reads are free.
Performance
Does the snippet slow down my page?
Does the snippet slow down my page?
import(), which is non-blocking, and signal collection runs asynchronously after the page is interactive. It stays off the critical render path.Scoring happens on the server: the Risk Score and identifiers are computed in about 300 ms and pushed to your webhook, at most about 10 seconds after the check when follow-up network checks run, so the user never waits on it. The browser only collects signals and posts them.