---
type: tutorial
title: Quickstart — add Caputchin to your site in 5 minutes
description: End-to-end walkthrough from sign-up to a verified form, with the bare minimum HTML and one backend call.
---

# Quickstart

The fastest path from "I just made an account" to "my form is protected". You need a page with a `<form>` and a backend that can make an outbound HTTP call. No build step required.

By the end of this guide your form submission will only accept entries from a verified human.

## 1. Get a site key

Sign in to the dashboard and create a new site. You will receive two values:

| Value | Where it lives | Shape |
|---|---|---|
| Public site key | Embed in the page HTML | `cpt_pub_...` |
| Secret | Server-side only; never ship to the browser | `cpt_sec_...` |

Save the secret somewhere your backend can read it — environment variable, secrets manager, whatever you already use.

## 2. Drop the widget into your form

Paste these two lines into the page that hosts the form you want to protect:

```html
<script src="https://cdn.jsdelivr.net/npm/@caputchin/widget@1/dist/widget.js"></script>

<form action="/submit" method="POST">
  <!-- your existing form fields -->
  <input name="email" type="email" required />

  <caputchin-widget sitekey="cpt_pub_..."></caputchin-widget>

  <button type="submit">Sign up</button>
</form>
```

On successful completion the widget injects a hidden `<input name="caputchin-token">` into the enclosing `<form>`. Nothing else on your page needs to change.

If you want a game challenge instead of the invisible default, add the `game` attribute. See [widget](../widget.md#element-shape) for the full attribute table.

## 3. Verify on your backend

When the form posts to `/submit`, your handler receives one extra field: `caputchin-token`. Pass it to `/siteverify` along with your secret:

```js
// Node 18+
app.post("/submit", async (req, res) => {
  const verdict = await fetch("https://api.caputchin.com/api/v1/siteverify", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      secret: process.env.CAPUTCHIN_SECRET,
      response: req.body["caputchin-token"],
    }),
  }).then((r) => r.json());

  if (!verdict.success) {
    return res.status(400).json({ error: verdict["error-codes"] });
  }

  // Verified — continue your normal sign-up flow.
});
```

Snippets for Python, Go, PHP, and curl are in [snippets](../snippets.md).

## 4. Submit the form

Open the page, fill in the form, complete the widget challenge (or wait for the invisible flow), and hit submit. Your backend should log a successful verification.

If something is wrong, the response carries an `error-codes` array. The full mapping is in [troubleshooting](troubleshooting.md).

## What just happened

| Step | Where | What it does |
|---|---|---|
| Widget mounts | Browser | Loads the challenge surface inside a sandboxed iframe |
| User completes the challenge | Browser | Proof-of-work runs in the iframe, isolated from your page |
| Widget injects `caputchin-token` | Browser | One-time token, single-use, expires after 10 minutes |
| Your form posts | Backend | Token rides along with the rest of the form fields |
| `/siteverify` | Server-to-server | Caputchin checks the token, returns `success: true` or an error code |

## Next steps

- [Integrate the widget](integrate-widget.md) — deeper attributes, events, programmatic control
- [Verify server-side](verify-server-side.md) — long-form backend integration including idempotency notes
- [Troubleshooting](troubleshooting.md) — what each error code means and how to recover
- [Migrate from reCAPTCHA](migrate-from-recaptcha.md) — field-by-field swap
