---
title: Web App Integration
description: How to integrate the Web & SDK contract on the client side — redirect flows and iFrame SDK setup.
canonical: https://developer.unico.io/dual-api/developers/sdks-and-tools/web/web-integration/
locale: en
generated_by: markdown-export
---

This page describes how the Unico journeys work and the available integration models for embedding
them into an application.

A **journey** is the set of steps the user goes through to complete an identity verification. For
example: capturing a document photo and performing a facial capture (liveness).

Unico manages the entire experience. The integration effort is minimal: the journey is created via
**CreateProcess**, the user is directed to it, and the result is received at the end. Everything
in between — screens, instructions, validations — is already built and maintained by Unico.

:::tip[Choosing your integration approach]

- **Web SDK** (`unico-webframe` package) — use when your back-end already controls the identity
  verification flow and only needs the client-side capture component. Returns `base64` + encrypted
  JWT directly to your callback; you manage the API calls.
- **Web App Integration** (`idpay-b2b-sdk` package) — use when you want Unico to orchestrate the
  full journey (multi-step flows, document capture + liveness). The
  `idpay-b2b-sdk` package powers the embedded **Journeys SDK** (iFrame) model; the **Direct access**
  (redirect) model needs no library.
  :::

## Two integration models

Each client has different needs. Unico offers **two models** for directing the user to the journey.

| Model             | Best for                                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| **Direct access** | Mobile applications that already use WebView, or web flows where the journey can happen outside the main page |
| **Journeys SDK**  | Web applications that need an integrated, seamless experience, keeping the user within the same environment   |

### Direct access

The user is **redirected to a Unico-hosted link**, where the journey takes place. Upon completion,
they are returned to the URL defined during process creation (the `callbackUri` parameter).

This is the simplest approach to adopt: it requires no library installation and works well when the
journey does not need to happen inside the application's own page. On the other hand, taking the
user outside the client's environment tends to introduce more friction and, consequently, a higher
drop-off rate.

After creating a process, the API response includes the URL of the Unico-hosted journey. There are
two common ways to direct the user to it:

- **Standard redirect.** The user is redirected directly to the journey URL. Upon completion,
  Unico redirects them back to the `callbackUri` defined during process creation.
- **New tab with `window.open()`.** The journey is opened in a new browser tab, keeping the user
  in a separate context. In this case, it is recommended to monitor for a URL change to the
  `callbackUri` and close the tab once the process is complete. See the
  [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) for details
  on the API.

```mermaid
sequenceDiagram
    participant Unico as Unico backend service
    participant Backend as Customer backend server
    participant Frontend as Customer frontend application
    participant Hosted as Unico hosted frontend application

    Frontend->>Backend: start kyc flow
    Backend->>Unico: call createProcess
    Unico->>Backend: return Unico process info
    Backend->>Frontend: return Unico process info
    Frontend->>Hosted: redirect user to process url
    Note over Hosted: User journey
    Hosted->>Frontend: redirect user to callback url

    critical Get process result
        Frontend->>Backend: signals that journey was finished
        Backend->>Unico: call getProcess
        opt Webhook
            Note over Backend: In addition to getProcess, you can use the Webhook implementation to ensure a fallback in obtaining the process result.
        end
    end
```

In mobile applications, it is common to use a **WebView** to open the journey directly, with no
additional redirect needed. In this case, the `callbackUri` also accepts a **deeplink**, allowing
the journey's completion to trigger the opening of a specific screen in the native application.
Simply configure the deeplink as the return destination and the operating system handles routing
the user to the right place.

```mermaid
sequenceDiagram
    participant Unico as Unico backend service
    participant Backend as Customer backend server
    participant App as Customer mobile application
    participant Hosted as Unico hosted frontend application

    App->>Backend: start kyc flow
    Backend->>Unico: call createProcess
    Unico->>Backend: return Unico process info
    Backend->>App: return Unico process info
    App->>Hosted: open the process url in a webview
    Note over Hosted: User journey
    Hosted->>App: redirect user to the deeplink (callback url)

    critical Get process result
        App->>Backend: signals that journey was finished
        Backend->>Unico: call getProcess
        opt Webhook
            Note over Backend: In addition to getProcess, you can use the Webhook implementation to ensure a fallback in obtaining the process result.
        end
    end
```

### Journeys SDK

