> ## 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.

# Build with AI

> Copy-paste prompts that get ShieldLabs working fast in ChatGPT, Claude, or Cursor, plus how to feed the whole docs to your AI tool as context.

Most of a ShieldLabs integration is glue code: load the snippet, verify a webhook, turn a score into a decision. The prompts below are written so you can paste one into your AI assistant, fill in the placeholders, and get working code back. Each one already carries the product facts the model needs, so it does not guess.

<Note>
  **Two ways to give your AI tool the full context.**

  * Every page in these docs has a menu in the top-right to **Copy page**, **View as Markdown**, or open it directly in **ChatGPT** or **Claude** with the page preloaded.
  * The entire documentation set is published as a single file at [`/llms-full.txt`](https://docs.shieldlabs.ai/llms-full.txt) (with a short index at [`/llms.txt`](https://docs.shieldlabs.ai/llms.txt)). Paste either URL into your assistant to load all of ShieldLabs as background before you ask.
</Note>

## Install the snippet

Replace the placeholders, then paste into ChatGPT, Claude, or Cursor.

```text theme={null}
You are helping me integrate ShieldLabs visitor identification into my web app.

Stack: <your framework, e.g. Next.js App Router, React, Vue, or plain HTML>.
Public key: <YOUR_PUBLIC_KEY>.

How ShieldLabs loads:
- It is a browser ES module loaded from a CDN. It is NOT an npm package and not a native SDK.
- Load and run it like this:
    const mod = await import('https://cdn.shieldlabs.ai/snippet.js?publicKey=YOUR_PUBLIC_KEY');
    mod.checkAnonymous();
- It runs in the browser, posts signals to ShieldLabs automatically, requests no
  permissions, and must not block page load (use a dynamic import, run it async).
- For a signed-in user, call mod.checkAuthenticatedUser('<hashed-account-id>') instead,
  passing a hash of my user id, never the raw id.

Write the integration for my stack, show exactly where the code goes, and load it on
the pages I want to identify visitors on.
```

## Verify a webhook

```text theme={null}
Write a <Node + Express, Go, or Python + Flask> webhook handler for ShieldLabs.

Delivery format: ShieldLabs POSTs flat snake_case JSON shaped like
  {
    "request_id": "<uuid>",
    "risk_score": <0-100>,
        "signals": [ { "name": "<name>", "weight": <points> } ],
    "observed_at": "2026-06-16T10:00:00Z",
    ...
  }
  There is no envelope: no event_type, no nested data wrapper.

Verification:
- The signature is in the X-Shield-Signature header: sha256=<hex>.
- Compute HMAC-SHA256 over the raw request body bytes, keyed with my endpoint
  secret SHIELDLABS_WEBHOOK_SECRET=<whsec_...>. Do not re-serialize JSON.
- Constant-time compare. Reject with 401 on mismatch.

Reliability:
- Delivery is at-most-once with no retries and a ~1 second timeout.
- Make the handler idempotent on request_id and return 200 quickly.

Give me the full handler with signature verification and an idempotency guard.
```

## Turn the Risk Score into a decision

```text theme={null}
I receive a ShieldLabs Risk Score per visit and want to turn it into an action.

Facts:
- Score is 0-100. Bands: Clean 0-9, Low 10-29, Medium 30-59, High 60-100.
- Webhook signals is an array of { name, weight }: the signals that built the
  score and the points each one added. History snapshots use { Value, Description }.
- Branch on the Risk Score band, each signal's weight, and which signals fired (the stable detection_flags booleans), NOT on the exact signal label text (it can change).
- ShieldLabs surfaces the score and signals; my code owns the decision: allow, challenge (step-up
  or 2FA), send to manual review, or block.
- A legitimate user can score high (corporate VPN, privacy browser), so weigh the score
  against how sensitive the action is.

Write a function decide(score, details, actionSensitivity) that returns one of
allow | challenge | review | block, with sensible thresholds I can tune per action.
```

## Read a visitor's history

```text theme={null}
Write a <language> function that reads a visitor's history from the ShieldLabs History API.

Base URL: https://account.shieldlabs.ai/api
- GET /v1/history/{search_type}/{value}?limit=N returns { data: [...], total }.
- Each row in data is snake_case: request_id, visitor_id, device_id, score, score_details, country, created_at, …
- Auth: Authorization: Bearer <PRIVATE_API_KEY> (sec_… from the dashboard API tab). Keep the key server-side only.
- History reads do not consume request balance.

Give me the function plus a short example that fetches the last few snapshots for one
visitor_id and prints how its score changed over time.
```

## Keep the model honest

When you paste generated code back, sanity-check it against the real product:

<CardGroup cols={2}>
  <Card title="Snippet" icon="code" href="/setup/snippet">
    The real exports, framework examples, and what the browser collects.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/setup/webhooks">
    The snake\_case event payload, `X-Shield-Signature` verification, and handlers in Node, Go, and Python.
  </Card>

  <Card title="Risk Scoring" icon="gauge" href="/features/risk-scoring">
    The 0-100 score, its bands, and the `signals` breakdown to branch on.
  </Card>

  <Card title="Server API" icon="server" href="/api/server-api">
    History and profile endpoints with full request and response shapes.
  </Card>
</CardGroup>

<Tip>
  If a model invents an endpoint, an npm package, or a `Description` value to switch on, it
  is guessing. Re-prompt it with the page above (Copy page, then paste) and it will correct.
</Tip>
