Troubleshooting
A fast path from a symptom to its fix. Each entry names the likely cause and the one change that resolves it, then points you to the page with the full detail. For the meaning of a specific status code, the Errors reference is the canonical table; for short answers to common questions, start with the FAQ.Install and data flow
The snippet does not load, or no data appears
The snippet does not load, or no data appears
Symptom. Nothing reaches your dashboard or webhook, and the browser never posts a snapshot.Cause. One of three things is blocking the snippet before it can run:
- A Content Security Policy is refusing the hosts the snippet needs. The module and its dependency load under
script-src, and the snapshot and network checks post underconnect-src. A policy missing those hosts stops the snippet cold. - An ad blocker or content blocker is dropping the CDN host, so the module never downloads.
- The page is not served over HTTPS. The snippet collects signals in a secure context only, so it does not run on plain
http://or a non-secure origin.
Refused to load or Refused to connect error naming the directive and host, which is your signal to add that host. The exact directives and host list live on the CSP setup page, and the snippet install guide shows the HTML and framework methods. Confirm the page loads over HTTPS, then load it with any blockers disabled to rule the extension in or out. Loading the module is not enough on its own: you must also call checkAnonymous() or checkAuthenticatedUser(hashedId).No webhook arrives
No webhook arrives
Symptom. The check runs and bills, the score lands in the dashboard, but your endpoint never receives the POST.Cause. Either no callback URL is set for the domain, or the delivery was dropped. Webhook delivery is at-most-once with no retries and a short timeout, so a slow, down, or non-2xx endpoint silently loses that delivery, and there is no resend.Fix. Confirm a callback URL is set for the domain, from the dashboard or by posting it to the Server API
/callback route, as the webhook setup covers. Make your handler return 200 fast, then do slow work asynchronously so you stay inside the timeout. Because a single delivery can always be lost, treat the History API as the guaranteed read: look the result up by request_id whenever it must not be missed.Signature verification fails on a valid webhook
Signature verification fails on a valid webhook
Symptom. The payload looks correct, but your HMAC check rejects it.Cause. You are hashing a re-serialized copy of the JSON. Parsing the body and re-encoding the
Data object reorders keys or changes spacing, so the bytes you hash no longer match the bytes that were signed.Fix. Compute the HMAC over the raw Data bytes exactly as received, not over a parsed-then-re-encoded object, and not over the whole envelope. Keep the raw body around, slice out the Data object bytes, and compare with a constant-time check. The webhook setup page has working Node, Go, and Python examples that do this correctly.Reading the result
The DeviceID comes back all zeros
The DeviceID comes back all zeros
Symptom. A result carries
DeviceID of 00000000-0000-0000-0000-000000000000, and that visit scores 90.Cause. No stable device characteristics reached the server, so no identity could be built. This happens when the snippet was blocked or JavaScript was disabled. The visit still scores, and a visit with nothing to identify scores 90.Fix. Route a null or all-zero DeviceID to review rather than auto-allowing it. You cannot recognize a returning person from an identity that was never collected, so treat the absence as a signal in its own right. The handling and the all-zero case are described on the Identification page.A legitimate user scores high
A legitimate user scores high
Symptom. A real customer lands in the Medium or High band with no wrongdoing.Cause. A corporate VPN, a proxy, or a privacy-focused browser raises the Risk Score on its own. The signals are real, but they describe the connection, not the person’s intent.Fix. Decide on the Score plus its
Details plus the action context, never the number alone. A withdrawal warrants a stricter line than a page view, and the Details tell you which signals fired so you can weigh them. Working from the four bands, the guide to acting on the Risk Score walks through tuning thresholds gradually so legitimate VPN users are not punished.Reading an entity's recent or first and last activity
Reading an entity's recent or first and last activity
Symptom. You want the history for a device, visitor, account, or IP, or its earliest and latest sighting.Cause. There is no dedicated first-seen field on a result. History is a list of point-in-time snapshots, and each one carries
LastRequestTime, the moment that snapshot was recorded.Fix. Read the History API by device_id, visitor_id, user_hid, or ip. Rows come back newest first, so the first row is the most recent activity and the last row in a full result is the earliest you have stored. The LastRequestTime on any row is when that visit was seen. Each returned row bills 1 request, so set limit to the smallest value that answers your question.Status codes and limits
HTTP 402 on ingest or the Server API
HTTP 402 on ingest or the Server API
Symptom. The snippet’s snapshot post, or a Server API call, returns
402.Cause. Your request balance is exhausted. A 402 appears in two places when you run out: when the snippet posts an identification to be scored, and on the Server API. The History part of the Server API bills one request per returned row, so a wide search can drain a low balance quickly.Fix. Top up your request balance or upgrade your plan, as the Billing page lays out. A 402 is a billing state and is unrelated to rate limiting.HTTP 429, or a Score of 999
HTTP 429, or a Score of 999
Symptom. The gateway returns The Risk Score is always 0 to 100. Read
429, or a webhook or History row arrives with Score of 999.Cause. Both come from the per-IP rate limit, a gateway protection rather than a verdict. The browser receives the 429, and a rate-limit-banned request can also surface a 999 marker on a webhook delivery or a History row. That 999 is not capped to 100, so it arrives as-is.Fix. Guard for it at the very top of your handler so it never reaches your decision logic:999 as “this IP was rate-limited,” and spread traffic across client IPs rather than proxying through one server. The thresholds and the ban window are on the rate limits page.Checking whether the service is up
Checking whether the service is up
Symptom. You want a liveness probe for monitoring or a load balancer health check.Cause. You need a lightweight endpoint that confirms a gateway is serving, without spending a request or running a scoring path.Fix. Each gateway exposes a
GET /health endpoint that returns 200 with { "status": "ok" }. Point your uptime monitor or orchestrator liveness probe at it. It does not authenticate and does not bill.