SDK integration contracts
The SDK offers four ways to add it. Each suits a different kind of site, but all four MUST behave the same way for the same input. This page is the reference: choose a way knowing exactly what it gives you, and check the guarantees below if you're adding a fifth.
The four ways
| Way | Import / mount | Best for |
|---|---|---|
| React component | import { VerifyHuman } from '@verifyhuman/sdk/react' | Next.js, Remix or Vite apps that want the React component in their own page. You control everything around the check. |
Managed (VH.mount) | await VH.mount(el, { siteKey }) | Sites without React that still want the ready-made check. The SDK draws and manages it for you (with Preact). |
| HTML snippet | <script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"> + <div data-sitekey="..."> | Static sites, marketing pages, anywhere a build step is too much. The same ready-made check as the managed way, in one script file (UMD, with Preact). |
| Core library | import { VerifyHuman as VHSDK } from '@verifyhuman/sdk' | Node.js and advanced setups that build their own screens. Direct access to verify(), requestChallenge() and so on. Nothing is drawn for you. |
Guarantees — true on every way
The seven guarantees below hold for all four ways. A test (packages/sdk/tests/contract-invariants.test.ts) fails our build if any way stops meeting them. If you see a difference in production that this page doesn't explain, it is a bug — report it to us as urgently as a security issue.
1. Our server's result decides
A check our server rejects reaches you as a failure on every way. How it reaches you differs (a rejected promise, the onError callback, or what verify() returns), but you never get a pass with a token when the server said no.
2. A failure in the browser is still sent
When the check fails in the browser (for example a low camera score or a missed challenge), the SDK still sends it to /public/verify. Our server records it, the dashboard shows it and the failure webhook is sent. A check that never reaches our server is a bug.
3. A specific error code for every rejection
Every rejection by our server raises a VerifyError with a code for the kind of failure — never a plain SERVER_ERROR when the kind is known. See typed errors for how to handle them.
4. Result fields come from our server
VHMountResult.demographics and .glassesDetected are filled from what our server returns on the /public/verify response, NOT from data held in the browser. So the value you read is one our server confirmed; if the server could not read it, the field is left undefined, so you can see the data is missing.
5. Safe clean-up
endSession() calls DELETE /api/v1/public/sessions/{id} whenever a check ends. Our server answers 204 whether the session was active, finished or already cleaned up, so no 404 errors appear in your browser console.
6. The same outage rule on every way
When /public/verify can't be reached (a network error, a time-out, or a 5xx error after retries), the SDK applies the project's outage setting (degradedMode) on every way. The default, FAIL_OPEN_FLAGGED, lets the respondent through with degraded: true; choose FAIL_CLOSED for surveys where letting anyone through unchecked is not acceptable. See when VerifyHuman can't be reached.
7. Built files match the source
The SDK's built files in packages/sdk/dist/ are rebuilt from the source by the prepublishOnly / prepack scripts; postinstall warns when they are out of date, and a test fails our build if they fall behind. This protects projects that use the SDK as a local file: dependency from running older code than the source shows.
Where the four ways differ
These differences are intended — they come from the kind of site each way is for, not from broken guarantees. Read them before switching:
| Behaviour | React component | Managed / HTML snippet | Core library |
|---|---|---|---|
| How you receive the result | onVerify / onError callbacks | await VH.mount(...) resolves withVHMountResult OR throws VerifyError | await sdk.verify(envelope) returns aResult wrapper |
| Token shape on success | JWT string | JWT string on result.token | JWT string on result.verificationToken |
| Check drawn on the page | Yes — you control everything around it | Yes — the SDK draws and manages it | No — you build your own screens |
| What sends the result to our server | The React component (submitVerification) | The SDK itself (VHSession.mountWidget → vh.verify) | You call verify() yourself |
| Projects that return a score | Older handling only | Supported via VHWidgetMountOptions.onResult | You call verify(), then VHSession.getResult(), yourself |
If you're adding a fifth way
Update three places in one change:
- This page. Add a row to the four ways table saying what kind of site the new way is for.
packages/sdk/tests/contract-invariants.test.ts→ENTRY_POINTSconstant. Add the file path of the new way so the test checks it exists, AND extend every guarantee check to cover it.- CHANGELOG. Describe the new way in a new entry, including which guarantees needed new tests.
The guarantee tests are the gate. A new way without matching tests fails the build on its first change, so a difference is found before a customer reports it.