---
type: tutorial
title: Set up hosted verification with a test inbox
summary: Wire hosted verification end to end against a free webhook.site inbox, watch a real verified submission arrive, then point it at your own destination.
---

By the end of this tutorial you will have a working contact form whose submissions are verified by Caputchin and delivered to a webhook, and you will have watched a verified submission land in a test inbox you can inspect. We use [webhook.site](https://webhook.site) as the destination first, because it shows you the exact payload Caputchin sends before you commit to your own endpoint.

You need an Alpha, Troop, or Apex plan (hosted verification is not on Solo), a site key, and a page where you can edit the form's HTML. If you have not added the widget to a page yet, do [add the widget](/docs/getting-started/add-the-widget) first; this tutorial assumes a working `<caputchin-widget>`.

## 1. Get a test inbox

Open [webhook.site](https://webhook.site) in a new tab. It immediately gives you a unique URL that looks like `https://webhook.site/#!/<uuid>`, and a matching delivery URL of the form `https://webhook.site/<uuid>`. Copy the delivery URL (the one without the `#!/`). Leave the tab open; every request it receives appears there in real time.

This URL is a stand-in for your real webhook. Treat it as throwaway, since anyone with the URL can read what arrives.

## 2. Turn on hosted verification

In the dashboard, open the site key and go to its **Hosted verification** page.

1. Paste the webhook.site delivery URL into the **webhook URL** field.
2. Leave **email** empty for now.
3. Save.

You do not have to flip the **enabled** toggle yet. The next step uses **Test delivery**, which works on a saved destination whether or not hosted verification is switched on, so you can confirm the wiring before going live.

## 3. Send a test delivery

On the same page, use **Test delivery**. Caputchin sends a synthetic submission straight to your destination without running a game or verifying a token, so it is a pure check of the destination wiring.

Switch to the webhook.site tab. A new `POST` appears within a second or two. Its JSON body looks like this:

```json
{
  "caputchin": {
    "site_key": "cpt_pub_...",
    "session_id": "test_...",
    "game_id": null,
    "score": null,
    "duration_ms": null,
    "verified_at": 1748640000000,
    "test": true
  },
  "form": {
    "email": "test@example.com",
    "message": "Test submission from your Caputchin dashboard."
  }
}
```

The `"test": true` marker tells your handler this was a dashboard test, not a real visitor; it is absent on real submissions. If you see this payload, the destination works. If nothing arrives, re-check that you copied the delivery URL (not the `#!/` viewer URL) and saved it.

## 4. Point your form at the forwarder

Now make a real submission go through Caputchin. The only change to your form is its `action`: instead of your own backend, it posts to the forwarder URL the site key shows you on the Hosted verification page.

```html
<form action="https://verify.caputchin.com/v1/forward/cpt_pub_..." method="POST">
  <input name="email" type="email" />
  <input name="message" />
  <caputchin-widget sitekey="cpt_pub_..."></caputchin-widget>
  <button type="submit">Send</button>
</form>
```

Nothing else changes. The widget still injects the hidden `caputchin-token` field when the visitor clears the challenge; the forwarder reads that field, verifies it, and strips it before delivering the rest to your destination.

## 5. Enable hosted verification

Back on the Hosted verification page, flip the **enabled** toggle on and save. The forwarder rejects submissions for a site key that is not enabled, so this switch is what makes step 4 live.

## 6. Submit the form for real

Load your page, fill the form, clear the challenge, and submit. Watch the webhook.site tab: a second `POST` arrives, this time with no `test` field and with real verification metadata.

```json
{
  "caputchin": {
    "site_key": "cpt_pub_...",
    "session_id": "...",
    "game_id": "caputchin/games/leaf-memory",
    "score": 847,
    "duration_ms": 4200,
    "verified_at": 1748640000000
  },
  "form": {
    "email": "visitor@example.com",
    "message": "Hello!"
  }
}
```

That is the whole path: visitor submits, Caputchin verifies, your destination receives only the verified submission. A submission that fails verification (a missing, expired, or reused token) never reaches your destination at all.

Treat the `caputchin` block the way you would a backend verdict: `game_id`, `score`, and `duration_ms` are client-claimed metadata for your analytics, not a trust signal. The trust decision is the fact that the submission arrived at all. Any of those three can be `null` on a real submission (for example a verification with no game), so guard for null before you read them.

## 7. Try the email destination too

webhook.site also gives each inbox an email address, so you can test email delivery the same way. On the webhook.site page, find your inbox's unique email address and copy it; at the time of writing it looks like `<your-inbox-id>@emailhook.site`, but check the page for the current form since webhook.site owns that detail. Then:

1. Back on the Hosted verification page, paste that address into the **email** field.
2. Save, and use **Test delivery** again.

The email arrives in the webhook.site inbox alongside the requests: a plain message carrying the form fields and a footer noting Caputchin verified the submission. Email is delivered through Caputchin's email provider, so it can take a little longer to land than a webhook `POST`. With both a webhook URL and an email address set, a single submission fans out to both, and counts as delivered if either one succeeds.

## 8. Swap in your real destination

When the test inbox has shown you what to expect, replace it with your own:

- **Your own webhook.** Put your endpoint's URL in the **webhook URL** field. It must be a public `https` URL; Caputchin refuses URLs that resolve to private, loopback, or cloud-metadata addresses, and it will not follow redirects. Your handler receives exactly the JSON shape above. Deliveries are not signed, so keep the URL secret, since its secrecy is what authenticates the call.
- **Email instead, or as well.** Put your own address in the **email** field, the same way you tested it above. You can run both at once, for example a webhook for processing plus an email copy to yourself.

Send one more real submission against the live destination to confirm, then you are done.

## Where to go next

- [Hosted verification statistics](/docs/hosted-verification/statistics): watch delivery health over time.
- [Hosted verification](/docs/hosted-verification/overview): the concepts, the privacy posture, and what is intentionally left out.
- [Verify on your backend](/docs/integration-guides/verify-on-your-backend): the alternative if you later add a server.