The journey takes place **inside the application itself**, without taking the user out of their
context. The **Journeys SDK** is installed in the application and used to open the journey when
needed.

This is the recommended path for a more integrated and seamless experience, keeping the user in
the same environment throughout, which tends to reduce friction and drop-off across the flow.

Unico provides a JavaScript library compatible with modern browsers, allowing the journey
to be integrated into virtually any application with just a few lines of code.

#### Compatibility

The library is designed to fit into any project without friction, regardless of the stack in use:

- **Any web application.** Distributed in UMD format, it works when imported through modern bundlers (such as webpack or Vite). Compatible
  with any framework (React, Angular, Vue) or plain JavaScript.
- **Modern browsers.** The library already includes the necessary polyfills for features like
  Promises and `async/await`, extending compatibility to older browser versions as well.
- **Standard web APIs.** The journey runs on native browser capabilities, with no dependency on
  plugins or external libraries in the project.

#### How the SDK works internally

When a journey is opened, the SDK inserts an iFrame into the page and takes control of the
entire visual experience from that point on. The screens, scripts, and assets for each step run
inside this iFrame, from the moment the user begins through to the completion of the process.

This architectural decision is intentional: iFrame isolation ensures the Unico journey does not
interfere with the application's styles or behavior. No script leaks into the external context, no
CSS rule collides with the application's own styles. The result is a consistent experience for the
end user and minimal impact on the client's product.

Since Unico is responsible for creating and managing the iFrame, journey improvements — whether in
performance, experience, or validation — are delivered automatically to all users, with no changes
required in the integrated application. The integration will always run with the best available
optimizations, without needing to track or react to every platform update.

#### Getting started

****Step 1** — Installation**

The `idpay-b2b-sdk` package is shared between IDPay payment journeys and identity verification
journeys. For identity use cases, import the `ByUnicoSDK` class as shown in the steps below.

```bash
  npm install idpay-b2b-sdk
```

The recommended way to install the Journeys SDK is through a package manager such as **npm** or
**yarn**, from the package available on the **npm registry**. In addition to simplifying
installation and dependency management, this approach provides clear control over the version in
use and makes it straightforward to update whenever a new version is published.

The SDK follows **Semantic Versioning (SemVer)**, meaning patch and minor updates do not introduce
breaking changes. It is safe to configure the project to receive these updates automatically.
Changes that may require integration updates are reserved for major versions and always come with
a migration guide.

:::tip[Keeping the SDK up to date]
Staying on the latest version is especially important for two reasons. The first is **security**:
security patches are published whenever vulnerabilities are identified or opportunities to
strengthen the communication protocol arise. Running an outdated version means missing these fixes
and exposing the flow to unnecessary risk. The second is **stability**: bug fixes are distributed
the same way, and older versions may exhibit behaviors that have already been resolved in more
recent releases.
:::

Before getting started, register your domains with the Unico support team. All domains must use
HTTPS.

****Step 2** — Call `init(options)`**

Initializes the SDK and pre-loads the scripts necessary for the journey to function correctly,
creating a smoother experience for the end user. Call this as early as possible in the flow.

| Parameter | Required | Description                                      |
| --------- | -------- | ------------------------------------------------ |
| `token`   | Yes      | Process token returned by the Create Process API |
| `env`     | No       | Set to `'uat'` for test environments only        |

```javascript
import { ByUnicoSDK } from 'idpay-b2b-sdk';

ByUnicoSDK.init({
  token,
  // env: 'uat' // only for test environments
});
```

****Step 3** — Call `open(options)`**

Displays the iFrame and starts the journey for the user. From this point on, everything happens
automatically inside the iFrame, with no need to manage any intermediate step.

| Parameter                  | Required | Description                                                |
| -------------------------- | -------- | ---------------------------------------------------------- |
| `transactionId`            | Yes      | Process ID returned by the Create Process API              |
| `token`                    | Yes      | Process token returned by the Create Process API           |
| `onFinish`                 | Yes      | Callback executed when the journey ends or is closed       |
| `onWidgetVisibilityChange` | No       | Callback executed when the widget visibility state changes |

The next interaction with the application occurs when the journey ends, whether the user
completed it or closed it. At that point, the SDK invokes the **`onFinish`** callback, passed as
a parameter in `open`. From there, the application can call the **`getProcess`** API to check the
result, or wait for a **Webhook** notification if an asynchronous approach is preferred.

