signals arrive on your server by webhook and are readable from the Server API. What happens next, allow / challenge / review / block, is logic you write in your own application. There is no in-product rules engine and nothing blocks on its own.
This page is the recommendation toolkit: a default band-to-action ladder, per-scenario thresholds you can copy, how to drive decisions off the score band and the action context, the honest caveats, and the implementation notes that keep your decisions correct under at-most-once webhook delivery.
Treat everything here as a starting point, not a rule. The 0 to 100 scale is fixed. Where you draw the action line is yours, and the right line depends on how costly a wrong allow or a wrong block is for the action in front of you.
The principle: Score plus signals plus action context
The number alone is never the decision. A Risk Score of 65 on a blog comment and a 65 on a $5,000 withdrawal are the same number and completely different situations. Make every decision from three inputs:
The Risk Score is explainable: every webhook ships with a
signals array of { "name": "<name>", "weight": <int> } entries, so you can see and log the reasons behind a number instead of acting on a black box. (The History API exposes the same breakdown.) Drive the decision off the Risk Score band, the numeric weight of each signal, and which anonymity signals fired (read the stable detection_flags, not the free-form signal label string, which is not a stable contract). Read the Risk Score for how the score is built and its weights, and Anonymity Signals for what each signal means.
The default band-to-action ladder
ShieldLabs maps every score into one of four bands. These labels are fixed; the recommended action per band is a sensible default you should adapt per scenario below.Default ladder (Node.js)
The bands come straight from the score.
Clean 0–9, Low 10–29, Medium 30–59, High 60–100. They are the only band labels ShieldLabs uses. 999 is a rate-limit ban marker, not a 0–100 score, but it can still arrive in the score field (risk_score on the webhook, score on the History API), so guard the value > 100; see the Implementation notes below.Hard rules on specific signals
The band folds every signal into one number, but sometimes you want to act on a specific tell no matter the score. Branch on the stabledetection_flags booleans for that, not the free-form signal labels. The object ships on every webhook:
Flag-level override
Per-scenario recommended thresholds
The cost of a mistake changes with the action, so the threshold should too. Be lenient where a wrong block annoys a real user but costs little (a blog comment), and strict where a wrong allow moves money or grants trust (a withdrawal, or KYC — Know Your Customer identity verification). The tables below are recommended starting points; calibrate them against your own data as described in rule 2 below.Signup
The most common entry point for multi-accounting, promo and bonus abuse, and account farms. You usually have nothing else to go on yet, so the score and signals carry the decision. Be generous in the Clean and Low bands so you do not tax real users, and reserve hard friction for clear High-band anonymity.Signup decision
Login and 2FA
At login you already have an account and its history, so you can be a little more permissive on the raw score and lean harder on step-up authentication (an extra verification step) you already own. A Medium score is a strong reason to require a second factor; reserve a block for High-band signals or an account-takeover pattern.Login decision
Checkout and payment
Money is moving, so the threshold drops. Anonymity signals on a payment (proxy, Tor, a VPN that does not match the saved billing region) deserve a hard look earlier than they would at signup. Add friction in the Medium band and gate the High band behind verification.Withdrawal and high-value action
The strictest scenario. A wrong allow here is an irreversible loss, so react to signals you would wave through elsewhere. Add verification as early as the Low band, and route the High band to a human.Withdrawal decision (strictest)
KYC gating
Use the score to decide who must complete identity verification before they get a sensitive capability, not to make the identity decision itself. A Medium or High score is a strong reason to require full KYC up front rather than letting the user defer it.Content, comment, and posting
The most lenient scenario. A wrong block costs you a real contributor; a wrong allow costs you a spam comment you can remove later. Keep friction low and only react meaningfully in the High band.Driving decisions off the Risk Score band
The Risk Score is the decision input. It already encodes signal severity: a Tor exit carries a far higher weight than a lone VPN, several overlapping mismatches push a visit into the High band, and a stripped or non-cooperating client lands near the top. So a band-based action ladder, tuned per action context, captures the intent of “react harder to stronger anonymity” without you having to inspect each anonymity signal by hand. Two visits with the same total can still differ, and the lever for that is action context plus your own data, layered on top of the band:- Raise the stakes, lower the threshold. On money-movement (checkout, withdrawal) react in the Low band already; on a low-stakes action you can wave a Medium-band visit through. The per-scenario tables above set those cut-points as a recommendation.
- A high score on a sensitive step is a hard look. A Medium or High score at a payment, withdrawal, or password reset warrants a step-up or a hold, because the score is already telling you the visit looks masked or spoofed.
- Cross-reference your own data. A low Risk Score plus a DeviceID that matches a known-bad device in your database is still a block. You get the identity and the signals; your own history is the other half of the decision.
Use the
signals array to see which anonymity signals fired and the points each added — for the decision, for logging, and for later review. Drive the decision off the Risk Score band, each signal’s numeric weight, and which signals fired (branch on the stable detection_flags, not the free-form signal label string, which is not a stable contract). What each signal means is documented in Anonymity Signals, and the weights in Risk Scoring.Honest caveats (read before you tune)
Three rules that keep you out of trouble:- Match the response to the band and the action context. A 30 is a Medium-band visit: on a low-stakes action it deserves no friction, while the same 30 at a password reset or a withdrawal is worth a challenge. Let the band plus the stakes of the action set the response, and use the
signalsto inform it and to log the reasoning. - Tune thresholds gradually, starting in log-only mode. Ship the integration first with no enforcement: record
scoreandsignalsfor every check and watch how your real traffic distributes in the analytics dashboard against your conversion and chargeback data. Only then turn on friction, starting with the highest-stakes actions, and tighten in small steps. - Match friction to stakes. It is fine to wave a Medium-band visit through on a low-stakes action and to challenge a Low-band visit on a withdrawal. The per-scenario tables above exist precisely so the same Risk Score earns different friction.
ShieldLabs is the detection layer. It surfaces the identity and the signals; your application owns the verdict. The subject of every “block”, “challenge”, and “allow” is your code, not ShieldLabs.
Implementation notes: getting it right
The recommendations above only hold if your handler reads the data correctly. At-most-once webhook delivery means a naive handler can miss a result entirely, and applying the same check twice (a webhook plus a History API fallback) can double-apply effects. Wire these in from the start.Receive via webhook, and verify it
Your decision logic lives in the webhook handler. Verify theX-Shield-Signature header before you trust the payload, then respond 200 fast and run your decision off the request path. The signature recipe, constant-time comparison, and the full Node, Go, and Python handlers are on Webhooks: this page assumes a verified payload and focuses on what you do with it.
Verify first, then act (Node.js / Express)
Be idempotent on request_id
ShieldLabs sends one webhook per scored identification: the server waits up to ~60 seconds for any follow-up network check, then sends the final score once. Still key your apply logic onrequest_id. For anything you cannot afford to miss you may also read the same result from the History API as a fallback, so making the write idempotent on request_id ensures the webhook and a History read for the same check converge instead of double-applying a business effect.
Idempotent apply
Fall back to the History API
Webhook delivery is at-most-once: a single attempt, no retries, with a roughly 1 second timeout. Do not assume at-least-once. For any decision you must not miss (a withdrawal, a payout, a KYC gate), read the result back from the Server API History endpoint instead of relying solely on the webhook.Read the result back by RequestID
{ data, total } envelope with snapshots, newest first. You can also look up recent activity by device_id, visitor_id, ip, user_hid, request_id, session_id, or cookie_id. History reads through account.shieldlabs.ai do not consume request balance. The Server API has the full field list and search types.
A complete decision handler
Putting the pieces together: verify, cross-reference your own data, drive the verdict off the per-action band ladder, log thesignals for review, and stay idempotent.
Full handler sketch (Node.js)
Where to go next
The Risk Score
How the 0 to 100 score is built, what
signals contains, and the band definitions.Signals
Every signal you can branch on, in plain language, with weights and combination rules.
Webhooks
At-most-once delivery, the full payload, signature verification, and idempotency.
Cookbook
End-to-end recipes for login and 2FA, checkout, signup, affiliate fraud, and traffic quality.