SDK

Embed the verify widget on a page. Five integration paths, identical verification behavior — pick what fits your stack. All paths are server-integrated as of v0.5.0 (2026-05-19) — every path produces a server-signed token.

Install

npm: npm install @verifyhuman/sdk (current version 0.5.0).
CDN: <script src="https://verifyhuman.riwi.com/sdk/v1/verifyhuman.umd.js" async defer></script>

Integration paths

1. HTML drop-in (auto-init)

Zero JS. The script auto-discovers elements with data-sitekey on page load and mounts a server-integrated widget. Use data-study-id / data-respondent-id / data-metadata for cross-reference; the values echo back on token / webhook responses.

<script src="https://verifyhuman.riwi.com/sdk/v1/verifyhuman.umd.js" async defer></script>

<div
  data-sitekey="YOUR_SITE_KEY"
  data-study-id="YOUR_STUDY_ID"
  data-respondent-id="YOUR_RESPONDENT_ID"
  data-metadata='{"panel":"prolific"}'
  data-theme="light"
  data-callback="onVerify"
></div>

<script>
  function onVerify(result) {
    // result.sessionId, result.token, result.scores, ...
    fetch('/your-app/verified', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        sessionId: result.sessionId,
        token: result.token,
      }),
    });
  }
</script>

2. HTML + JS API

Programmatic mount when the auto-init element isn't present in your DOM at load time. Use window.VerifyHuman.VH.mount(...) from the CDN bundle.

<div id="vh-container"></div>

<script>
  window.VerifyHuman.VH.mount(
    document.getElementById('vh-container'),
    {
      siteKey: 'YOUR_SITE_KEY',
      studyId: 'YOUR_STUDY_ID',
      respondentId: 'YOUR_RESPONDENT_ID',
      metadata: { panel: 'prolific' },
    }
  ).then((result) => {
    console.log('verified', result.sessionId, result.token);
  });
</script>

3. React

import { VerifyHuman } from '@verifyhuman/sdk/react';

function MyForm() {
  const handleVerify = (token, result) => {
    fetch('/api/verify', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token }),
    });
  };

  return (
    <VerifyHuman
      siteKey="YOUR_SITE_KEY"
      studyId="YOUR_STUDY_ID"
      respondentId="YOUR_RESPONDENT_ID"
      metadata={{ panel: 'prolific' }}
      widgetMode="standard"       // 'invisible' | 'captcha' | 'standard'
      onVerify={handleVerify}
      onError={(err) => console.error(err)}
    />
  );
}

4. Next.js

Same React component with the 'use client' directive at the top of the file. Works with the App Router and the Pages Router.

5. Vanilla TypeScript

import { VH } from '@verifyhuman/sdk';

const result = await VH.mount('#verify-container', {
  siteKey: 'YOUR_SITE_KEY',
  studyId: 'YOUR_STUDY_ID',
  respondentId: 'YOUR_RESPONDENT_ID',
});

// result.sessionId, result.token, result.scores, ...

Widget modes

Set on the project server-side (dashboard → project → Settings) — not in the SDK. The SDK reads the configured mode from /projects/{siteKey}/public-config at mount time.

  • Invisible — no UI, behavioral + device signals only. Escalates to camera challenge if signals are weak.
  • CAPTCHA — one-click verify, escalates to camera only when needed.
  • Standard — full camera-driven flow with liveness checks. Strongest signal.

Dispatch modes

  • BLOCK — the widget gates pass / fail locally; your onVerify callback gets the JWT and you forward it to your backend for /token/verify. Default behavior.
  • SCORE — callback — non-blocking; the SDK fetches a signed score envelope from /public/sessions/{id}/result and fires your onResult. The envelope carries scores, studyId, respondentId, metadata, failureReasons.
  • SCORE — redirect — server-signed redirect URL handoff. Validate the embedded vh_token client-side via JWKS for offline verification.
  • SCORE — visual handoff — replaces the widget with a cdn.verifyhuman.io/handoff iframe. Used when you want VerifyHuman's UI to show the verdict, not your own.

Server-side token verification

Whichever embed path you pick, validate the JWT server-side before granting access to your protected content. Pattern:

POST https://verifyhuman.riwi.com/api/v1/token/verify
X-API-Key: YOUR_SECRET_KEY
Content-Type: application/json

{ "token": "TOKEN_FROM_CLIENT" }

# →
{
  "success": true,
  "sessionId": "sess_abc123",
  "status": "pass",
  "scores": { "liveness": 0.95, "uniqueness": 0.98, ... },
  "studyId": "study_001",
  "respondentId": "resp_456"
}

Batch up to 100 tokens via POST /token/verify-batch — see the API reference.

For project-specific snippets

Sign in to the dashboard, open your project, click Integrate. The page renders snippets pre-filled with your site key, allowed origins, and your project's configured widget mode.

Configuration nuances

For project-level thresholds, retry / mode escalation, the SCORE envelope shape, and custom branding, see the configuration guide in the public repo.