caputchin
All docs
View raw .md

Troubleshooting

Error codes returned from /siteverify and emitted by the widget, with the cause and the fix.

/siteverify error codes

The response shape:

{ "success": false, "error-codes": ["..."] }
Code Cause Fix
missing-input-secret Request body has no secret field, or it is empty Make sure the env var is set on the server, and the request body includes it
invalid-input-secret secret value is malformed or unknown Verify the value starts with cpt_sec_ and matches what the dashboard shows. If you rotated the secret, the old one stops verifying immediately
missing-input-response Request body has no response field Read the form field named caputchin-token and pass its value as response
invalid-input-response Wrapped token is malformed (truncated, modified, or wrong shape) Get a fresh token by re-submitting the form. Wrapped tokens are base64url-JSON — do not trim or re-encode them
timeout-or-duplicate Token already redeemed or expired (10-minute TTL) Each token verifies exactly once. If you saw this on a retry, you already have a verified verdict — return the cached result instead of re-calling /siteverify

Widget client-side errors

The widget emits an error event with a code:

widget.addEventListener("error", (e) => console.log(e.detail.code));
Code Recoverable Cause Fix
invalid-config no Conflicting attributes — e.g. mode="manual" together with game or game-src See widget modes for which combinations are valid
resolve-failed no Marketplace game id could not be resolved at /games/:id/resolve Confirm the id is correct and currently indexed in the marketplace
iframe-load-failed no Sandboxed iframe failed to load the game script Verify the game id is correct or your game-src URL is reachable. For marketplace games the SRI hash must still match — see game-distribution
iframe-script-blocked no CSP inside the iframe blocked the game script Marketplace games include the canonical CSP; check that your customer-hosted game URL passes the iframe's script-src policy
game-not-registered no The loaded game script did not call register() at top level The author must call register() synchronously at module load. Confirm with the game author
game-error-relayed no The game's runtime threw an error and the widget forwarded it The relayed error carries an originalCode in event.detail — share it with the game author
postmessage-bad-origin yes An unexpected postMessage arrived from outside the iframe Almost always benign cross-frame noise; the widget keeps running
cap-solve-failed yes Cap's proof-of-work solve() failed (network or tab suspended mid-solve) The widget can re-issue. Encourage the user to retry; persistent failures point at a CSP or network issue with api.caputchin.com
cap-redeem-failed yes Cap's redeem step failed Same as cap-solve-failed — retry
form-not-found no Widget could not locate its enclosing <form> to inject the caputchin-token field Wrap the widget inside the <form> you want it to protect. The widget walks up the DOM looking for an ancestor <form>

Common integration mistakes

"Token is always null on the server"

The widget injects caputchin-token only after successful completion. If your form auto-submits before completion, the field is not there. Either use mode="form-submit" (widget gates submission until done) or wait for the complete event.

"I see two tokens injected"

You mounted two <caputchin-widget> elements in the same <form>. Each one injects a caputchin-token field on completion, and the second overwrites the first. Mount only one widget per form.

"Score is null even though the user played the game"

score is reported by the game, not the platform. The invisible default has no game, so score is null. Some games choose not to emit a score — that is also null. Either way, do not gate verification on score; it is client-claimed metadata. See principles.

"Verification works locally but fails in production"

The widget script must be reachable from the deployed page. Cloudflare and other CDN-fronted sites should allow cdn.jsdelivr.net. Your CSP must allow api.caputchin.com for the runtime calls.

"Hosted-verification webhook never fires"

Hosted verification is paid-tier only. Confirm the site is on the paid plan and the webhook is enabled in the dashboard. The platform retries failed deliveries with exponential backoff; check the dashboard delivery log.

Reporting a bug

If a verdict is wrong (verified humans rejected, or invalid tokens accepted), include:

  • The verdict response body (success + error-codes)
  • The wrapped token that was submitted (it is safe to share — it is single-use and already consumed)
  • A timestamp accurate to the minute
  • Whether the issue reproduces or is intermittent