> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shieldlabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# FAQ

> Answers to common ShieldLabs questions: API keys, request billing, the six identifiers, webhooks, and what a Risk Score of 0 means.

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

## The product

<AccordionGroup>
  <Accordion title="What is ShieldLabs?" icon="shield">
    ShieldLabs is a visitor identification, anonymous-traffic detection, and abuse and fraud prevention platform. One JS [snippet](/setup/snippet) collects 100+ browser, device, and network signals. The server returns a persistent identity (a [DeviceID and VisitorID](/features/identification)) plus an explainable [Risk Score (0-100)](/features/risk-scoring) and the [anonymity signals](/features/anonymity-signals) behind it, delivered by [webhook](/setup/webhooks) and readable through the [Server API](/api/server-api) and a [dashboard](/features/traffic-analytics) that also surfaces ready-made [patterns](/features/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.
  </Accordion>

  <Accordion title="What problems does it solve?" icon="circle-question">
    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](/use-case) has worked, copy-pasteable flows, and the [Patterns](/features/patterns) tab surfaces the historical correlations on the dashboard.
  </Accordion>
</AccordionGroup>

## Keys and setup

<AccordionGroup>
  <Accordion title="What is the difference between the Public Key and the Secret Key?" icon="key">
    Each domain gets one key pair.

    * **Public Key** goes in the [snippet](/setup/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](/api/server-api) on `api.shieldlabs.ai` (profile and balance). The [History API](/api/server-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](/setup/webhooks) on incoming webhooks.

    Keys are per domain. Do not reuse one domain's keys on another site. Full details on [Public and Secret Keys](/setup/keys).
  </Accordion>

  <Accordion title="What counts as a request?" icon="hashtag">
    One user check is one request. The snippet creates that check when you call `checkAnonymous`, `checkAuthenticatedUser`, or the `forceCheck*` variants. The [Risk Score](/features/risk-scoring), its `signals`, and the [webhook](/setup/webhooks) for that check are all part of that single request.

    Reading history later is billed by which surface you use: the [History API](/api/server-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](/billing) breakdown spells out in full.
  </Accordion>

  <Accordion title="My installation is not working. How do I troubleshoot?" icon="wrench">
    Work down the path the data takes:

    <Steps>
      <Step title="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.
      </Step>

      <Step title="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.
      </Step>

      <Step title="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](/setup/csp).
      </Step>

      <Step title="Webhook not arriving?">
        Check your [webhook endpoints](/setup/webhooks): 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.
      </Step>
    </Steps>

    If checks succeed but you see no scores, jump to the **Why is my score 0?** question below.
  </Accordion>
</AccordionGroup>

## Identifiers and scoring

<AccordionGroup>
  <Accordion title="What are the identifiers (RequestID, SessionID, CookieID, DeviceID, VisitorID, UserHID)?" icon="fingerprint">
    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](/features/identification) page.
  </Accordion>

  <Accordion title="Why is my Risk Score 0?" icon="gauge">
    A 0 is usually correct, not a bug. The [Risk Score](/features/risk-scoring) 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.
  </Accordion>

  <Accordion title="What do the score bands mean and where do I draw the line?" icon="sliders">
    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](/features/risk-scoring) 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](/guides/acting-on-risk-score) walks through tuning thresholds gradually.

    <Note>
      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](/rate-limits); your backend may see the 999.
    </Note>
  </Accordion>
</AccordionGroup>

## Reading your results

<AccordionGroup>
  <Accordion title="Do I need a webhook?" icon="webhook">
    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](/features/traffic-analytics) and stay queryable through the [History API](/api/server-api), but you get no real-time push.

    Configure [webhook endpoints](/setup/webhooks) 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).
  </Accordion>

  <Accordion title="Dashboard versus History API: which do I use?" icon="table-columns">
    Both read the same scored requests; they differ in shape.

    * The **[dashboard](/features/traffic-analytics)** 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](/api/server-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.
  </Accordion>

  <Accordion title="Does ShieldLabs work without cookies?" icon="cookie-bite">
    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](/features/identification) page draws out.
  </Accordion>
</AccordionGroup>

## Performance

<AccordionGroup>
  <Accordion title="Does the snippet slow down my page?" icon="bolt">
    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](/features/risk-scoring) and identifiers are computed server-side (about a second) and pushed to your [webhook](/setup/webhooks), so the visitor never waits on it. The browser only collects signals and posts them.
  </Accordion>
</AccordionGroup>

## Still stuck?

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