---
type: how-to
title: Migrate from reCAPTCHA
summary: "Swap reCAPTCHA for Caputchin with minimal changes: the same two-part shape (a client widget plus a server-side verify) maps almost one-to-one, here is the exact before-and-after for each piece."
---

Caputchin uses the same two-part model as reCAPTCHA, a widget on the page that produces a token, and a server-side check that confirms it, so migrating is mostly a mechanical swap, not a rewrite. The server exchange in particular mirrors reCAPTCHA's `siteverify` shape on purpose. This guide gives the before-and-after for each piece.

If you have not yet created a Caputchin account and site key, do [create your account](/docs/getting-started/create-your-account) first; you will need a **public key** (`cpt_pub_...`) for the page and a **secret** (`cpt_sec_...`) for your backend, the same split as reCAPTCHA's site key and secret key.

## The mental model is unchanged

```mermaid
flowchart LR
  A["Widget on your page"] -->|token| B["Your form / request"]
  B -->|token| C["Your backend"]
  C -->|secret + token| D["siteverify endpoint"]
  D -->|success?| C
```

Everything you already built around reCAPTCHA, render a widget, collect a token, POST it to your server, verify it before trusting the request, stays. Only the names and endpoints change.

## 1. Swap the client snippet

| | reCAPTCHA (v2 checkbox) | Caputchin |
|---|---|---|
| Script | `<script src="https://www.google.com/recaptcha/api.js" async defer>` | `<script src="https://cdn.jsdelivr.net/npm/@caputchin/widget@3/dist/widget.js">` |
| Element | `<div class="g-recaptcha" data-sitekey="...">` | `<caputchin-widget sitekey="cpt_pub_...">` |
| Token field in a form | `g-recaptcha-response` (auto-injected) | `caputchin-token` (auto-injected) |
| Reading the token in JS | `grecaptcha.getResponse()` | the [`pass` event](/docs/references/widget-methods-and-events) `detail.token` |

Like reCAPTCHA, the Caputchin widget **auto-injects a hidden token field** into the form it sits in, so if your form already did a normal POST, you only change the element and the field name your backend reads. See [add the widget](/docs/getting-started/add-the-widget) for the full client setup and [the CDN vs npm choice](/docs/integration-guides/cdn).

## 2. Swap the server verify

This is where the two are closest, the request and response shapes line up almost field for field.

| | reCAPTCHA | Caputchin |
|---|---|---|
| Endpoint | `POST https://www.google.com/recaptcha/api/siteverify` | `POST https://verify.caputchin.com/v1/siteverify` |
| Request fields | `secret`, `response` | `secret`, `response` (identical) |
| Response | `{ success, challenge_ts, hostname, "error-codes" }` | `{ success, challenge_ts, hostname, error-codes, score? }` |
| Trust rule | act only if `success === true` | act only if `success === true` (identical) |

In most stacks the only change to your verification code is the URL and the secret value. The full request/response reference, including the error codes, is on [verify a token on your backend](/docs/integration-guides/verify-on-your-backend); framework snippets are in [backend examples](/docs/integration-guides/backend-examples).

## 3. Map the score, if you used reCAPTCHA v3

reCAPTCHA v3 returns a `score` (0.0 to 1.0) and asks you to pick a threshold, you are guessing how human a request looked. Caputchin does **not** work that way: `success` is an authoritative pass/fail, because the verification was actually cleared (a proof-of-work check, or a [game round re-derived on the server](/docs/understanding-caputchin/gaming-anti-cheat)), not a probability.

If your code had `if (data.score >= 0.5)`, replace it with `if (data.success)`. Caputchin's `score` field exists only for game rounds and is the **game's** score (informational, for your own leaderboards), never a bot-likelihood, so do not gate access on it.

## What gets better in the move

- **No visitor data collection.** reCAPTCHA profiles your visitors; Caputchin collects no IP, User-Agent, fingerprint, or behavioral telemetry, the protocol has nowhere to put a visitor identifier. See the [philosophy](/docs/understanding-caputchin/philosophy).
- **One widget, not v2-vs-v3.** There is no separate invisible/score product to choose between; one element covers the checkbox and the optional game.
- **An optional game.** Instead of a grid-of-traffic-lights, you can turn verification into a short game your visitors actually play. See [add a game](/docs/getting-started/add-a-game).
- **A strict CSP stays strict.** Caputchin runs under a tight Content-Security-Policy; you allow a few origins rather than loosen your policy. See [how Caputchin sandboxes games](/docs/understanding-caputchin/game-sandboxing#the-csp-you-set-on-your-own-page).

## Gotchas

- **Two keys, two homes.** The public key (`cpt_pub_...`) goes on the page; the secret (`cpt_sec_...`) stays server-side only, exactly the reCAPTCHA discipline. Do not ship the secret to the browser.
- **The token is single-use.** Like reCAPTCHA, a token verifies once. Do not cache or replay it.
- **Change the field name your backend reads.** The hidden field is `caputchin-token`, not `g-recaptcha-response`, a common one-line miss.

## See also

- [Add the widget](/docs/getting-started/add-the-widget): the full client setup.
- [Verify a token on your backend](/docs/integration-guides/verify-on-your-backend): the authoritative `siteverify` reference.
- [Backend examples](/docs/integration-guides/backend-examples): the verify call in each language.
- [Add a game](/docs/getting-started/add-a-game): turn verification into a game.
