Server API
Complete REST reference for backend integration. Snippets below are runnable — replace placeholder values with your real keys. For dashboard-rendered snippets pinned to a specific project, sign in and visit your project's Integrate page.
Base URL
Production: https://verifyhuman.riwi.com/api/v1
Staging: https://vhuman.riwi.com/api/v1
Authentication
- Public endpoints (
/public/sessions,/public/verify,/public/projects/{id}/demo-key) — no API key. Origin is validated against the project's allowed-domains list. - Server-side endpoints (
/token/verify,/token/verify-batch,/projects/provision,/projects/{id}/analytics) — send your secret API key in theX-API-Keyheader. Manage keys at your dashboard.
Endpoint reference
POST /public/sessions
Create a verification session from a site key. The SDK calls this from the browser; you typically don't call it yourself unless you're building a custom widget.
Body fields: siteKey (required), studyId (optional), respondentId (optional, max 256 chars — your respondent identifier, echoed back on /token/verify and on webhook payloads for cross-reference), metadata (optional object).
curl -X POST https://verifyhuman.riwi.com/api/v1/public/sessions \
-H "Content-Type: application/json" \
-d '{
"siteKey": "proj_abc123",
"studyId": "study_001",
"respondentId": "resp_456",
"metadata": {"panel": "prolific"}
}'
# → { "sessionId": "sess_...", "clientSecret": "...", "expiresAt": 1710853200, ... }POST /public/verify
Submit the verification envelope. The SDK populates this body from local capture data. Returns a server-signed JWT in verificationToken that you can validate via /token/verify from your backend.
POST /token/verify
Server-side token validation. Required headers: X-API-Key: vf_live_….
curl -X POST https://verifyhuman.riwi.com/api/v1/token/verify \
-H "X-API-Key: $VERIFYHUMAN_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"token": "TOKEN_FROM_CLIENT"}'Success response:
{
"success": true,
"sessionId": "sess_abc123",
"status": "pass",
"scores": {
"liveness": 0.95,
"uniqueness": 0.98,
"authenticity": 0.92,
"overall": 0.95
},
"studyId": "study_001",
"respondentId": "resp_456",
"issuedAt": 1710849600,
"expiresAt": 1710853200
}On failure, success: false with error ∈ { EXPIRED, INVALID, WRONG_AUDIENCE, MALFORMED }.
POST /token/verify-batch (new — VH-05, 2026-05-19)
Validate up to 100 tokens in one round-trip. Useful for data-pipeline re-verification jobs where you've queued responses without a per-response server check at submit time.
curl -X POST https://verifyhuman.riwi.com/api/v1/token/verify-batch \
-H "X-API-Key: $VERIFYHUMAN_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"tokens": ["TOKEN_A", "TOKEN_B", "TOKEN_C"]}'
# → { "results": [ TokenVerifyResponse, TokenVerifyResponse, ... ] }Each token is verified independently. Cross-org tokens land as success: false with WRONG_AUDIENCE rather than failing the whole batch.
POST /projects/provision (new — VH-01, 2026-05-19)
Server-to-server project creation. Lets you auto-provision a VerifyHuman project from your own backend when (e.g.) a researcher adds a verification step to a study, without the researcher ever opening the VH dashboard.
Auth: X-API-Key scoped to the calling organization.
curl -X POST https://verifyhuman.riwi.com/api/v1/projects/provision \
-H "X-API-Key: $VERIFYHUMAN_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My Study",
"description": "Pilot for fall cohort",
"externalId": "your-study-id-here"
}'
# → {
# "projectId": "proj_...",
# "siteKey": "proj_...", // same value — use as SDK siteKey
# "externalId": "your-study-id-here",
# "name": "My Study",
# "organizationId": "org_...",
# "createdAt": "2026-05-19T..."
# }externalId is preserved on the project record for cross-reference. Signing material is bootstrapped server-side and not exposed by this endpoint — rotate it via POST /projects/{id}/signing-key/rotate when you need the PSK.
GET /projects/{id}/analytics (new — VH-08, 2026-05-19)
Server-to-server analytics for your project. Mirrors the dashboard's analytics page; useful for embedding metrics in customer-facing reports.
curl -G https://verifyhuman.riwi.com/api/v1/projects/$PROJECT_ID/analytics \
-H "X-API-Key: $VERIFYHUMAN_SECRET_KEY" \
--data-urlencode "period=day" \
--data-urlencode "fromDate=2026-05-01" \
--data-urlencode "toDate=2026-05-19"
# → {
# "totalVerifications": 1234,
# "passedCount": 1100,
# "failedCount": 134,
# "passRate": 0.892, // 0–1 fraction
# "avgLivenessScore": 0.86,
# "avgUniquenessScore": 0.95,
# "avgDurationMs": 4200,
# "timeSeries": [...],
# "failureReasons": [...],
# "devices": [...],
# "browsers": [...]
# }POST /public/projects/{id}/demo-key
Mint a short-lived test key for integration trials. Rate-limited (10/min soft). Demo verifications are tracked in a separate is_demo bucket and don't bill against production quota.
Score envelope
The four scores in scores are all 0–1 floats:
liveness— passive liveness from facial geometry + micro-expressions + eye movement.uniqueness— cosine similarity against previously-seen fingerprints in the same study. 1.0 = highly unique, 0.0 = exact duplicate.authenticity— anti-spoof / virtual-camera / screen-detection composite.overall— weighted aggregate driving thestatusverdict. Default weightsliveness×0.40 + uniqueness×0.30 + authenticity×0.30.
Webhooks
See Webhooks for the full payload contract, retry behavior (5 attempts with exponential backoff, terminal-4xx skips retry), idempotency key, and the v1 / v2 payload shapes. v2 payload (2026-05-19) carries respondent_id, metadata, ISO timestamp, fraud signals, and risk breakdown.
Rate limits & quotas
- Rate limit (429 RATE_LIMIT_EXCEEDED): per project + origin. Response includes
Retry-After,X-RateLimit-Remaining,X-RateLimit-Reset. - Quota (429 QUOTA_EXCEEDED): monthly cap on verifications. Body includes
limit,usage,resetDate. - Trial expiry (402 TRIAL_EXPIRED): distinct code so your UI can prompt for upgrade rather than "wait until next month."
JWKS (offline token verification)
Score-mode redirect tokens (vh_token) are Ed25519-signed. To verify offline:
GET https://verifyhuman.riwi.com/api/v1/.well-known/jwks.jsonKeys rotate; cache the JWKS for 1 hour and refresh on signature failure. Note the non-standard mount under /api/v1/ rather than the root.
OpenAPI
Full machine-readable spec at /api/v1/openapi.json. Swagger UI at /api/v1/docs. The dashboard's TypeScript types are generated from this spec via npm run generate:types.
SDK
For browser integration, see SDK. The HTML drop-in passes studyId / respondentId via data-study-id / data-respondent-id attributes; React/Next.js components accept studyId / respondentId / metadata as props.