Skip to main content
Short answers to the questions developers ask most. Each one links into the page where the full detail lives.

The product

ShieldLabs is a visitor identification, anonymous-traffic detection, and abuse and fraud prevention platform. One JS snippet collects 100+ browser, device, and network signals. The server returns a persistent identity (a DeviceID and VisitorID) plus an explainable Risk Score (0-100) and the anonymity signals behind it, delivered by webhook and readable through the Server API and a dashboard that also surfaces ready-made patterns.ShieldLabs surfaces the evidence. Your own code owns the decision. We score a request; you decide whether to allow, challenge, review, or block in your application. There is no in-product rules engine and no auto-blocking.
The recurring jobs developers wire it into:
  • Suspicious signups and logins from account farms and multi-accounting (one device behind many accounts, or accounts that keep changing identifiers).
  • Referral, promo, and bonus abuse where one person poses as many users.
  • Anonymous traffic at sensitive moments such as payment or withdrawal, where VPN, proxy, Tor, or anti-detect browsers mask the visitor.
  • Traffic-quality measurement, telling real visitors from anonymous ones across all your traffic and ranking each paid or organic source by its anonymous-traffic share, so you pay for real visitors, not masked clicks.
  • Recognizing trusted returning visitors to personalize their experience or cut friction for a known-good device, the inverse of the fraud checks.
The Cookbook has worked, copy-pasteable flows, and the Patterns tab surfaces the historical correlations on the dashboard.

Keys and setup

Each domain gets one key pair.
  • Public Key goes in the snippet URL as ?publicKey=.... It identifies your domain and is safe to expose in the browser.
  • Secret Key is backend only, for the Management API on api.shieldlabs.ai (profile and balance). The History API on account.shieldlabs.ai uses a separate Private API Key (sec_…) as a Bearer token. Neither belongs in front-end code.
  • Webhook signing secret (whsec_…) is backend only, one per endpoint. You use it to verify the X-Shield-Signature header on incoming webhooks.
Keys are per domain. Do not reuse one domain’s keys on another site. Full details on Public and Secret Keys.
One user check is one request. The snippet creates that check when you call checkAnonymous, checkAuthenticatedUser, or the forceCheck* variants. The Risk Score, its signals, and the webhook for that check are all part of that single request.Reading history later is billed by which surface you use: the History API on account.shieldlabs.ai (Private API Key) is free, while the Management API History path on api.shieldlabs.ai bills one request per returned row (an empty result still bills one). Webhook delivery, dashboard views and exports, and reading your profile and balance are free, as the Billing & Plans breakdown spells out in full.
Work down the path the data takes:
1

Confirm the snippet loads

Open the browser network tab and check that 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.
2

Confirm the function runs

Make sure you actually call an export (checkAnonymous() or checkAuthenticatedUser(hashedUserId)) after the import resolves. Nothing is sent until one of them runs. There is also a Check installation button in the dashboard.
3

Clear CSP and ad-block

A strict Content-Security-Policy or an ad-blocker can silently block the snippet or its network calls. Allow the hosts in CSP setup.
4

Webhook not arriving?

Check your webhook endpoints: each must be HTTPS with a valid certificate and return quickly. If your handler returns a 4xx or 5xx, or your 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.
If checks succeed but you see no scores, jump to the Why is my score 0? question below.

Identifiers and scoring

Each result carries six identifiers that play different roles. The RequestID (webhook: request_id) is the per-call join key (make your webhook handler idempotent on it); the SessionID marks one visit, short-lived; the CookieID is a first-party browser id, lost when cookies or storage are cleared; the DeviceID is the durable, server-derived identity that survives cleared cookies and incognito; the VisitorID is built from the DeviceID plus the CookieID, so it resets when storage is cleared; and UserHID is your own hashed or pseudonymous account id. The visitor’s public IP rides along too, as seen by the server.A result also carries WebRtcHIP, a server-side correlation field for the local-IP entity. Keep it server-side and do not display it. The full model, the durability of each id, and SessionID and CookieID all live on the Identifiers page.
A 0 is usually correct, not a bug. The Risk Score is 0-100, and 0 means no meaningful anonymity signals fired (the Clean band, 0-9). On clean test traffic from a normal browser, a score of 0 is exactly what you should expect.It can also be 0 when a network check is blocked, so fewer signals are available. Only treat a permanent 0 as a problem if you see it on all real production traffic, which points at a setup issue. In that case, work through the installation troubleshooting above.
The bands are a guide; the action per band is yours to configure in your code. There are four: Clean (0-9), Low (10-29), Medium (30-59), and High (60-100), defined in full on the Risk Score page. A common starting point is to pass Clean and Low through, give Medium a step-up challenge or second look, and block, review, or verify on High.Always decide on Score plus signals plus action context, never the number alone. A legitimate user can score high (corporate proxy, VPN, privacy browser), so the guide to acting on the Risk Score walks through tuning thresholds gradually.
The customer-facing Risk Score is capped at 100. A rate-limit-banned request can still surface the internal value 999 on a webhook or History row, so guard your logic with if (score > 100) return; (webhook: data.risk_score). The browser receives an HTTP 429 at the gateway; your backend may see the 999.

Reading your results

No, a webhook is optional. Without a registered endpoint, checks still run and still bill exactly the same. The difference is only how you read the result: with no webhook configured, scores land in the dashboard and stay queryable through the History API, but you get no real-time push.Configure webhook endpoints when you want to act on the score live, for example to challenge a risky login or hold a withdrawal. Add them in the dashboard Webhooks tab (up to 10 per domain).
Both read the same scored requests; they differ in shape.
  • The dashboard is the visual surface: overview, visitors, traffic sources, patterns, and a searchable per-request data table. You search by one identifier type at a time and can export to CSV. Reading and exporting are free.
  • The History API is the programmatic surface: GET https://account.shieldlabs.ai/api/v1/history/{type}/{value}, authenticated with a Bearer Private API Key, returns JSON snapshots, newest first, where type is ip, visitor_id, device_id, user_hid, request_id, session_id, or cookie_id. Account History reads are free (limit caps at 100).
Reach for the dashboard for review and reporting, and the History API when your own code needs the data after the fact.
Partially. It still works, with lower precision.The DeviceID does not depend on cookies: it is derived from the stable browser and device environment, so a returning person keeps the same DeviceID even after clearing cookies or using incognito. That is what lets ShieldLabs recognize returning visitors that cookie-based analytics count as brand new.The VisitorID does lean on storage: it is built from a CookieID, so clearing cookies or storage regenerates the CookieID and produces a new VisitorID. With cookies blocked you lose the VisitorID continuity but keep the durable DeviceID, a boundary the Device Intelligence page draws out.

Performance

The impact is minimal. The snippet is loaded with a dynamic import(), which is non-blocking, and signal collection runs asynchronously after the page is interactive. It does not sit on the critical render path.Scoring also happens on the server, not in the browser: the Risk Score and identifiers are computed server-side (about a second) and pushed to your webhook, so the visitor never waits on it. The browser only collects signals and posts them.

Still stuck?

If a check runs but a result never appears, re-read the snippet setup and webhook setup. The API overview and API models carry the exact request and response shapes, and the Errors page explains each error meaning.