Skip to content

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:

RiskWhat the user experiences
Low / attestedInvisible. A lightweight proof-of-work runs in the background; no interaction.
MediumA single image challenge (slide, click, select).
HighA 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.

PrefixProduced whenHow to verifyNotes
pt_Verification passed normallyPOST /v1/validate (server-side)Single-use. The common case.
sct_Issued by your server ahead of timen/a — it is an input to challenge init, not a pass tokenServer-issued challenge token; binds a challenge to your trusted backend. See below.
dg_App quota exhausted (degraded mode)/v1/validate returns valid: false, degraded: trueHMAC-signed, 5-min expiry. Accept/reject is your decision.
offline_Main API unreachable; issued by the bypass workerVerified against the backup API (server SDKs do this automatically)Continuity fallback.
co_Main and backup unreachable; generated client-sideCannot be verified server-sideApply 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.
  • action matches the scene you expected — reject a token minted for one flow (e.g. pay) if it is presented at another (e.g. /login). The action is 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.

MIT-licensed examples · CaptchaLa is operated independently