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

# Adding Captcha Protection

<Card title="Recommended: JavaScript SDK for React Native" icon="react" color="#4779FE">
  While this SDK is still supported, we recommend using newer [JavaScript SDK](/docs/javascript/reference/react-native-quickstart), which is optimized for React Native, but also comes with a host of other benefits.
</Card>

## Summary

Dynamic supports two captcha providers to protect your app from bots:

* **Cloudflare Turnstile** — Dynamic manages the keys by default, or bring your own site key and secret key.
* **hCaptcha** — Requires your own credentials.

Configure your provider in the Dynamic Dashboard under **Fraud Protections → Captcha**, then follow the implementation steps below.

## Setting up Cloudflare Turnstile

The managed widget is enabled by default with no Cloudflare account required.

1. In the Dynamic Dashboard, go to **Fraud Protections → Captcha**.
2. Select **Cloudflare Turnstile** as your provider.
3. Optionally supply your own site key and secret key for widget customization.
4. Toggle captcha **on**.

## Setting up hCaptcha

hCaptcha requires your own account.

1. Sign up at [hcaptcha.com](https://www.hcaptcha.com/) (Pro plan recommended for passive/friction-free modes).
2. Add a new site with the hostname where your app operates.
3. Copy your **site key** (used client-side) and **secret key** (used server-side).
4. In the Dynamic Dashboard, go to **Fraud Protections → Captcha**, select **hCaptcha**, enter both keys, and enable the toggle.

## Implementation

The React Native SDK handles captcha through a `CaptchaProvider` that renders the challenge in a full-screen WebView modal and automatically calls `setCaptchaToken` on completion.

### Prerequisites

`react-native-webview` is required to render the captcha widget:

```sh theme={"system"}
npx expo install react-native-webview
```

### 1. Wrap your app with CaptchaProvider

Add `CaptchaProvider` inside your `DynamicProvider`. It mounts the captcha modal and manages the token lifecycle.

```tsx theme={"system"}
import { CaptchaProvider } from '@dynamic-labs-sdk/react-native-demo-components';

export default function App() {
  return (
    <DynamicProvider environmentId="YOUR_ENVIRONMENT_ID">
      <CaptchaProvider>
        {/* rest of your app */}
      </CaptchaProvider>
    </DynamicProvider>
  );
}
```

### 2. Prompt for captcha before sign-in

Use the `useCaptchaPrompt` hook to show the captcha modal before sending an OTP. `promptCaptchaIfRequired` is a no-op when captcha is not enabled for the environment, so no extra conditional logic is needed.

```tsx theme={"system"}
import { useCaptchaPrompt } from '@dynamic-labs-sdk/react-native-demo-components';
import { useSendEmailOTP } from '@dynamic-labs-sdk/react-hooks';

function SignInScreen() {
  const { promptCaptchaIfRequired } = useCaptchaPrompt();
  const { mutateAsync: sendEmailOTP } = useSendEmailOTP();

  const handleSubmit = async () => {
    try {
      await promptCaptchaIfRequired();
    } catch {
      return; // user dismissed the captcha
    }
    await sendEmailOTP({ email });
  };

  // ...
}
```

When the user completes the captcha challenge, the token is set automatically and the OTP request proceeds. If the user dismisses the modal, the `catch` block returns early without sending the OTP.
