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

# API Reference

> Install the BotShield SDK and start verifying human presence in minutes

# API Reference

BotShield provides two SDKs for integrating human presence verification:

* **Frontend SDK** — Drop-in web component for your pages (`cdn.botshield.ai/sdk.js`)
* **Backend SDK** — TypeScript/Node.js library for server-side validation (`botshield-sdk` on npm)

<Info>
  Get your API keys from the [Partner Dashboard](https://console.botshield.ai) → Settings → API & Credentials.
</Info>

## Install

<Tabs>
  <Tab title="Frontend (CDN)">
    ```html theme={null}
    <script src="https://cdn.botshield.ai/sdk.js"></script>
    ```

    Then render the widget:

    ```javascript theme={null}
    const widget = BotShield.render('#container', {
      siteKey: 'pk_live_YOUR_SITE_KEY',
      signals: true,
      onSuccess: ({ token, signal_token }) => {
        // Send to your backend for validation
        fetch('/api/verify', {
          method: 'POST',
          body: JSON.stringify({ token, signal_token }),
        });
      },
    });
    ```

    Or use the HTML element directly:

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

  <Tab title="Backend (npm)">
    ```bash theme={null}
    npm install botshield-sdk
    ```

    ```typescript theme={null}
    import BotShield from 'botshield-sdk';

    const client = new BotShield({
      apiKey: process.env.BOTSHIELD_API_KEY, // bs_prod_... or bs_test_...
    });

    // Validate a verification token
    const result = await client.sdk.verifyToken({
      token: req.body.botshield_token,
    });

    if (result.data.valid) {
      // Anonymous attestation — claims are { request_id, verified,
      // organization_id, timestamp, nonce } only. No email, no auth mode,
      // no user ID. Combine with the verdict + reason from /signal/check
      // or /signal/evaluate to drive your enforcement policy.
    }
    ```
  </Tab>
</Tabs>

## The Primary Output: Three Result States

Census's partner-facing output is **three result states**:

| Result state          | Meaning                                                                                                                         |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **Human Verified**    | A fresh Face ID / passkey presence event completed for this request.                                                            |
| **MultiPass Active**  | The user passed on credential continuity (an active passkey + presence consent within a valid TTL) — no fresh biometric needed. |
| **Human Unavailable** | Verification could not complete (expired, declined, or the user could not satisfy the check).                                   |

These are what you build against. In the webhook each maps to its own event: Human Verified → `census.human_verified`, MultiPass Active → `census.multipass_active`, Human Unavailable → `census.human_unavailable` (covers both denial/error and TTL expiry) — see [Webhooks](/concepts/webhook-payloads).

### Internal verdict + reason (fast-path only)

Under the hood, BotShield computes a `verdict` + `reason` pair that projects to those three states. This pair is **not** the partner contract and is **never** in the webhook. It is surfaced only on the embed component's `botshield:multipass-status` event, for clients that want to drive the synchronous fast path without waiting for a fresh challenge:

| Field     | Values                       | Meaning                                                                |
| --------- | ---------------------------- | ---------------------------------------------------------------------- |
| `verdict` | `pass`                       | No further verification needed — proceed with the gated action         |
| `verdict` | `require_presence`           | Run the full Face ID / passkey flow                                    |
| `reason`  | `multipass_active`           | Pass on credential continuity (passkey + presence consent + valid TTL) |
| `reason`  | `presence_fresh`             | Pass on a fresh Face ID event (within the 5-minute lease window)       |
| `reason`  | `multipass_stale`            | Standard scope, MultiPass freshness lapsed — verify again              |
| `reason`  | `elevated_requires_presence` | Elevated scope always demands live proof, even if MultiPass is active  |
| `reason`  | `no_resolution`              | No prior presence established for this request — run the full flow     |

BotShield has no opinion on which actions require which scope — that is your policy decision. See [Human Presence](/concepts/human-presence) for the full reason enum and the standard/elevated scope canon.

## Authentication

All backend SDK calls use your API key in the `Authorization` header:

```
Authorization: Bearer bs_prod_your_key_here
```

| Key Type       | Prefix     | Purpose                           |
| -------------- | ---------- | --------------------------------- |
| **Production** | `bs_prod_` | Live verification with real users |
| **Test**       | `bs_test_` | Development and testing           |

## SDK Methods

### Core Verification

<CardGroup cols={2}>
  <Card title="Create Session" icon="key" href="/api-reference/endpoint/post-sdkcreate-session">
    Get a session token to start creating verification requests.
  </Card>

  <Card title="Create Verification Link" icon="link" href="/api-reference/endpoint/post-sdkcreate-verification-link">
    Generate a verification request with deep link, web URL, and QR code.
  </Card>

  <Card title="Verify Token" icon="shield-check" href="/api-reference/endpoint/post-sdkverify-token">
    Validate a verification receipt. Returns anonymous claims: `request_id`, `verified`, `organization_id`, `timestamp`, `nonce`.
  </Card>

  <Card title="Check Status" icon="circle-check" href="/api-reference/endpoint/post-verificationstatus">
    Poll verification status. Returns signed token when complete.
  </Card>
</CardGroup>

### Signal Pixel

<CardGroup cols={2}>
  <Card title="Store Signal" icon="radar" href="/api-reference/endpoint/post-sdkstore-signal">
    Store a Signal Pixel bot score server-side. Returns a tamper-proof signal token.
  </Card>

  <Card title="Validate Signal" icon="lock" href="/api-reference/endpoint/post-sdkvalidate-signal">
    Validate a signal token to get the real server-side bot score. One-time use.
  </Card>

  <Card title="Partner Config" icon="gear" href="/api-reference/endpoint/post-sdkpartner-config">
    Get enabled integrations (Turnstile, etc.) for a site key.
  </Card>
</CardGroup>

### Session Management

<CardGroup cols={2}>
  <Card title="Revoke Verification" icon="ban" href="/api-reference/endpoint/post-sdkrevoke-verification">
    Cancel a pending verification. Use when create-verification-link returns 409.
  </Card>

  <Card title="Revoke Session" icon="right-from-bracket" href="/api-reference/endpoint/post-sdklogout">
    Invalidate a session token.
  </Card>
</CardGroup>

## Typical Flow

```mermaid theme={null}
sequenceDiagram
    participant B as Your Backend
    participant S as BotShield
    participant U as User's Phone

    B->>S: client.sdk.createSession()
    S-->>B: session_token

    B->>S: client.sdk.createVerificationLink()
    S-->>B: { web_url, qr_code_url }

    B->>U: Show QR code to user
    U->>S: User scans QR
    S->>U: Passkey verification

    B->>S: client.sdk.verifyToken({ token })
    S-->>B: { valid, claims: { request_id, verified, organization_id, timestamp, nonce } }

    Note over B: Anonymous claims — apply policy based on verdict + reason ✓
```

## Webhooks

Verification results are delivered to your callback URL as a signed **Svix** envelope. Authenticity is the Svix signature, not a token in the body — verify the `svix-id`, `svix-timestamp`, and `svix-signature` headers with your endpoint signing secret (`whsec_...`). There is nothing to decode inside the payload.

The payload is anonymous:

```json theme={null}
{
  "type": "census.human_verified",
  "event_id": "req_xyz789...",
  "request_id": "req_xyz789...",
  "verified_at": "2026-06-16T12:00:00Z",
  "product": "census",
  "metadata": { "order_id": "ORD-12345" }
}
```

A returning human resolved via MultiPass continuity arrives as `census.multipass_active` with the same shape. The `census.human_unavailable` event carries `failed_at` + `reason` (denial/error) or `expired_at` (TTL lapse) instead. `event_id` is the same `req_…` value as `request_id`. No `user_email`, `auth_mode`, or `botshield_user_id` is ever present. Correlate back to your action via `request_id` and the `metadata` you supplied when creating the link.

## Error Handling

| Code  | Meaning                                                                                                                               |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Success                                                                                                                               |
| `400` | Invalid or missing parameters                                                                                                         |
| `401` | Invalid or expired token                                                                                                              |
| `403` | Forbidden — includes **rate-limit blocks** (check `errors[0].code === 'RATE_LIMITED'`, see [Rate Limits](/api-reference/rate-limits)) |
| `409` | Duplicate pending verification — use `revokeVerification()` first                                                                     |
| `500` | Internal server error                                                                                                                 |

```json theme={null}
{
  "error": {
    "message": "Description of what went wrong",
    "statusCode": 400
  }
}
```

For rate-limit specifics — per-key-type limits, the `_rateLimit` response envelope, and block behavior — see the [Rate Limits](/api-reference/rate-limits) page.

## Next Steps

* [Web Component Reference](/embed/web-component) — Frontend SDK details, `BotShield.render()` API
* [Signal Pixel](/embed/signal-pixel) — Passive bot scoring details
* [Quick Start](/quick-start) — Step-by-step integration walkthrough
* [npm package](https://www.npmjs.com/package/botshield-sdk) — Backend SDK on npm
