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

# Bot protection

> Require a Cloudflare Turnstile check before a web call can start, with the built-in widget or your own token.

Bot protection stops automated traffic from starting calls with your agent. When it's on, Callers verifies a [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/) token on its servers before it issues call credentials, so it covers voice and text conversations, the script tag and the programmatic SDK. Unlike the domain list, a token does not bypass it.

## Turn it on

**1. Create a Turnstile widget in Cloudflare**

In the [Cloudflare Turnstile dashboard](https://dash.cloudflare.com/?to=/:account/turnstile), create a widget and add the same hostnames your agent's widget runs on.

**2. Paste both keys into Callers**

Open your agent, go to **Channels** > **Widget** > **Embed on Web**, and fill in **Site key** and **Secret key** under **Bot protection**.

**3. Save the agent**

Protection switches on only when **both** keys are filled in. Clearing either key switches it off.

Whether visitors ever see a challenge is set by the **Widget Mode** you choose for the site key in Cloudflare (Managed, Non-interactive or Invisible), not by the SDK. The SDK renders Turnstile so it appears only when Cloudflare actually needs the visitor to interact.

## With the built-in widget

Nothing to change. When the agent has bot protection on, the SDK:

1. Reads the agent's public configuration when it loads.
2. Shows the Turnstile check inside the call card when the visitor starts a call, and removes it as soon as it's solved.
3. Sends the token with the call request.

After a protected call ends, the visitor must close and reopen the widget to start another conversation, so each one gets a fresh check.

## Bring your own token

If you hide the built-in widget or run in a React Native WebView, solve Turnstile in your own UI and pass the token to `call()`. When you pass a token, the SDK skips its own configuration fetch and challenge.

**1. Get the site key**

Read it at runtime from the agent's public config (no authentication needed; the secret is never returned):

```bash theme={"dark"}
curl https://api.callers.ai/agents/YOUR_AGENT_ID/web-call/config
```

```json theme={"dark"}
{
  "status": "success",
  "data": {
    "captcha": {
      "enabled": true,
      "provider": "turnstile",
      "siteKey": "0x4AAAAAAA...",
      "action": "web-call-start"
    },
    "theme": null,
    "closeWidgetFormOnNextTranscriptMessage": false,
    "voiceGender": "female"
  }
}
```

When protection is off, `enabled` is `false` and the other `captcha` fields are `null`.

**2. Render Turnstile with the web call action**

The token **must** be created with the action `web-call-start`, exported as `WEB_CALL_CAPTCHA_ACTION`. A token minted for any other action is rejected.

```javascript theme={"dark"}
const { WebCall, WEB_CALL_CAPTCHA_ACTION } = window.CallersWebSdk;

function solveTurnstile(siteKey) {
  return new Promise((resolve, reject) => {
    turnstile.render('#captcha', {
      sitekey: siteKey,
      action: WEB_CALL_CAPTCHA_ACTION,
      callback: resolve,
      'error-callback': reject,
    });
  });
}
```

**3. Pass the token to call()**

```javascript theme={"dark"}
const webCall = new WebCall({ token, showFab: false });

startButton.onclick = async () => {
  const captchaToken = await solveTurnstile(siteKey);
  await webCall.call(captchaToken); // sent as the X-Turnstile-Token header
};
```

> [!WARNING]
>
> Turnstile tokens are single-use and expire after about 300 seconds. Mint a fresh one right before each `call()`; never reuse one.

> [!NOTE]
>
> Cloudflare Turnstile runs only in browsers; there is no native mobile SDK. In React Native, render the challenge in a WebView and pass the token back to your code. See [Headless and React Native](https://docs.callers.ai/sdk/headless-and-react-native.md#react-native).

## Errors

| Message | Cause |
| - | - |
| `Bot verification is required` (403) | Protection is on and the call request had no Turnstile token |
| `Bot verification failed` (403) | The token was invalid, expired, already used, created for a different action, or the secret key is wrong |
| `Captcha verification failed` / `expired` / `timed out` | Raised in the browser by the built-in challenge; the visitor can retry |
| `Failed to load Cloudflare Turnstile` | A content blocker or your CSP blocked `https://challenges.cloudflare.com` |

## Related

> **[Authentication](https://docs.callers.ai/sdk/authentication.md)**
>
> Domains and tokens.

> **[Troubleshooting](https://docs.callers.ai/sdk/troubleshooting.md)**
>
> More error messages.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.