---
icon: browser
label: Web SDK
order: 900
---

# Web SDK

## Overview

The VerifEye Web SDK (`@realeyes/verifeye-sdk`) is a React component library for embedding the VerifEye verification flow directly into your web application. Its single entry point is the [`VerifyVerifier`](#verifyverifier) component, which renders the full client-side experience — camera access, consent, liveness, and image capture — against a verification session you create server-side.

The SDK handles the browser-side capture and the calls to the VerifEye Service. It does **not** create sessions or expose results on its own:

- **Your backend** creates a verification session (using your API key) and returns its `sessionId` and `accessToken` to the browser.
- **The `VerifyVerifier` component** runs the verification using those credentials and invokes `onVerificationCompleted` when the flow finishes.
- **Your backend** fetches the outcome from the [VerifEye Session API](/verifeye-apis/verifeye-session-api/#get-session-result) once the flow completes.

!!!tip
Session creation requires your API key and must happen **server-side only**. Never ship your API key to the browser — the SDK only ever receives a short-lived session `accessToken`.
!!!

---

## Prerequisites

- **React 18+** and **React DOM 18+**
- A **VerifEye account and API key** from the [VerifEye Developer Console](https://verifeye-console.realeyes.ai/).
- A **server-side endpoint** that creates a verification session and returns its `sessionId` and `accessToken` (see [Quick Start](#quick-start)).
- A **secure context (HTTPS)** — browsers only grant camera access over HTTPS or `localhost`.

---

## Installation

```bash
npm install @realeyes/verifeye-sdk
```

---

## Quick Start

### 1. Create a session on your server

Call the VerifEye Service to [create a verification session](/verifeye-apis/verifeye-session-api/#create-session), authenticating with your API key. The response contains the session ID and a short-lived session token.

```ts
// Server-side (Node.js) — never expose your API key to the browser
const res = await fetch(
  "https://verifeye-session-api-eu.realeyes.ai/v1/verification/create-session",
  {
    method: "POST",
    headers: {
      Authorization: `ApiKey ${process.env.VERIFEYE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      verifierConfigs: {
        liveness: { type: "Verification", challengeType: "Balanced" },
        age: { type: "CalculationOnly" },
        gender: { type: "CalculationOnly" },
      },
    }),
  }
);

const { verificationSessionId, sessionToken } = await res.json();
// Return verificationSessionId -> sessionId and sessionToken -> accessToken
// to the browser.
```

### 2. Render the component in your React app

Mount `VerifyVerifier` once the session credentials are available, and react to completion.

```tsx
import { VerifyVerifier } from "@realeyes/verifeye-sdk";

function Verification({ sessionId, accessToken }: Props) {
  return (
    <VerifyVerifier
      sessionId={sessionId}
      accessToken={accessToken}
      region="eu"
      onVerificationCompleted={(sessionId) => {
        // The flow has finished. Fetch the outcome from your backend, which
        // calls the VerifEye Service "Get Session Result" endpoint.
        console.log("Verification completed for", sessionId);
      }}
    />
  );
}
```

!!!tip
`onVerificationCompleted` fires when the flow finishes — it does **not** tell you whether verification passed or failed. Retrieve the result server-side via [Get Session Result](/verifeye-apis/verifeye-session-api/#get-session-result) using the `sessionId`.
!!!

### 3. Read the result on your server

When `onVerificationCompleted` fires, have the browser notify your backend, then fetch the outcome from the VerifEye Service [Get Session Result](/verifeye-apis/verifeye-session-api/#get-session-result) endpoint — again authenticating with your API key, never from the browser.

```ts
// Server-side (Node.js) — never expose your API key to the browser
const res = await fetch(
  "https://verifeye-session-api-eu.realeyes.ai/v1/verification/get-session-result" +
    `?verificationSessionId=${sessionId}`,
  {
    headers: {
      Authorization: `ApiKey ${process.env.VERIFEYE_API_KEY}`,
    },
  }
);

const result = await res.json();
// result.verificationResult -> "passed" | "failed" (null until the flow completes)
// Per-verifier results (e.g. result.livenessVerificationResult) can also be
// "not_executed" when that verifier was disabled for the session.
// Other fields are present only for the verifiers you enabled, e.g.
// result.faceId, result.estimatedAge, result.estimatedGender.

if (result.verificationResult === "passed") {
  // Decide what happens next (e.g. allow sign-up).
}
```

!!!tip
The result returns **all** captured fields regardless of the session's result-parameter configuration, and stays available for **7 days** after the session was created. Fields for verifiers you did not enable are `null`. See [Get Session Result](/verifeye-apis/verifeye-session-api/#get-session-result) for the full response schema.
!!!

---

## API Reference

### `VerifyVerifier`

```ts
const VerifyVerifier: React.FC<VerifyVerifierProps>;
```

The main React component. Render it to run a single verification session. Each session is single-use — to run another verification, create a new session and remount the component (for example with a new React `key`).

#### Props

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `sessionId` | `string` | Yes | The verification session ID returned by your server-side `create-session` call. |
| `accessToken` | `string` | Yes | The short-lived session token returned alongside the session ID. |
| `region` | [`Region`](#region) | Yes | The VerifEye region the session belongs to — `"eu"` or `"us"`. Must match the region your backend created the session in. |
| `onVerificationCompleted` | `(sessionId: string) => void` | Yes | Called when the verification flow finishes (regardless of pass/fail). Receives the `sessionId`; use it to fetch the result server-side. |
| `onServiceError` | `(operation: `[`VerifyServiceOperation`](#verifyserviceoperation)`) => void` | No | Called when a backend operation fails. Receives the operation that was in progress when the error occurred. |
| `consentSkipMode` | [`ConsentSkipMode`](#consentskipmode) | No | Controls whether the camera-consent screen is skipped. When omitted, the consent screen is always shown. |
| `headless` | `boolean` | No | When `true`, runs the verification without the built-in full-screen UI. See [Headless verification](#headless-verification). Defaults to `false`. |
| `headlessCallbacks` | [`HeadlessCallbacks`](#headlesscallbacks) | No | Lifecycle hooks for headless mode. Ignored unless `headless` is `true`. |
| `mediaStream` | [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream) | No | Headless only. A camera stream you supply for the SDK to capture from instead of opening the camera itself — useful for running repeated headless checks against one long-lived stream. Passing it without `headless` throws; it is ignored for liveness sessions. See [Reusing a camera stream](#reusing-a-camera-stream). |
| `showVerificationSessionId` | `boolean` | No | When `true`, displays the verification session ID in the UI (useful for debugging and support). Defaults to `false`. |
| `lang` | `string` | No | Language tag for the SDK's built-in UI text (e.g. `"en"`, `"de"`, `"pt-BR"`). Case-insensitive; unsupported values fall back to `"en"`. Defaults to `"en"`. See [Localization](#localization). |
| `langOverrides` | [`VerifyTextOverrides`](#verifytextoverrides) | No | Per-key overrides applied on top of the selected `lang`. Only the keys you pass are replaced; everything else comes from `lang`. See [Localization](#localization). |
| `createConfig` | `(region: `[`Region`](#region)`) => `[`Config`](#config) | No | Advanced override that supplies a custom [`Config`](#config) controlling which environment and API base URL the SDK targets. When omitted, the SDK targets the production VerifEye Service for the selected `region`. |

---

### Supporting types

#### `Region`

```ts
type Region = "eu" | "us";
```

The supported VerifEye regions.

#### `ConsentSkipMode`

Controls whether the camera-consent screen is shown before capture.

| Member | Value | Meaning |
|--------|-------|---------|
| `ConsentSkipMode.SkipIfCameraGranted` | `"1"` | Skip the consent screen only if camera permission has already been granted. |
| `ConsentSkipMode.AlwaysSkip` | `"2"` | Always skip the consent screen. |

When the `consentSkipMode` prop is omitted, the consent screen is always shown. See [Consent handling](#consent-handling) for details on when it is safe to skip the built-in consent screen.

#### `VerifyServiceOperation`

```ts
type VerifyServiceOperation =
  | "init-session"
  | "capture-image"
  | "verify"
  | "unknown";
```

Identifies which backend operation was in progress when `onServiceError` was invoked.

#### `HeadlessCallbacks`

Lifecycle hooks used in [headless mode](#headless-verification). All are optional.

| Callback | Type | Description |
|----------|------|-------------|
| `onVerificationStarting` | `() => void` | Called just before the verification begins. |
| `onBeforeCameraAccess` | `() => void` | Called immediately before the SDK requests camera access. |
| `onAfterCameraAccess` | `() => void` | Called once camera access has been resolved. |

#### `Config`

Returned by `createConfig` to control which VerifEye environment the SDK communicates with.

```ts
interface Config {
  getCurrentEnvironment(): Environment;
  getApiBaseUrl(): string;
}
```

| Method | Returns | Description |
|--------|---------|-------------|
| `getCurrentEnvironment()` | [`Environment`](#environment) | The environment the SDK should operate against. Client applications must always return `"production"`. |
| `getApiBaseUrl()` | `string` | The base URL of the VerifEye Service the SDK calls. |

!!!warning
You only need a custom `Config` for advanced scenarios. When `createConfig` is omitted, the SDK targets the production VerifEye Service automatically. If you do supply one, `getCurrentEnvironment()` must return `"production"` — the other environments are reserved for internal SDK testing (see [`Environment`](#environment)).
!!!

#### `Environment`

```ts
type Environment = "production" | "development" | "local";
```

Identifies which VerifEye environment a [`Config`](#config) targets.

| Member | Meaning |
|--------|---------|
| `"production"` | The live VerifEye Service. **This is the only value client applications should use** — always return it from [`getCurrentEnvironment()`](#config). |
| `"development"` | An internal pre-production environment used **only for testing the SDK itself**. Not intended for client applications. |
| `"local"` | A developer's local environment used **only when working on the SDK itself**. Not intended for client applications. |

#### `VerifyTextOverrides`

```ts
type VerifyTextOverrides = Partial<Record<VerifyTextKey, string>>;
```

A flat map of per-key text overrides for the [`langOverrides`](#props) prop. [`VerifyTextKey`](#verifytextkey) is the union of every text key the SDK renders, so you supply only the keys you want to change — the rest come from the selected [`lang`](#localization). See [Localization](#localization).

#### `VerifyTextKey`

```ts
type VerifyTextKey = "consent_title" | "consent_continue_button" | /* ...all text keys */;
```

The union of every text key the SDK renders — the valid keys for [`VerifyTextOverrides`](#verifytextoverrides). The full list is available through your editor's autocomplete; keys are grouped by dialog with an underscore prefix (e.g. `consent_*`, `no_face_*`, `liveness_*`).

The SDK also exports the runtime constants `SUPPORTED_LANGUAGES` (the array of shipped language tags) and `DEFAULT_LANGUAGE` (`"en"`), so you can build a language picker without hard-coding the list.

---

## Consent handling

The `VerifyVerifier` flow accesses the user's camera and processes facial (biometric) data to perform liveness and identity verification. Captured images are used solely to carry out the verification — the SDK does not store them or expose raw biometric data to your application.

### Obtaining consent is your responsibility

The hosting application is responsible for obtaining valid, informed user consent for camera access and biometric processing before a verification runs, and for meeting the requirements of the privacy and biometric-data regulations that apply to your users (e.g. GDPR, BIPA, CCPA). See the [Realeyes Privacy Policy](https://realeyes.ai/privacy-policy/).

### Built-in consent screen

By default, the SDK shows its own consent screen before it requests camera access, so a baseline consent step is always present out of the box. This behaviour is controlled by the [`consentSkipMode`](#consentskipmode) prop:

| `consentSkipMode` | Behaviour |
|-------------------|-----------|
| Omitted (default) | The consent screen is always shown. |
| `SkipIfCameraGranted` | The consent screen is skipped only if camera permission has already been granted. |
| `AlwaysSkip` | The consent screen is never shown. |

!!!warning
Only disable the built-in consent screen (`SkipIfCameraGranted` or `AlwaysSkip`) when your application already obtains equivalent, legally valid consent for camera access and biometric processing before mounting `VerifyVerifier`. If your application does not handle consent itself, leave the built-in consent screen enabled.
!!!

---

## Localization

The SDK's built-in UI text — dialog titles, buttons, hints, etc. — is driven by a locale file and can be translated or overridden per key. Set the [`lang`](#props) prop to translate the whole flow, and use [`langOverrides`](#props) to change individual strings.

### Selecting a language

```tsx
<VerifyVerifier
  sessionId={sessionId}
  accessToken={accessToken}
  region="eu"
  lang="de"
  onVerificationCompleted={handleVerificationCompleted}
/>
```

`lang` accepts a language tag such as `"en"`, `"de"`, or `"pt-BR"`. Values are case-insensitive, and `_` is accepted in place of `-`. A tag is resolved by exact match first, then by a known alias, then by its base language — so `"de-AT"` resolves to `de`, `"zh-TW"` to `zh-Hant`, `"zh-CN"` to `zh-Hans`, `"pt"` to `pt-PT`, and `"no"` to `nb`. An unsupported value logs a warning and falls back to `"en"` rather than failing the flow.

#### Supported languages

`en` `bg` `cs` `da` `de` `el` `es` `et` `fi` `fr` `hu` `id` `it` `ja` `ko` `lt` `lv` `nb` `nl` `pl` `pt-BR` `pt-PT` `ro` `ru` `sk` `sl` `sv` `tr` `uk` `zh-Hans` `zh-Hant`

The same list is exported at runtime as `SUPPORTED_LANGUAGES`, with `DEFAULT_LANGUAGE` (`"en"`) alongside it.

### Overriding individual strings

Use `langOverrides` to replace specific strings on top of the selected language — for example to match your product's wording or brand name. Only the keys you pass are replaced; every other string still comes from `lang`.

```tsx
<VerifyVerifier
  sessionId={sessionId}
  accessToken={accessToken}
  region="eu"
  lang="de"
  langOverrides={{
    consent_title: "Kurze Überprüfung",
    consent_continue_button: "Los geht's",
  }}
  onVerificationCompleted={handleVerificationCompleted}
/>
```

See [Translation reference](/verifeye-sdks/web-sdk/translation-reference/) for the full list of keys and their default English text.

---

## Usage examples

The following examples mirror how the component is used in the VerifEye Demo application.

### Standard verification

The default, interactive flow — the SDK renders its own full-screen UI and drives the user through consent, liveness, and capture.

```tsx
import { VerifyVerifier } from "@realeyes/verifeye-sdk";

<VerifyVerifier
  sessionId={verifySessionId}
  accessToken={verifyAccessToken}
  region="eu"
  onVerificationCompleted={handleVerificationCompleted}
  showVerificationSessionId={false}
/>;
```

### Localized verification

Translate the flow by setting `lang`, and adjust individual strings with `langOverrides`. See [Localization](#localization) for the full behaviour and supported languages.

```tsx
import { VerifyVerifier } from "@realeyes/verifeye-sdk";

<VerifyVerifier
  sessionId={verifySessionId}
  accessToken={verifyAccessToken}
  region="eu"
  lang="fr"
  langOverrides={{ consent_continue_button: "Commencer" }}
  onVerificationCompleted={handleVerificationCompleted}
/>;
```

### Headless verification

Set `headless` to run a verification without the built-in UI — for example, to silently re-verify a user while they keep using your application. Provide `headlessCallbacks` to hook into the lifecycle, and remount the component with a fresh `key` and session for each verification cycle.

```tsx
<VerifyVerifier
  key={session.key}
  headless
  sessionId={session.sessionId}
  accessToken={session.accessToken}
  region="eu"
  onVerificationCompleted={() => onCompleted(session.sessionId)}
  onServiceError={() => onError()}
/>
```

#### Reusing a camera stream

By default each headless check opens the camera itself (via `getUserMedia`) and releases it when the check finishes. When you run checks back-to-back on a short interval, repeatedly acquiring and releasing the camera is wasteful and can make the browser's camera indicator flicker.

Pass a `mediaStream` to have the SDK capture from a stream **you** own instead of opening the camera itself. Acquire one long-lived stream, keep it open across cycles, and hand the same stream to each `VerifyVerifier` mount:

```tsx
// Acquire once and keep it open for as long as you run checks.
const stream = await navigator.mediaDevices.getUserMedia({ video: true });

<VerifyVerifier
  key={session.key}
  headless
  sessionId={session.sessionId}
  accessToken={session.accessToken}
  region="eu"
  mediaStream={stream}
  onVerificationCompleted={() => onCompleted(session.sessionId)}
  onServiceError={() => onError()}
/>;

// When you are done running checks, stop the tracks yourself.
stream.getTracks().forEach((track) => track.stop());
```

Keep the following in mind:

- **Headless only.** Passing `mediaStream` without `headless` throws — the interactive flow manages the camera itself.
- **You own the stream's lifecycle.** The SDK captures from your stream but never stops its tracks. Stop them yourself once you have finished running checks.
- **Automatic fallback.** If the supplied stream has no live video track (for example the camera was unplugged or its permission was revoked), the SDK falls back to opening the camera itself for that check.

### Matching a returning user (Match modes)

Face recognition's match modes (`MatchVerification`, `UniqueMatchVerification`, `MatchOnlyVerification`) perform a **1:1 match** against the face stored under your own identifier for the user — for example to re-authenticate a known account instead of sending an SMS/email code. See [Verifier choice](/redirect/user-lifecycle/#verifier-choice-comparing-the-match-modes) for how the three modes differ.

The `VerifyVerifier` usage is unchanged — the match mode is configured entirely **server-side** when the session is created. Two things matter:

- Set `verifierConfigs.faceRecognition.type` to the match mode you need.
- Set `externalId` to your stable identifier for the user (e.g. your user ID). The match modes **require** it — it selects which enrolled face to match against. Without it, face recognition cannot run and the session fails.

```ts
// Server-side (Node.js) — create the session with a match-mode config
const res = await fetch(
  "https://verifeye-session-api-eu.realeyes.ai/v1/verification/create-session",
  {
    method: "POST",
    headers: {
      Authorization: `ApiKey ${process.env.VERIFEYE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      // Your stable identifier for this user — required by the match modes.
      externalId: "user-123",
      verifierConfigs: {
        liveness: { type: "Verification", challengeType: "Balanced" },
        faceRecognition: {
          // MatchVerification: enrolls the face on the user's first visit,
          //   then 1:1-matches on every return visit.
          // UniqueMatchVerification: like MatchVerification, but also fails if
          //   the same face is already enrolled under a different externalId.
          // MatchOnlyVerification: never enrolls — passes only if a face was
          //   already registered under this externalId (fail closed).
          type: "MatchVerification",
          collectionId: "my-collection",
        },
      },
    }),
  }
);

const { verificationSessionId, sessionToken } = await res.json();
```

The browser then runs `VerifyVerifier` exactly as in [Standard verification](#standard-verification). Afterwards, read the outcome server-side:

```ts
// Server-side — after onVerificationCompleted fires
const result = await getSessionResult(sessionId); // see "Read the result on your server"

if (result.faceRecognitionResult === "passed") {
  // Same person as the face enrolled under externalId "user-123".
}
```

!!!tip
`MatchOnlyVerification` is for flows where enrollment happens in a separate, controlled step (for example via [Onboard](/verifeye-apis/verifeye-face-api/#onboard) on the VerifEye Face API, paired with an external key): unknown users **fail** instead of being silently enrolled. With `MatchVerification` and `UniqueMatchVerification`, the first session for a new `externalId` enrolls the face automatically — there is no separate enrollment endpoint.
!!!

### Handling service errors

Use `onServiceError` to react to backend failures and inspect which operation failed.

```tsx
<VerifyVerifier
  sessionId={sessionId}
  accessToken={accessToken}
  region="eu"
  onVerificationCompleted={handleVerificationCompleted}
  onServiceError={(operation) => {
    // operation: "init-session" | "capture-image" | "verify" | "unknown"
    console.error(`Verification failed during: ${operation}`);
  }}
/>;
```

---

## Next Steps

- [Web SDK Use Cases](/verifeye-sdks/web-sdk/use-cases/) — common patterns for embedding the VerifEye verification flow into your web application.
- [VerifEye Session API](/verifeye-apis/verifeye-session-api/) — manage verification configurations and retrieve session results server-side.
- [Authentication](/verifeye-apis/authentication/) — API key and bearer token authentication for server-side calls.

---

*Last updated: 2026-08-07*