In addition to querying the result, it is recommended to use `onFinish` to handle the
application's front-end state:

- **Avoid loops.** Prevent the immediate and unnecessary recreation of processes in case the user
  triggers the flow again right after the journey ends.
- **Flow management.** Ensure the user is directed to the next step of the application, avoiding
  a dead-end screen after the journey closes.

:::warning
The `onFinish` callback signals that the user has completed the journey, but does not guarantee
approval. The process may have ended with a failure on one of the Unico validation rules.
Querying via `getProcess` or receiving the Webhook notification is not optional: these are the
only sources of the actual result, and the application's behavior must be based on them. `onFinish`
must not be used in isolation to determine whether a user was approved.
:::

The `onFinish` callback receives an object describing how the journey ended:

| Field                     | Type                | Description                                                                               |
| ------------------------- | ------------------- | ----------------------------------------------------------------------------------------- |
| `type`                    | string              | How the journey ended: `'FINISH'` (completed) or `'CLOSE'` (user closed before finishing) |
| `transaction`             | object \| undefined | Present when `type` is `'FINISH'`; `undefined` when `type` is `'CLOSE'`                   |
| `transaction.id`          | string              | Process identifier (same as `transactionId` passed in)                                    |
| `transaction.redirectUrl` | string              | URL to redirect the user after the journey                                                |

Handling the **`onWidgetVisibilityChange`** callback is optional and may not be relevant to your
use case. It is invoked whenever the widget's visibility state changes, and is useful only in a
specific scenario: some journeys display a transparent background, keeping the application page
visible behind the experience. Applications that show a custom modal during the verification flow
(for example, as part of an orchestration across multiple KYC providers) may end up displaying
that modal behind the Unico widget, degrading the visual experience. In that case, the callback
lets the application suppress any additional visual elements while the Unico journey is active,
and restore them once it ends. If your application has no UI that could overlap the widget, you
can safely omit it.

```javascript
ByUnicoSDK.open({
  transactionId: '9bc22bac-1e64-49a5-94d6-9e4f8ec9a1bf',
  token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
  onFinish: ({ transaction, type }) => {
    if (type === 'FINISH') {
      // Journey completed (transaction = { id, redirectUrl }) — continue your flow here.
    }
    // type === 'CLOSE' → user closed before finishing;
  },
  // Optional: only needed if your app shows UI that could overlap the widget.
  onWidgetVisibilityChange: (visible) => {
    // suppress or restore your modal based on widget visibility
  },
});

// To close the SDK explicitly at any point:
ByUnicoSDK.close();
```

The sequence diagram below demonstrates how to use the SDK and the API result to configure the
iFrame:

```mermaid
sequenceDiagram
    participant Unico as Unico backend service
    participant Backend as Customer backend server
    participant Frontend as Customer frontend application
    participant SDK as ByUnicoSDK

    Frontend->>Backend: start kyc flow
    Backend->>Unico: call createProcess
    Unico->>Backend: return Unico process info
    Backend->>Frontend: return Unico process info
    Note over Frontend: Initialize the Unico journey as soon as it is determined that the KYC flow is required

    critical Unico journey initialization
        Frontend->>SDK: byUnicoSDK.init
        SDK-->>Frontend: validates customer domain and initialize
    end

    critical Unico journey exhibition
        Frontend->>SDK: byUnicoSDK.open
        SDK-->>Frontend: show byUnico experience
    end
    Note over SDK: User journey

    critical Unico journey completion
        SDK-->>Frontend: call onFinish
    end

    critical Get process result
        Frontend->>Backend: signals that journey was finished
        Backend->>Unico: call getProcess
        opt Webhook
            Note over Backend: In addition to getProcess, you can use the Webhook implementation to ensure a fallback in obtaining the process result.
        end
    end
```

#### Sample apps

