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 the X-API-Key header. 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 the status verdict. Default weights liveness×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.json

Keys 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.