Docs / Server verify / Go
Verify Playtcha from Go
This page is the server-side contract for Go apps. Your backend receives a 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 shortest correct shape is still { secret, token } over JSON, then gate the request on success.
Production-ready example
In production, use a timeout, parse the upstream error envelope on non-2xx responses, and log degraded mode separately from actual failures.
Common gotchas
- Use a client timeout or context deadline so verify never becomes an unbounded dependency.
- 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, decode the upstream error envelope ({ error: { code, message, request_id, ... } }) instead of inventing your own failure shape first.
- The wire format has no success: false field on errors. The Success bool only exists on 2xx responses. Helper code that adds Success: false to a failure struct is doing it locally.
- Treat Degraded=true as success and log it as quota state, not a failed human check.
- 409 already_redeemed is single-use-by-design. In production it means replay, stuck retry, or duplicate POST. Log with RequestID 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.