| Language / Framework | Description | Repository |
| --------------------- | ------------ | ---------- |
| Angular | PoC in Angular that implements the Journeys SDK | [GitHub — unico-cbu-poc-angular](https://github.com/unico-labs/unico-cbu-poc-angular) |
| JS Vanilla | PoC in JS Vanilla that implements the Journeys SDK | [GitHub — unico-cbu-poc-js](https://github.com/unico-labs/unico-cbu-poc-js) |
| React | PoC in React that implements the Journeys SDK | [GitHub — unico-cbu-poc-react](https://github.com/unico-labs/unico-cbu-poc-react) |
| Vue JS | PoC in Vue JS that implements the Journeys SDK | [GitHub — unico-cbu-poc-vuejs](https://github.com/unico-labs/unico-cbu-poc-vuejs) |

#### Security

:::note[Scope of this section]
This security rationale applies specifically to the **Web App Integration** (`idpay-b2b-sdk`). The
**Web SDK** (`unico-webframe`) uses a different model — it runs entirely in the page context and
[does require a CSP](/dual-api/developers/sdks-and-tools/web/web-sdk/installation). These are two distinct
products with different security architectures.
:::

Security in this model is built in layers, starting with the communication protocol between the SDK
and the application running inside the iFrame.

When the journey is loaded, both parties perform a **handshake** to establish communication. In
this process, the Unico application validates the origin of the data injection message received via `postMessage`
against a closed list of authorized domains, segmented by environment (UAT and PROD). Messages
from non-approved origins are discarded immediately, preventing the journey from being embedded in
unauthorized pages and eliminating the attack surface for vulnerabilities such as **clickjacking**.

In addition to origin validation, the flow only proceeds with a valid transaction token: a
single-use JWT, issued and signed by the Unico backend. This ensures that even an authorized origin
cannot operate with an expired, reused, or forged token.

After the handshake, the token is injected into the iFrame and no sensitive information flows
between the two parties. All remaining communication serves only for interface control — opening,
closing, and screen transitions — preventing process data from being intercepted or leaked during
the journey.

The iFrame isolation also protects the integrity of the Unico scripts at runtime. Since the code
runs in a context separate from the page, it cannot be accessed or modified by external scripts,
ensuring the journey executes exactly as built, without interference.

By design, CSP is not adopted in this integration model. The authorized domains are part of each
client's security configuration, and their public exposure in headers could facilitate infrastructure
mapping by malicious actors. Since client identification only happens at the time of `init`, it is
not possible to inject these domains dynamically into the headers before that point, making CSP
unviable without giving up this privacy. All security guarantees are provided by the handshake
protocol described above.

#### SDK-specific troubleshooting

This section covers the most common issues encountered during integration and the recommended ways
to investigate them.

##### Unexpected behavior or broken flow

Check whether any application script is directly manipulating the iFrame in the DOM. The SDK
creates and manages the iFrame in the page `body`, and any external modification — whether to
scope, positioning, or attributes — can interfere with the journey's lifecycle and cause
unpredictable behavior.

##### Visual experience different from expected

Check whether any global stylesheet in the application is overriding properties inside the iFrame.
The SDK creates the iFrame and all its internal elements with dynamic IDs and classes prefixed with
`unico`, which significantly reduces the risk of conflict via ID or class selectors. Even so, broad
CSS rules — such as tag selectors — can reach elements inside the iFrame and alter the visual
experience delivered to the user.

##### SDK library files modified directly

Check whether any library file has been modified outside the package manager. The library must be
managed exclusively via **npm** or **yarn**, with no direct edits to installed files. Manual
modifications can produce anomalous behavior that is difficult to reproduce and will prevent
Unico support from being able to assist.

#### Do not keep DevTools open during capture tests

The Unico application uses the Capture SDK (unico-webframe) for facial capture, which detects open DevTools as a potential fraud signal and blocks the submission. Close DevTools before running end-to-end capture tests.

:::warning[Unsupported integrations]
The models described in this documentation — direct access and Journeys SDK — are the only
integration approaches officially supported by Unico. Integrations that deviate from these
standards may cause unexpected behavior, security flow failures, and journey interruptions, and
will not be covered by Unico support.

Examples of unsupported approaches:

- **Embedding the SDK inside a WebView** in mobile applications. In these cases, the correct
  approach is to use the **direct access** model, opening the journey link directly in the WebView
  without involving the Journeys SDK.
- **Loading the iFrame directly via an `<iframe>` HTML tag**, without going through the Journeys
  SDK. The iFrame is an internal implementation detail of the SDK and must not be instantiated
  manually. The correct approach is to use the **Journeys SDK**, which manages the iFrame lifecycle
  securely and within the expected standards.

If there is any doubt about whether an approach is within the supported standard, consult the
documentation or contact support before proceeding with the implementation.
:::