caputchin
All docs
View raw .md

Verify a Caputchin token on your backend

When a user submits the form, your backend receives a caputchin-token field. Pass it to /siteverify along with your secret. Reference for the endpoint shape is api.

1. Read the token from the form

caputchin-token=<wrappedToken>

The widget injects this hidden field. See widget.

2. POST to /siteverify

POST https://api.caputchin.com/siteverify
Content-Type: application/json

{
  "secret": "cpt_sec_...",
  "response": "<wrappedToken>"
}

Top level is reCAPTCHA-shape-compatible — if you've integrated reCAPTCHA before, the request shape is familiar.

3. Read the response

{
  "success": true,
  "...other cap_siteverify fields": "...",
  "platform": {
    "game_id": "bubble-pop",
    "score": 0.847,
    "duration_ms": 4200
  }
}

The platform.score and platform.duration_ms are client-claimed metadata, not security signals — see ADR-0002. Treat verification as binary on success. If you want to record score for a leaderboard or analytics, that's fine; just don't gate trust decisions on it.

4. Handle replay protection

The platform verifies the wrapped token's HMAC and rejects replays automatically. You don't need to track tokens client-side. A token is single-use; submitting the same one twice returns success: false.

5. Use any HTTP client

There is no first-party server SDK in any language — see ADR-0011. The endpoint is a plain JSON POST, so fetch in Node, requests in Python, Net::HTTP in Ruby, or whatever HTTP client your backend already uses works directly. Copy-paste examples for the most common languages live in snippets. For a typed client, run your preferred generator against the OpenAPI spec — the contract is three endpoints, so the generated output is short.

What about the customer's risk model?

If you want risk scoring (block / challenge / allow tiers), build it on top of verified PoW with your own signals. The platform deliberately does not provide a confidence score — see ADR-0002 and principles.

Common mistakes

  • Verifying client-side. Don't. The widget's complete event is UX-gating only. All trust decisions happen server-side with your secret.
  • Sending the secret to the browser. Don't. The secret never leaves your backend.
  • Forgetting to add your domain to the allowlist. Tokens issued from non-allowed origins fail verification. Configure in the dashboard.
  • Using score to make trust decisions. It's metadata, not security. See above.