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

# Web Component

> Reference for the <botshield-verify> web component — active human verification via passkey, with optional Signal Pixel and third-party integrations

# `<botshield-verify>` Web Component

The `<botshield-verify>` element is BotShield's client-side orchestration layer. It renders a verification widget on the merchant's page and coordinates up to three layers of defense:

1. **Passkey Verification** (always on) — cryptographic human proof via device biometrics
2. **Signal Pixel** (opt-in via `signals="true"`) — passive behavioral fingerprinting and edge scoring
3. **Third-party Integrations** (automatic) — Cloudflare Turnstile, reCAPTCHA, and more when configured in your [dashboard Integrations settings](https://console.botshield.ai)

On success, the widget fires a callback with a **PII-free** `verification_token`, an opaque `signal_token` (tamper-proof bot score), and any third-party results.

<Info>
  **Anonymous by construction.** The widget never collects or returns identity. The `verification_token` claims are only `{ request_id, verified, organization_id, timestamp, nonce }` — no email, no user id, no device data. The widget resolves to one of three result states: **Human Verified**, **MultiPass Active**, or **Human Unavailable**.
</Info>

## Installation

Add a single script tag to your page. No npm install, no build step.

```html theme={null}
<script src="https://cdn.botshield.ai/sdk.js"></script>
```

The script registers the `<botshield-verify>` custom element globally. It uses a closed Shadow DOM so it never conflicts with your page styles.

## Quick Start

```html theme={null}
<botshield-verify
  site-key="pk_live_YOUR_SITE_KEY"
  theme="auto"
  signals="true"
  onsuccess="handleVerified"
  onfailure="handleFailed"
></botshield-verify>

<script>
  function handleVerified({ token, signal_token, signal_score, turnstile_token }) {
    // Send ALL signals to your backend for server-side validation
    fetch('/api/verify-checkout', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        botshield_token: token,         // Signed verification JWT
        signal_token: signal_token,     // Tamper-proof bot score (validate server-side)
        // signal_score is display-only — always use signal_token for security
      }),
    });
  }

  function handleFailed({ reason }) {
    console.error('Verification failed:', reason);
  }
</script>
```

## Attributes

| Attribute           | Type                        | Default                              | Description                                                                                                                                                                                                                |
| ------------------- | --------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `site-key`          | string                      | *required*                           | Your public site key (`pk_live_...` or `pk_test_...`)                                                                                                                                                                      |
| `scope`             | string                      | —                                    | The Census scope this verification is bound to (e.g. `checkout.complete`). Required to enable the **MultiPass** pre-check fast path — when set, a MultiPass-Active user resolves instantly without a QR or Face ID prompt. |
| `theme`             | `light` \| `dark` \| `auto` | `auto`                               | Visual theme. `auto` follows the user's `prefers-color-scheme`.                                                                                                                                                            |
| `signals`           | `true` \| `false`           | `false`                              | Enable **BotShield Signal Pixel** — collects device and behavioral signals silently via an invisible 1x1 embed. Returns a tamper-proof `signal_token` alongside the bot score.                                             |
| `scan-mode`         | `modal` \| `redirect`       | `modal`                              | How the QR scan UI is presented when a desktop user needs to verify on their phone. `modal` shows the QR code as an overlay. `redirect` opens a full-page verification screen.                                             |
| `platform`          | string                      | —                                    | An opaque label for the surface initiating verification (e.g. `shopify`). Threaded through for attribution only — never identity.                                                                                          |
| `platform-user-ref` | string                      | —                                    | An opaque partner reference (your own user id or a hashed equivalent) used to resolve a MultiPass linkage. Never identity — no email, no name. Hashed with a server secret before storage.                                 |
| `link-on-verify`    | `true` \| `false`           | `true`                               | Whether a successful verification writes a MultiPass linkage tying `platform-user-ref` to the user's BotShield credential. Set `false` to opt out.                                                                         |
| `challenge-url`     | string                      | `https://cdn.botshield.ai/challenge` | Override the challenge page URL (for self-hosted deployments).                                                                                                                                                             |
| `onsuccess`         | string                      | —                                    | Name of a global function called on success with `{ token, signal_token, signal_score, turnstile_token }`                                                                                                                  |
| `onfailure`         | string                      | —                                    | Name of a global function called on failure with `{ reason }`                                                                                                                                                              |
| `onexpired`         | string                      | —                                    | Name of a global function called when the verification expires                                                                                                                                                             |
| `onready`           | string                      | —                                    | Name of a global function called when the widget loads                                                                                                                                                                     |

<Note>
  The `token` passed to `onsuccess` is the **PII-free** `verification_token`. On a MultiPass fast-path success it may be `null` (no challenge token is minted) — listen for `botshield:multipass-status` if you need to distinguish the MultiPass Active result. There is no identity in any callback payload.
</Note>

## Events

In addition to callback attributes, the element dispatches standard Custom Events:

```javascript theme={null}
const el = document.querySelector('botshield-verify');

el.addEventListener('botshield:success', (e) => {
  const { token, signal_token, signal_score, turnstile_token } = e.detail;
  submitToServer(token, signal_token);
});

el.addEventListener('botshield:failure', (e) => {
  console.error('Failed:', e.detail.reason);
});
```

| Event                        | `e.detail`                                               | Description                                                                                                                                            |
| ---------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `botshield:ready`            | `{}`                                                     | Widget has loaded and is ready for interaction                                                                                                         |
| `botshield:multipass-status` | `{ event_id, verdict, reason, result_state }`            | Fires after the MultiPass pre-check (when `scope` is set). `result_state` ∈ `human_verified` \| `multipass_active` \| `not_verified` \| `unavailable`. |
| `botshield:success`          | `{ token, signal_token, signal_score, turnstile_token }` | Resolved positive (Human Verified or MultiPass Active). `token` is the PII-free `verification_token` (may be `null` on a MultiPass fast-path pass).    |
| `botshield:failure`          | `{ reason }`                                             | Resolved Human Unavailable. `reason` is an opaque code — the underlying cause is never exposed across the boundary.                                    |
| `botshield:expired`          | `{}`                                                     | The verification expired before completion. Widget resets to idle.                                                                                     |
| `botshield:reset`            | `{}`                                                     | Widget was reset to idle state.                                                                                                                        |

## Signal Pixel

When `signals="true"`, an invisible 1x1 iframe collects behavioral fingerprints alongside the verification widget:

**Edge signals** (server-side, can't be spoofed):

* Datacenter ASN detection
* TLS fingerprint analysis
* HTTP protocol version
* IP velocity tracking
* Header anomaly detection

**Behavioral signals** (client-side):

* WebDriver / automation flags
* Canvas fingerprint
* WebGL renderer
* Mouse / touch events
* Screen & hardware profile

The combined score (0–100) is returned in the `signal_score` field. However, this client-side score can be spoofed via DevTools. For tamper-proof validation, use the `signal_token`:

```javascript theme={null}
// Client-side score (display only — can be spoofed)
console.log('Bot score:', e.detail.signal_score); // 13

// Tamper-proof token (validate server-side — can't be spoofed)
console.log('Signal token:', e.detail.signal_token); // "bs_sig_a1b2c3..."
```

See the [Signal Pixel reference](/embed/signal-pixel) for score ranges and signal details.

## Third-Party Integrations

BotShield automatically loads and validates third-party bot detection tools when configured in your [dashboard Integrations settings](https://console.botshield.ai).

### Cloudflare Turnstile

When Turnstile is enabled for your organization:

1. The web component loads the Turnstile script automatically
2. An invisible Turnstile widget runs in the background
3. The Turnstile token is collected and returned in `e.detail.turnstile_token`
4. BotShield validates the token server-side using your stored secret key
5. The pass/fail result is included in your verification confidence score

**No code changes required** — just enable Turnstile in your dashboard and add your keys.

**Recommended Turnstile settings:**

* **Widget Mode:** Invisible — BotShield handles all UI
* **Pre-clearance:** Yes — let verified visitors bypass lower CF challenges
* **Pre-clearance Level:** Interactive (high) — BotShield provides the real biometric proof

### Adding More Integrations

reCAPTCHA, DataDome, HUMAN, and more are coming. Each integration follows the same pattern: configure in your dashboard, BotShield auto-loads and validates. [Contact us](mailto:partners@botshield.ai) to request an integration.

## Server-Side Validation

<Warning>
  Never trust client-side state alone. The widget appearance, `signal_score`, and even the `token` must be validated on your server. BotShield provides three levels of server-side validation.
</Warning>

### 1. Verify the Verification Token

The primary check — validate the signed, PII-free `verification_token` that proves a human completed the challenge. No shared secret is needed: BotShield holds the signing key, so the token itself is the proof.

```javascript theme={null}
const response = await fetch('https://api.botshield.ai/operations/sdk/verify-token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    token: requestBody.botshield_token,
  }),
});

const { data } = await response.json();
// {
//   valid: true,
//   claims: {
//     request_id: "req_...",        // your join key
//     verified: true,
//     organization_id: "org_...",
//     timestamp: "2026-06-16T12:00:00Z",
//     nonce: "..."                  // anti-replay
//     // anonymous by construction — NO email, user id, or device data
//   }
// }

if (!data.valid || !data.claims.verified) {
  // Human Unavailable — apply your own policy
}
```

### 2. Validate the Signal Token (tamper-proof bot score)

If you're using the Signal Pixel (`signals="true"`), validate the `signal_token` to get the real server-side bot score:

```javascript theme={null}
const response = await fetch('https://api.botshield.ai/v1/validate-signal', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk_live_YOUR_SECRET_KEY',
  },
  body: JSON.stringify({
    signal_token: requestBody.signal_token,
  }),
});

const result = await response.json();
// {
//   valid: true,
//   score: 13,              // Server-side score (can't be spoofed)
//   edge_score: 10,         // Network-layer signals
//   client_score: 16,       // Behavioral fingerprint
//   country: "US",
//   fp_hash: "a1b2c3..."   // Fingerprint for correlation
// }
```

<Note>
  Signal tokens are one-time use and expire after 10 minutes. The `signal_score` in the client event is for display only — always use `signal_token` for security decisions.
</Note>

### 3. Combined Confidence Score

When all signals are present, BotShield combines them into a single confidence score:

```javascript theme={null}
// Full server-side validation with all signals
const response = await fetch('https://api.botshield.ai/v1/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk_live_YOUR_SECRET_KEY',
  },
  body: JSON.stringify({
    token: requestBody.botshield_token,
    signal_token: requestBody.signal_token,
  }),
});

const result = await response.json();
// {
//   valid: true,
//   confidence: 0.97,
//   signals: {
//     botshield_score: 13,
//     turnstile: { success: true },
//     passkey: { verified: true }
//   }
// }

if (result.valid && result.confidence >= 0.5) {
  // Human verified — proceed with checkout
} else {
  // Suspected bot — block or require additional verification
}
```

## Visual States

The widget transitions through five states, mapping to the three Census result states:

| State                 | Result state      | Appearance                                                 | Behavior                                |
| --------------------- | ----------------- | ---------------------------------------------------------- | --------------------------------------- |
| **idle**              | —                 | Robot icon, "Verify human with BotShield"                  | Click to start verification             |
| **verifying**         | —                 | Spinner, "Verifying…"                                      | Verification in progress                |
| **verified**          | Human Verified    | Green checkmark, "Presence Live", green border             | Resolved. Token available via callback. |
| **multipass\_active** | MultiPass Active  | Green card icon, "MultiPass Active", outlined green border | Resolved via the MultiPass fast path.   |
| **failed**            | Human Unavailable | Red X, "Presence Unavailable", red border                  | Click to reset and retry.               |

<Note>
  The visual state is derived client-side from the anonymous result — it is not a transmitted field. The cause of a **Human Unavailable** result (failed presence vs. infrastructure) is never distinguished at the partner boundary.
</Note>

### Themes

<Tabs>
  <Tab title="Light">
    White background (`#f9f9f9`), gray border (`#bebfc1`), dark text.
  </Tab>

  <Tab title="Dark">
    Dark background (`#0b0b0c`), subtle border (`#2a2a2a`), light text. Blue gradient BotShield logo.
  </Tab>

  <Tab title="Auto">
    Follows the user's system preference via `prefers-color-scheme`.
  </Tab>
</Tabs>

## JavaScript API

### `BotShield.render()` — Programmatic Rendering

Create and mount a verification widget from JavaScript. Works with SPAs (React, Vue, Angular) and anywhere you need programmatic control.

```javascript theme={null}
const widget = BotShield.render('#checkout-container', {
  siteKey: 'pk_live_YOUR_SITE_KEY',
  signals: true,
  theme: 'dark',
  scanMode: 'modal',
  onSuccess: ({ token, signal_token, signal_score }) => {
    // Send to your backend
    submitToServer(token, signal_token);
  },
  onFailure: ({ reason }) => {
    console.error('Verification failed:', reason);
  },
  onReady: () => {
    console.log('Widget loaded');
  },
});
```

**Options:**

| Option         | Type                              | Description                                                          |
| -------------- | --------------------------------- | -------------------------------------------------------------------- |
| `siteKey`      | string                            | *required* — Your public site key                                    |
| `theme`        | `'light'` \| `'dark'` \| `'auto'` | Visual theme (default: `'auto'`)                                     |
| `signals`      | boolean                           | Enable Signal Pixel (default: `false`)                               |
| `scanMode`     | `'modal'` \| `'redirect'`         | QR scan presentation (default: `'modal'`)                            |
| `challengeUrl` | string                            | Override challenge URL                                               |
| `onSuccess`    | function                          | Called with `{ token, signal_token, signal_score, turnstile_token }` |
| `onFailure`    | function                          | Called with `{ reason }`                                             |
| `onExpired`    | function                          | Called when token expires                                            |
| `onReady`      | function                          | Called when widget loads                                             |

**Returns a widget handle:**

```javascript theme={null}
// Trigger verification programmatically
widget.verify();

// Get the token after verification
const token = widget.getToken();     // string | null

// Get the Signal Pixel score
const score = widget.getScore();     // number | null

// Reset to idle state
widget.reset();

// Remove widget from DOM
widget.destroy();

// Access the underlying element
widget.element;  // <botshield-verify> HTMLElement
```

### Direct Element Access

If you prefer HTML attributes, you can also query the element directly:

```javascript theme={null}
const el = document.querySelector('botshield-verify');
el.verify();
el.getToken();
el.getScore();
el.reset();
```

### Framework Examples

**React:**

```jsx theme={null}
import { useEffect, useRef } from 'react';

function CheckoutVerification() {
  const containerRef = useRef(null);
  const widgetRef = useRef(null);

  useEffect(() => {
    widgetRef.current = BotShield.render(containerRef.current, {
      siteKey: 'pk_live_YOUR_KEY',
      signals: true,
      onSuccess: ({ token, signal_token }) => {
        fetch('/api/checkout', {
          method: 'POST',
          body: JSON.stringify({ token, signal_token }),
        });
      },
    });
    return () => widgetRef.current?.destroy();
  }, []);

  return <div ref={containerRef} />;
}
```

**Vue 3:**

```vue theme={null}
<template>
  <div ref="container" />
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const container = ref(null);
let widget = null;

onMounted(() => {
  widget = BotShield.render(container.value, {
    siteKey: 'pk_live_YOUR_KEY',
    signals: true,
    onSuccess: ({ token, signal_token }) => {
      submitCheckout(token, signal_token);
    },
  });
});

onUnmounted(() => widget?.destroy());
</script>
```

## QR Scan Mode

When a desktop user needs to verify on their phone (cross-device flow), the `scan-mode` attribute controls the presentation:

### Modal (default)

```html theme={null}
<botshield-verify site-key="pk_..." scan-mode="modal" />
```

Shows a QR code overlay on the current page with:

* BotShield logo and step-by-step instructions
* QR code for phone scanning
* "Waiting for verification..." status with auto-close on completion
* Cancel button

### Redirect

```html theme={null}
<botshield-verify site-key="pk_..." scan-mode="redirect" />
```

Opens a full-page verification screen in a new tab.

## Form Integration

When placed inside a `<form>`, the widget automatically injects a hidden input with the verification token:

```html theme={null}
<form action="/checkout" method="POST">
  <botshield-verify
    site-key="pk_live_YOUR_SITE_KEY"
    signals="true"
    onsuccess="enableSubmit"
  ></botshield-verify>

  <!-- These hidden inputs are auto-created on verification success -->
  <!-- <input type="hidden" name="botshield_token" value="eyJhbGc..."> -->

  <button type="submit" id="checkout-btn" disabled>
    Proceed to Checkout
  </button>
</form>

<script>
  function enableSubmit() {
    document.getElementById('checkout-btn').disabled = false;
  }
</script>
```

## Platform Examples

### Shopify (Cart Page)

```html theme={null}
<script src="https://cdn.botshield.ai/sdk.js"></script>

<botshield-verify
  site-key="pk_live_YOUR_KEY"
  theme="auto"
  signals="true"
  onsuccess="enableCheckout"
></botshield-verify>

<script>
  function enableCheckout({ token, signal_token }) {
    sessionStorage.setItem('botshield_token', token);
    sessionStorage.setItem('botshield_signal', signal_token);
    document.querySelector('[name=checkout]').disabled = false;
  }
</script>
```

### WooCommerce

```html theme={null}
<script src="https://cdn.botshield.ai/sdk.js"></script>

<botshield-verify
  site-key="pk_live_YOUR_KEY"
  theme="light"
  signals="true"
  onsuccess="onVerified"
></botshield-verify>

<script>
  function onVerified({ token, signal_token }) {
    const form = document.querySelector('form.checkout');

    const tokenInput = document.createElement('input');
    tokenInput.type = 'hidden';
    tokenInput.name = 'botshield_token';
    tokenInput.value = token;
    form.appendChild(tokenInput);

    const signalInput = document.createElement('input');
    signalInput.type = 'hidden';
    signalInput.name = 'botshield_signal_token';
    signalInput.value = signal_token;
    form.appendChild(signalInput);

    document.getElementById('place_order').disabled = false;
  }
</script>
```

### Escalation Pattern (with existing bot detection)

Use BotShield alongside your existing bot detection tool. Only show the verification widget when your existing tool is uncertain:

```html theme={null}
<script src="https://cdn.botshield.ai/sdk.js"></script>

<botshield-verify
  id="bs-verify"
  site-key="pk_live_YOUR_KEY"
  signals="true"
  style="display: none;"
></botshield-verify>

<script>
  // Your existing bot detection (Turnstile, DataDome, etc.)
  const riskScore = await getExternalBotScore();

  if (riskScore < 0.3) {
    // Clearly human — let through without BotShield
    proceedToCheckout();
  } else if (riskScore > 0.9) {
    // Clearly bot — block
    showBlockedMessage();
  } else {
    // Gray zone — escalate to BotShield
    document.getElementById('bs-verify').style.display = 'block';
  }
</script>
```

## Next Steps

* [Signal Pixel Reference](/embed/signal-pixel) — Passive bot scoring details, score ranges, signal sources
* [Integrations](https://console.botshield.ai) — Configure Cloudflare Turnstile and other providers
* [Site Keys](/embed/overview#site-keys) — Create and manage keys in the dashboard
* [Server SDK](/sdk/client-libraries) — Backend token validation libraries
