<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:
- Passkey Verification (always on) — cryptographic human proof via device biometrics
- Signal Pixel (opt-in via
signals="true") — passive behavioral fingerprinting and edge scoring
- Third-party Integrations (automatic) — Cloudflare Turnstile, reCAPTCHA, and more when configured in your Integrations settings
On success, the widget fires a callback with a signed token, an opaque signal_token (tamper-proof bot score), and any third-party results.
Installation
Add a single script tag to your page. No npm install, no build step.
<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
<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_...) |
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. |
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 token expires |
onready | string | — | Name of a global function called when the widget loads |
Events
In addition to callback attributes, the element dispatches standard Custom Events:
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:success | { token, signal_token, signal_score, turnstile_token } | Verification complete. token is a signed JWT. signal_token is the tamper-proof bot score reference. |
botshield:failure | { reason } | Verification failed. reason explains why. |
botshield:expired | {} | The verification token has expired. 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:
// 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 for score ranges and signal details.
Third-Party Integrations
BotShield automatically loads and validates third-party bot detection tools when configured in your Integrations settings.
Cloudflare Turnstile
When Turnstile is enabled for your organization:
- The web component loads the Turnstile script automatically
- An invisible Turnstile widget runs in the background
- The Turnstile token is collected and returned in
e.detail.turnstile_token
- BotShield validates the token server-side using your stored secret key
- 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 to request an integration.
Server-Side Validation
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.
1. Verify the Verification Token
The primary check — validate the signed JWT that proves a human completed the passkey challenge:
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,
}),
});
const result = await response.json();
// {
// valid: true,
// claims: {
// botshield_user_id: "uuid-...",
// auth_mode: "private",
// scope: "checkout.complete",
// ...
// }
// }
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:
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
// }
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.
3. Combined Confidence Score
When all signals are present, BotShield combines them into a single confidence score:
// 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 four states:
| State | Appearance | Behavior |
|---|
| idle | Shield icon, “Verify human presence”, “Required” badge | Click to start verification |
| verifying | Spinner, “Verifying…” | Verification in progress |
| verified | Green checkmark, “Human presence verified”, green border | Complete. Token available via callback. |
| failed | Red X, “Verification failed”, red border | Click to reset and retry. |
Themes
White background (#f9f9f9), gray border (#bebfc1), dark text.
Dark background (#0b0b0c), subtle border (#2a2a2a), light text. Blue gradient BotShield logo.
Follows the user’s system preference via prefers-color-scheme.
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.
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:
// 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:
const el = document.querySelector('botshield-verify');
el.verify();
el.getToken();
el.getScore();
el.reset();
Framework Examples
React:
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:
<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)
<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
<botshield-verify site-key="pk_..." scan-mode="redirect" />
Opens a full-page verification screen in a new tab.
When placed inside a <form>, the widget automatically injects a hidden input with the verification token:
<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>
Shopify (Cart Page)
<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
<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:
<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