How verification works
CaptchaLa is risk-engine-first. Every request is scored before any challenge is shown. Most requests never see an interactive challenge — the engine decides how much friction (if any) a given request needs. This page describes that flow and the tokens it produces.
The graduated model
When the SDK initializes a challenge, the server scores the request from device, network, and behavioral signals (and, where available, attestation signals — see Attestation & Privacy Pass). The score then selects a verification tier:
| Risk | What the user experiences |
|---|---|
| Low / attested | Invisible. A lightweight proof-of-work runs in the background; no interaction. |
| Medium | A single image challenge (slide, click, select). |
| High | A harder challenge, stricter rate limits, or rejection. |
The goal is that legitimate, low-risk traffic passes without friction, and only the requests the engine cannot clear automatically are asked to solve something. The tier is decided server-side; the client cannot raise its own trust level.
Token types
A completed verification yields a pass token that your frontend forwards to your backend. The prefix tells you how it was produced and how to verify it.
| Prefix | Produced when | How to verify | Notes |
|---|---|---|---|
pt_ | Verification passed normally | POST /v1/validate (server-side) | Single-use. The common case. |
sct_ | Issued by your server ahead of time | n/a — it is an input to challenge init, not a pass token | Server-issued challenge token; binds a challenge to your trusted backend. See below. |
dg_ | App quota exhausted (degraded mode) | /v1/validate returns valid: false, degraded: true | HMAC-signed, 5-min expiry. Accept/reject is your decision. |
offline_ | Main API unreachable; issued by the bypass worker | Verified against the backup API (server SDKs do this automatically) | Continuity fallback. |
co_ | Main and backup unreachable; generated client-side | Cannot be verified server-side | Apply your own risk controls. |
sct_ is not a pass token — it is a short-lived credential you issue from your backend (POST /v1/server/challenge/issue) and the browser forwards into challenge initialization, proving the request originated from your server. It can be bound to an IP, device id, or fingerprint. See the API Reference.
pt_ is the token you actually validate after a user solves a challenge.
Publicly-verifiable tokens
All tokens above are verified by calling CaptchaLa (or, for offline_, the backup API). A token you can verify offline against a published issuer public key — no call back to us — is a roadmap item, not available today. See the roadmap note.
Single-use and anti-replay
pt_ tokens are single-use. The first successful /v1/validate consumes the token; a second validation of the same token returns token_already_used. This is what prevents a captured token from being replayed against your endpoint.
Each token also carries the context recorded at solve time (action, the end-user IP, solve timestamp, platform, risk score). Two checks matter on your side:
valid === true— the token verified and has not been consumed.actionmatches the scene you expected — reject a token minted for one flow (e.g.pay) if it is presented at another (e.g./login). Theactionis bound into the token at issue time and echoed back at validation.
Tokens are also short-lived; an expired token returns token_expired.
Server-side verification flow
Browser/App Your backend CaptchaLa API
| | |
| 1. (optional) ask server | |
| for a server_token |--- POST /v1/server/ |
|<------ sct_ ---------------| challenge/issue --------->|
| |<------ sct_ -----------------|
| | |
| 2. init + solve challenge |------------------------------>|
| (sct_ forwarded) | (risk scoring + tier) |
|<------ pt_ ----------------|------------------------------|
| | |
| 3. submit form w/ pt_ ---->| |
| | 4. POST /v1/validate |
| | X-App-Key / X-App-Secret |
| | { pass_token, client_ip }-->|
| |<--- { valid, action, ... } --|
| | 5. check valid && action |Validation is always done server-to-server with your X-App-Secret, which must never reach the browser. Sending the end-user's client_ip is optional but recommended — it lets the engine apply an extra consistency check at validation time.
For request/response schemas and error codes, see the API Reference.