> ## Documentation Index
> Fetch the complete documentation index at: https://doc.youverify.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Web

> Add anti-deepfake liveness detection to a browser-based application with the Youverify Liveness Web SDK.

The Youverify Liveness Web SDK adds anti-deepfake liveness detection to your web application without redirecting users to a hosted page. Users complete one or more short tasks, such as tracing a circle with their head, and the SDK returns the result to your callbacks.

## Installation

> [tab] npm
>
> ```bash theme={null}
> npm install youverify-liveness-web
> ```

> [tab] Yarn
>
> ```bash theme={null}
> yarn add youverify-liveness-web
> ```

> [tab] CDN (jsDelivr)
>
> ```html theme={null}
> <script src="https://cdn.jsdelivr.net/npm/youverify-liveness-web/dist/index.js"></script>
> ```

> [tab] CDN (UNPKG)
>
> ```html theme={null}
> <!-- Latest version -->
> <script src="https://unpkg.com/youverify-liveness-web/dist/index.js"></script>
>
> <!-- Pinned version -->
> <script src="https://unpkg.com/youverify-liveness-web@1.2.3/dist/index.js"></script>
> ```

## Integration steps

**1. Generate a session ID and a session token on your backend**

The SDK doesn't generate session tokens itself. Your backend calls the Youverify API with your secret API key and passes both values to the page. See [Generate the session ID and session token](#generate-the-session-id-and-session-token).

**2. Import and initialize the SDK**

```js theme={null}
import YouverifyLiveness from "youverify-liveness-web";

const yvLiveness = new YouverifyLiveness(options);
```

For the valid `options`, see [Options](#options).

**3. Start the liveness check**

```js theme={null}
yvLiveness.start();
```

You can also pass an array of tasks. Tasks passed to `start()` override the tasks you set during initialization.

```js theme={null}
const tasks = [{ id: "complete-the-circle" }];
yvLiveness.start(tasks);
```

## Generate the session ID and session token

Both calls must come from your backend. Use the base URL for the environment you are testing against.

| Environment | Base URL |
| - | - |
| Sandbox | `https://api.sandbox.youverify.co` |
| Live | `https://api.youverify.co` |

> [!WARNING]
>
> Never expose your API key in frontend code. Make these calls from your secure backend.

### Session ID

**`POST /v2/api/identity/sdk/session/generate`**

```json Headers theme={null}
{
  "Content-Type": "application/json",
  "Token": "YOUR_API_KEY"
}
```

```json Request body theme={null}
{
  "publicMerchantID": "your_public_merchant_id",
  "metadata": {}
}
```

```json Response theme={null}
{
  "sessionId": "generated_session_id_here"
}
```

Pass the `sessionId` to the SDK constructor.

### Session token

**`POST /v2/api/identity/sdk/liveness/token`**

```json Headers theme={null}
{
  "Content-Type": "application/json",
  "Token": "YOUR_API_KEY"
}
```

```json Request body theme={null}
{
  "publicMerchantID": "your_public_merchant_id",
  "deviceCorrelationId": "unique_device_identifier"
}
```

```json Response theme={null}
{
  "authToken": "generated_auth_token_here"
}
```

Pass the `authToken` to the SDK constructor as `sessionToken`. The `deviceCorrelationId` should be a unique identifier for the user's device or session. It helps track and prevent abuse.

## Full example

```js theme={null}
import YouverifyLiveness from "youverify-liveness-web";

try {
  const yvLiveness = new YouverifyLiveness({
    sessionId: sessionId,        // from your backend
    sessionToken: sessionToken,  // from your backend
    sandboxEnvironment: true,    // set to false for production
    tasks: [{ id: "complete-the-circle" }],
    onSuccess: (data) => {
      console.log("Liveness check passed:", data);
    },
    onFailure: (data) => {
      console.log("Liveness check failed:", data);

      if (data.error?.key === "invalid_or_expired_session") {
        // Generate a new session and retry
      } else if (data.error?.key === "session_token_error") {
        // Generate a new session token and retry
      }
    },
  });

  yvLiveness.start();
} catch (error) {
  // Handle validation errors
  console.log(`Something isn't right, ${error}`);
}
```

## Preloading

The SDK downloads its face detection model, several MB, before the liveness check can begin. The download doesn't depend on the session, so start it early, before you generate the `sessionId` and `sessionToken`.

```js theme={null}
import YouverifyLiveness from "youverify-liveness-web";

YouverifyLiveness.preload();
```

* It's safe to call more than once. Later calls, including the one the constructor makes, reuse the download already in progress.
* If you don't call it, the download starts when the SDK is constructed.
* `preload()` keeps the detector in memory, so it only carries over to the liveness check if the page isn't reloaded in between. That's the case in single-page apps that navigate with their router.

### Single-page apps

Call `preload()` in your app's entry module.

| Framework | Where to call `preload()` |
| - | - |
| React / Preact (Vite, CRA) | `main.tsx` / `index.tsx`, before `render` |
| Vue | `main.ts`, before `createApp(...).mount()` |
| Angular | `main.ts`, before `bootstrapApplication` |
| Next.js | A client component in the root layout (or `_app`), inside `useEffect` |

```js theme={null}
// main.tsx
import YouverifyLiveness from "youverify-liveness-web";

YouverifyLiveness.preload();

