Migrate from Cloudflare Turnstile
Caputchin uses the same two-part model as Cloudflare Turnstile, 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. Turnstile is the closest of the common providers to Caputchin, because both return an authoritative pass/fail rather than a risk score, so there is no threshold to re-tune. 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 first; you will need a public key (cpt_pub_...) for the page and a secret (cpt_sec_...) for your backend, the same split as Turnstile's site key and secret key.
The mental model is unchanged
Everything you already built around Turnstile, 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
| Turnstile | Caputchin | |
|---|---|---|
| Script | <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer> | <script src="https://cdn.jsdelivr.net/npm/@caputchin/widget@3/dist/widget.js"> |
| Element | <div class="cf-turnstile" data-sitekey="..."> | <caputchin-widget sitekey="cpt_pub_..."> |
| Token field in a form | cf-turnstile-response (auto-injected) | caputchin-token (auto-injected) |
| Reading the token in JS | turnstile.getResponse() | the pass event detail.token |
Like Turnstile, 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 for the full client setup and the CDN vs npm choice.
2. Swap the server verify
This is where the two are closest, the request and response shapes line up almost field for field.
| Turnstile | Caputchin | |
|---|---|---|
| Endpoint | POST https://challenges.cloudflare.com/turnstile/v0/siteverify | POST https://caputchin.com/api/v1/siteverify |
| Request fields | secret, response | secret, response (identical) |
| Response | { success, challenge_ts, hostname, "error-codes", action, cdata } | { success, challenge_ts, hostname, error-codes } |
| 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; both already branch on success, so there is no score threshold to port. Turnstile also accepts JSON for the verify call, exactly like Caputchin, so the body shape carries over unchanged. The full request/response reference, including the error codes, is on verify a token on your backend; framework snippets are in backend examples.
If your Turnstile integration read action or cdata off the response, note Caputchin has no direct equivalent; those are Turnstile-specific labels you set on the widget. Caputchin's analogous metadata (which game was played, its score) lives in the response's platform block and is informational only.
What carries over, and what gets better
- Still a clean pass/fail. You keep the simplest possible trust rule,
if (success), with no probability or threshold to maintain. - The privacy posture you chose Turnstile for, kept. Caputchin collects no IP, User-Agent, fingerprint, or behavioral telemetry; the protocol has nowhere to put a visitor identifier. See the philosophy.
- An optional game. Instead of an invisible or managed challenge, you can turn verification into a short game your visitors actually play, and it is the part that holds up against AI solvers. See 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.
Gotchas
- Two keys, two homes. The public key (
cpt_pub_...) goes on the page; the secret (cpt_sec_...) stays server-side only, exactly the Turnstile discipline. Do not ship the secret to the browser. - The token is single-use. Like Turnstile, a token verifies once. Do not cache or replay it.
- Change the field name your backend reads. The hidden field is
caputchin-token, notcf-turnstile-response, a common one-line miss. - No
action/cdata. If you branched on those, move that logic elsewhere; Caputchin does not carry them.
See also
- Add the widget: the full client setup.
- Verify a token on your backend: the authoritative
siteverifyreference. - Backend examples: the verify call in each language.
- Add a game: turn verification into a game.