Docs / Server verify / Ruby
Verify Playtcha from Ruby
This page is the server-side contract for Ruby apps. Your backend receives the client-submitted result token, posts it to /v1/verify with your secret key, and only then decides whether to continue the request.
Minimal example
The minimal shape is still { secret, token } over JSON, then block the request unless success is true.
Production-ready example
In production, add timeouts, log the upstream error envelope, and treat degraded mode as quota state rather than a failed verification.
Common gotchas
- Set read and open timeouts so verify stays bounded inside signup or form-submit flows.
- Fail open on 5xx and network errors; fail closed on 4xx. Opt into PLAYTCHA_FAIL_CLOSED=1 for high-fraud paths.
- On non-2xx responses, inspect the upstream error envelope ({ error: { code, message, request_id, ... } }) instead of assuming a success-shaped payload.
- The wire format has no success: false field on errors. Any success-style key in helper code is a local discriminator.
- Treat degraded=true as success and keep it in logs or metrics, not in user-facing failure copy.
- 409 already_redeemed is single-use-by-design. In production it means replay, stuck retry, or duplicate POST. Log with request_id at warn level — do not retry.
- 429 rate_limited_ip means per-IP throttle. Honor the Retry-After header; do not retry tighter than 1s.
Relevant failure codes
| Status | Code | Meaning |
|---|---|---|
| 401 | token_invalid | Bad signature or claims. Often wrong env (live vs test). |
| 401 | bad_secret | Secret does not match the project. Config error. |
| 404 | unknown_project | Project deleted or never existed. |
| 409 | already_redeemed | Security signal. Single-use by design. Log with request_id at warn level. |
| 410 | token_expired | Result token TTL elapsed (5 minutes). |
| 429 | rate_limited_ip | Per-IP throttle. Response has Retry-After header. |
| 500 | db_error | Playtcha-side. Fail open. |
Where to go next
Pair this with token lifecycle for the token model, or domain whitelist if your issue happens before verify on the public widget endpoints.