createRoot(document.getElementById("root")).render(<App />);
```

A reload cancels any download still in progress. Files that already finished come from the browser's HTTP cache, and anything that was cut off downloads again as soon as the entry module runs.

### Multi-page sites

If users reach the liveness check through full page loads, the preloaded detector is discarded on every navigation. Call `prefetch()` on the pages before the liveness check instead.

```js theme={null}
YouverifyLiveness.prefetch();
```

It asks the browser to download the detector files into its HTTP cache in the background, without building the detector. When the SDK starts on the liveness page, the files come from the cache. The detector is still set up on that page, so this saves download time but not setup time.

* It's safe to call on every page. Files already requested aren't added again.
* It relies on the browser's HTTP cache. It has no effect if caching is disabled, for example in a webview configured with `LOAD_NO_CACHE`.
* `<link rel="prefetch">` is supported in Chromium-based browsers and Firefox, but not in Safari, where `prefetch()` does nothing.

| Your app | Call | Result on the liveness page |
| - | - | - |
| Single-page app | `preload()` once in the app entry module | Detector ready, if it finished loading before the user got there |
| Multi-page site | `prefetch()` on earlier pages | Files come from cache; detector still set up there |

## Options

**sessionId** `string` *required*

ID generated by your backend using your API key. The SDK validates it before initialization and attaches it to submissions.

**sessionToken** `string` *required*

Token generated by your backend for liveness verification.

**publicKey** `string`

Your Youverify public merchant key.

**sandboxEnvironment** `boolean` (default: true)

Whether the session runs in sandbox or live mode. Set to `false` for production.

**presentation** `string` (default: modal)

How the UI is displayed. One of `modal` or `page`.

**tasks** `array`

The tasks the user must perform to confirm liveness. See [Tasks](#tasks).

**entityId** `string`

Optional entity identifier to include when the liveness result is submitted.

**allowAudio** `boolean` (default: false)

Whether to narrate information to the user during tasks.

**user** `object`

Details of the user being checked.

**properties**

**firstName** `string` *required*

First name of the user.

**lastName** `string`

Last name of the user.

**email** `string`

Email of the user.

**branding** `object`

Customizes the UI to fit your brand.

**properties**

**name** `string`

The name of the brand.

**color** `string`

Your branding color, as a valid hex or RGB string.

**logo** `string`

A link to your logo image.

**logoAlt** `string` (default: Youverify)

Alternative text for the brand logo.

**hideLogoOnMobile** `boolean` (default: false)

Hides the logo in a mobile webview.

**showPoweredBy** `boolean` (default: false)

Shows the "Powered by" footer text.

**poweredByText** `string` (default: Powered by)

Customizes the "Powered by" text.

**poweredByLogo** `string`

A link to a custom "Powered by" logo image.

**onClose** `function`

Called when the modal is closed.

**onSuccess** `function`

Called when all tasks have been completed and passed. Receives the [callback data](#callback-data).

**onFailure** `function`

Called when at least one task fails. Receives the [callback data](#callback-data).

## Tasks

A task is a series of instructions the user follows to confirm liveness. Pass tasks in the `tasks` option or to `start()`. Every task takes these options:

| Option | Type | Required | Description | Default |
| - | - | - | - | - |
| `id` | String | Yes | ID of the task. See the sections below. | - |
| `difficulty` | String | No | `easy`, `medium` or `hard` | `medium` |
| `timeout` | Number | No | Time in milliseconds after which the task fails automatically | undefined |

### Complete the Circle

The user completes an imaginary circle with head movement. Use `id: "complete-the-circle"`.

### Yes or No

The user answers questions you set by tilting their head: right for yes, left for no. Use `id: "yes-or-no"`.

| Option | Type | Required | Description |
| - | - | - | - |
| `questions` | Array | No | The questions to ask the user |
| `questions.question` | String | Yes | A true-or-false question, for example "Are you ready" |
| `questions.answer` | Boolean | Yes | The correct answer, `true` or `false` |
| `questions.errorMessage` | String | No | Message shown if the user answers wrongly |

### Motions

The user performs random motions in random sequences: nodding, blinking and opening the mouth. Use `id: "motions"`.

| Option | Type | Required | Description | Default |
| - | - | - | - | - |
| `maxNods` | Number | No | Maximum number of nods the user is asked to perform | 5 |
| `maxBlinks` | Number | No | Maximum number of blinks the user is asked to perform | 5 |

### Blink

The user blinks a set number of times. Use `id: "blink"`.

| Option | Type | Required | Description | Default |
| - | - | - | - | - |
| `maxBlinks` | Number | No | Maximum number of blinks the user is asked to perform | 5 |

## Callback data

`onSuccess` and `onFailure` receive an object with these properties.

**data.passed** `boolean`

Whether the liveness check passed.

**data.faceImage** `string`

Face image of the user performing the check.

**data.livenessClip** `string`

Video of the user performing the check.

**data.metadata** `any`

Metadata passed in during initialization.

**data.error** `object`

Present only on failure. Contains `key` (`eyes_closed`, `image_capture_failed` or `API_ERROR`) and `message`.

When the SDK submits the liveness payload, it includes `sdkVersion` (the SDK package version, `1.2.3`) and, if you passed it during initialization, `entityId`.

## Errors and retries

| Error key | Meaning |
| - | - |
| `invalid_or_expired_session` | The `sessionId` is invalid or expired |
| `session_token_error` | There is a problem with the `sessionToken` during liveness verification |

* If session validation fails, generate a new `sessionId` and retry.
* If liveness fails, the user can retry while the current `sessionId` remains valid.
* If the `sessionId` expires, create a new session and restart the process.

> [!NOTE]
>
> Use `sandboxEnvironment: true` for testing and `sandboxEnvironment: false` for production.

For the token endpoints shared by all SDKs, see [Session tokens](https://doc.youverify.co/sdks/liveness-sdk.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.