Verify without a backend
A Caputchin token proves nothing until something confirms it. Normally that something is your own backend, which calls /siteverify and decides whether to trust the request. Hosted verification moves that step onto Caputchin: your form posts to a Caputchin forwarder, Caputchin verifies the token, drops anything that fails, and forwards only the verified submissions to a destination you choose.
It is the same trust decision as the backend check, run on Caputchin's side instead of yours. The point is that there is no /siteverify call to write and no secret to keep on a server, because there is no server.
Hosted verification is a paid feature, available on Alpha and up.
Who it is for
Hosted verification exists for the case where running a backend just to check a token is more than the project needs.
| You are | Why it fits |
|---|---|
| A static site (Webflow, Framer, plain HTML on a CDN) | There is no server to receive the form or call /siteverify. |
| A no-code builder (Wix, Squarespace, Carrd) | You cannot add server-side verification code. |
| A solo developer or small team | A contact or signup form is not worth standing up and operating a backend for. |
| Already running a backend, but not for this form | You can point one form at the forwarder and leave the rest of your stack alone. |
If you do run a backend and want to verify there, use verify on your backend instead. The two are alternatives, not layers.
How it works
The forwarder holds the submission in memory only for the moment it takes to deliver it. There is no storage between the post and the delivery, and the caputchin-token field is stripped before the payload reaches your destination.
Destinations
You configure one or both per site key. Enabling both is a common pattern: send to a webhook for processing and email yourself a copy.
| Destination | What you receive |
|---|---|
| Webhook | A JSON POST carrying your form fields plus Caputchin's verification metadata. |
| A plain email with the form fields and a footer noting Caputchin verified the submission. |
Deliveries are not signed. The forwarder URL's secrecy is what authenticates a webhook call, so keep the URL out of public repositories and client-side code. The setup tutorial shows the exact webhook payload.
What is kept private
Hosted verification follows the same no-visitor-data posture as the rest of Caputchin:
- Submissions are never stored. They live in process memory for the duration of the forward, then they are gone. Build your own record on the webhook end if you want one.
- No data about the submitter is collected. No IP, no User-Agent, no fingerprint, no tracking.
- Telemetry is aggregate only. Caputchin counts delivery successes and failures so you can see whether your destinations are healthy, never the contents of any submission. See statistics.
How a customer-supplied URL is kept safe
A URL that Caputchin's servers will call is a classic Server-Side Request Forgery risk. The forwarder rejects any webhook URL whose host is a private, loopback, link-local, or cloud-metadata address, both when you save it and again right before every delivery. The blocked categories include:
| Category | Examples |
|---|---|
| Loopback and unspecified | 127.0.0.1, 0.0.0.0, ::1 |
| Private (RFC 1918) | 10.x.x.x, 172.16.x.x through 172.31.x.x, 192.168.x.x |
| Link-local and cloud metadata | 169.254.x.x, in particular the metadata endpoint 169.254.169.254 |
| Carrier-grade NAT | 100.64.x.x through 100.127.x.x |
| Multicast and reserved | 224.x.x.x and above |
| Internal hostnames | localhost and any host ending in .local, .localhost, or .internal |
| Private IPv6 | link-local (fe80::/10) and unique-local (fc00::/7) addresses |
Encodings that try to disguise a blocked address are caught too: a decimal-integer host like http://2130706433/ or a hex host like http://0x7f000001/ (both of which mean 127.0.0.1) are rejected. Only http and https URLs are accepted; any other scheme is refused. The forwarder also refuses to follow redirects, so a webhook endpoint cannot bounce the call to an internal target. A blocked URL surfaces as a generic error rather than a specific one, so it cannot be used to probe a network. Your webhook must therefore live at a public https URL.
What is intentionally left out
- No submission inbox. There is no stored history of submissions to browse in the dashboard.
- No first-party Discord, Slack, Telegram, or SMS adapters. Webhook and email only. A webhook can fan out to any of those on your side.
- No file uploads. The forwarder accepts text fields; a submission carrying a file is rejected.
- No payload transformation. What the form posts is what your destination receives, plus the verification metadata.
See also
- Set up hosted verification: a walkthrough that wires it to a test inbox first.
- Hosted verification statistics: reading delivery health.
- Verify on your backend: the alternative when you do run a server.