Verification modes
VerifyHuman checks in levels: checks on our server, a silent check on your page, and a camera check. You choose where to start, based on how much proof you need and whether you can add code to the page. Checks are silent unless you choose a camera check.
You make two choices. Where the check runs: Server API (your server calls ours) or On your page (our script on the respondent's page). And, on your page, how strong the check is: Silent, Quick camera check or Full camera check. This page lists every option, what it checks and how to set it up.
Pick a mode
| If you… | Use | How to set it up |
|---|---|---|
| can't add code to the respondent's page (for example a panel or sample supplier calling from its server) | Server API | one HTTP call — POST /api/v1/lite/score |
| want a silent check on the page that the respondent never notices, with no camera | Silent | SDK mount with widgetMode: 'invisible' |
| want quick camera proof of a live person (about 2–3 seconds; the camera image is not shown) | Quick camera check | SDK mount with widgetMode: 'captcha' |
| want the strongest proof: a full camera check that the person is live and has not taken part before, with short challenges when needed | Full camera check | SDK mount with widgetMode: 'standard' (the default) |
| chose a camera check, but the camera couldn't open | Device-only result | happens automatically — nothing to set up |
The levels of check
- Server checks — every option, always. The respondent's network and address, device, repeat visits and history. Needs no code on the page.
- Page script — Silent. Our small script (under 15 KB) adds checks on the device and on how the respondent behaves on the page. No camera.
- Camera — Quick camera check and Full camera check. Proof that a live person is in front of the camera and has not taken part before.
- Short challenges — Full camera check, only when needed. Blink, smile or turn your head.
Every check, including a full camera check, runs the server checks first. The camera adds the one thing nothing else can: proof that a live, present person who has not taken part before is behind the session.
Server API (no page code)
A scoring API that your server calls once per respondent, at the same point in your flow as any other quality-check vendor. No camera, no JavaScript on the page, and no personal data stored. It returns a 0–100 risk score and an allow / review / block recommendation, based on the respondent's network, device, repeat visits, behaviour and history.
POST https://vhuman.riwi.com/api/v1/lite/score
Authorization: Bearer <project secret key>
Content-Type: application/json
{ "sessionId": "<your-id>", "clientIp": "<respondent-ip>",
"respondentId": "<stable-panelist-id>", // same person = same id, every visit
"userAgent": "<respondent-ua>", "sourceId": "<your-supply-source>",
"studyId": "<your-questionnaire-id>" } // optional; per-study duplicate scope
// → read response.recommendation (allow | review | block | step_up)Choose it when you can't put code on the respondent's page, want nothing for the respondent to do, and a risk score (not camera proof of a live person) is enough. Every field and value, how to hash IDs, thresholds per traffic source, and how to report outcomes back to us are in the Server API reference — or manage the project over MCP.
Where it's set: a project's deploymentType sets where the check runs: fraud_gate for Server API (no code on the page) or widget for On your page (the default). Set it in the dashboard, over MCP, or with PATCH /projects/{id}. For widget, widgetMode sets how strong the check on your page is (below).
On your page: Silent / Quick camera check / Full camera check
All three run the SDK on the respondent's page. The only difference is widgetMode. Setup for React, Next.js, the HTML snippet and plain TypeScript is in the SDK guide; this is the summary.
widgetMode | Respondent sees | What it checks |
|---|---|---|
invisible (Silent) | Nothing. The camera never opens, unless you turn on escalateOnSuspicion (off by default). | Server checks, plus the page script's device and on-page behaviour checks. No camera. Every mode returns the 0–100 riskScore from these checks beside Allow / Review / Block, whenever it could be computed (result reference). |
captcha (Quick camera check) | One click, or a short “verifying…” badge (about 2–3 seconds). The camera image is not shown. | Everything above, plus a quick check that a live face is in front of the camera. |
standard (Full camera check) | The camera view with a face guide; short challenges when needed. | Everything above, plus a full liveness check, a check that the camera is not faked, and a check that the face has not taken part before. |
Adaptive: Silent first, a camera only when unsure
A Silent project can turn on Adaptive in its settings. Most respondents still see nothing. When our server cannot clear a respondent from the silent checks alone, the same session continues with a camera check (Quick or Full, your choice) instead of ending unresolved. Respondents the server is sure about are allowed or blocked without a camera, exactly as in Silent.
| What happens | Result you receive | Checks billed |
|---|---|---|
| Silent checks clear the respondent | pass with a token | 1 |
| Silent checks find clear fraud | fail, never a camera | 1 |
| Unsure: the camera check runs and passes / fails | pass with a token / fail | 5 in total |
| Unsure, and the camera cannot be used or is refused | fail with no token, and unifiedVerdict.verdict review (or block if you choose that in settings) | 1 |
| Unsure, and the respondent leaves at the camera prompt | No result | 0 |
You do not need different code: the SDK (1.30.0 and later) handles the camera step itself and resolves VH.mount or calls onVerify / onError once, with the final result. If you call /verify yourself, a challenge_required response carrying stepUp: { mode } means “run that camera check next in this session”. A token is issued only on pass, so an integration that checks the token treats a review as not passed.
Keep the widget's container visible. The respondent starts the camera check themselves, so a Silent widget hidden off-screen leaves them waiting. To react when the camera step starts, use onStepUp(mode) on VH.mount, or listen for the vh:step-up event with the HTML snippet. On Adaptive projects onVerified fires once, for the step that finishes.
Minimal wiring
// Managed (npm: import { VH } from '@verifyhuman/sdk'; CDN: window.VH)
const result = await VH.mount("#vh", {
siteKey: "YOUR_PROJECT_ID", // your Project ID
widgetMode: "invisible", // 'invisible' | 'captcha' | 'standard'
});
// React
import { VerifyHuman } from "@verifyhuman/sdk/react";
<VerifyHuman siteKey={SITE_KEY} widgetMode="captcha" onVerify={(t) => ...} />The widgetMode option or prop overrides the project's setting in the dashboard. The plain HTML data-sitekey snippet has no data-widget-mode attribute, so it uses the project's setting. To choose it in code, use VH.mount or <VerifyHuman>. See the three layers of “mode” for which setting wins.
Device-only result (when the camera can't open)
You don't choose this. When a camera check can't open the camera (an app's built-in browser blocks it, the respondent denies permission, or the face model fails to load), the check finishes without the camera instead of stopping. It is scored by the server checks, plus on-page behaviour if the script collected any. You can see this on the result: unifiedVerdict.assuranceLevel is device_only or behavioral instead of biometric. You can also require a minimum level and reject these results. See In-app browsers & camera for how respondents recover and what each level means.
When the camera check can't finish
For the Quick camera check and Full camera check, a project chooses what decides the result when the camera check can't finish. The camera check itself runs the same way with both options; only the fallback changes. Set it in the dashboard under Project → Settings → When the camera check can't finish (or PUT /api/v1/projects/{id} with {"config":{"verdictPolicy":"camera_pilot"}}). Every project starts on Use the camera result as it is (default).
Option (verdictPolicy) | Camera works | Camera check can't finish |
|---|---|---|
Use the camera result as it is (default) | The camera result. | A failed liveness check or challenge is a fail, including a camera that shows no face (for example a covered lens). A camera that could not run (blocked by an app's built-in browser, permission denied, no camera, face model failed to load) follows the project's outage setting (degradedMode), but only once the respondent clicks "My camera isn't working". Without that click it does not pass (SDK v1.28.9 and later). From v1.28.10 the click counts only when the result carries cameraDiagnostics.declaredBy: "user_click". |
Fall back to the silent check (camera_pilot) | The camera result, as before. A fake camera is blocked: a virtual camera, a screen replay or a photo held up to a working camera fails with decisionPath: "camera_fraud_signal", whatever the silent check says (on by default; see the setting below). | The silent check that already runs inside every check decides: blocked only if the silent check says block. If it says allow, review or step_up, the respondent passes with a flag (no signed token, level device_only), so nobody is rejected only because their camera failed. If the silent check could not run, the respondent passes with a flag, and degradedSignals contains camera_pilot_lite_unavailable. |
Two things never change with this setting: repeat checks keep their own rule (a repeat respondent under enforcement.uniqueness = block still fails), and a respondent who declines the camera is treated like a camera check that couldn't finish. Two more settings sit on the same card. Always block a fake camera (blockOnCameraFraudSignals, on by default): a virtual camera, screen replay or photo in front of a working camera is blocked even when the silent check looks clean; turn it off only if you want those sessions judged by the silent check instead. Camera troubleshooting data (pilotDiagnostics: off or failed_camera_sessions): the second option also saves a limited number of the SDK's browser log lines for camera checks that did not pass; never images. The project overview and the camera check report show every camera outcome and what decided each session.
You can always tell what produced a result. Every check, in every option, returns three fields confirmed by our server: verdictPolicy, cameraStageOutcome (passed, failed_liveness, failed_challenge, failed_fraud_signal, not_run_permission_denied, not_run_environment_blocked, not_run_no_camera, not_run_camera_in_use, not_run_model_load_failed, not_run_no_face, not_run_timeout, not_run_unknown, declined, not_attempted) and decisionPath (camera, lite_fallback, camera_fraud_signal, degraded_mode, policy_gate, floor_tighten, invisible_floor). With the fallback on, filter on decisionPath === 'lite_fallback' to see exactly which respondents the camera could not judge. See the result shape.
Reading the result — the same words everywhere
Whatever option you use, the answer is allow / review / block, plus the level of check that produced it.
- Server API → read
recommendation(allow·review·block·step_up).verdictClass(human/fraud/verified_agent/unknown) says who we think is behind the session, separately from what to do.fraudmeans high risk, not proof. - On your page → read
unifiedVerdict.verdict(allow/review/block) for the same answer as the Server API, orstatus(pass/fail) for the result of the check on your page.unifiedVerdict.assuranceLevel(none/device_only/behavioral/biometric) tells you which level of check produced the result.
The check on your page maps its PASS / FAIL / CHALLENGE_REQUIRED decision onto the same allow/review/block words the Server API uses (PASS → allow, CHALLENGE_REQUIRED → review, FAIL → block), so you only need one set of words. step_up (Server API only) is a review that recommends a camera check next. You get it only if your project has turned on the camera step-up.