---
title: Web capture
description: Run the capture of a Journey flow in a web application, with the hosted link or the Journeys SDK, using what the Human Plugin create returns.
canonical: https://developer.unico.io/human-plugin/integrations/web
locale: en
generated_by: markdown-export
---

# Web capture

Take the person through the capture in a browser, using the token and link returned by Human Plugin.

Your back-end creates the process in Human Plugin with a Journey flow. The `201` response has three values the front-end needs:

| Field | Used for |
|---|---|
| `id` | The Human Plugin process id. The SDK takes it as `transactionId`, and your back-end reads the outcome with it |
| `token` | Starts the capture in the Journeys SDK |
| `link` | The hosted capture page |

`token` and `link` are returned **only** by the create. Pass them to the front-end in the same request, or store them yourself. See [Human Token Journey](/human-plugin/integrations/human-token-journey).

## Requirements

The capture runs in the browser and needs camera access.

| OS | Chrome | Firefox | Safari | Samsung Internet | Opera | Edge |
|---|---|---|---|---|---|---|
| Windows | ✅ | ✅ | N/A | N/A | ❌ | ❌ |
| macOS | ✅ | ✅ | ✅ | N/A | ❌ | ❌ |
| Android | ✅ | ❌ | N/A | ✅ | ❌ | ❌ |
| iOS | ✅ | ✅ | ✅ | N/A | ❌ | ❌ |

- Pages must be served over **HTTPS**. The camera only works in a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
- In-app browsing needs Android 8 (API 26) or iOS 13 or later.
- Browsers inside social-network apps are not supported.

## Choose a model

| Model | Best for |
|---|---|
| **Hosted link** | The simplest setup. No library: the person goes to the `link` and comes back to your `callbackUri` |
| **Journeys SDK** | The capture opens inside your page, so the person never leaves it |

### Hosted link

Send `callbackUri` in the create request. It is the page the person returns to when the capture ends.

```json
{
  "flow": "humantoken",
  "clientId": "client-a",
  "correlationId": "order-1001",
  "credential": { "type": "DUI_TYPE_NG_BVN", "value": "<document number>" },
  "callbackUri": "https://app.example.com/capture/done"
}
```

Then send the person to `link`, in one of two ways:

- **Redirect.** Navigate to `link`. At the end, the person is sent back to `callbackUri`.
- **New tab.** Open `link` with [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open), watch for the tab to reach `callbackUri`, and close it.

When the person is back, tell your back-end, which calls `GET /v1/processes/{id}`.

```mermaid
sequenceDiagram
  participant F as Your front-end
  participant B as Your back-end
  participant H as Human Plugin
  participant P as Hosted page
  F->>B: start the capture
  B->>H: POST /v1/processes (Journey flow, callbackUri)
  H-->>B: 201, id + token + link
  B-->>F: id + link
  F->>P: open link
  Note over P: capture
  P-->>F: back to callbackUri
  F->>B: capture ended
  B->>H: GET /v1/processes/{id}
  H-->>B: 200, final state
```

### Journeys SDK

The SDK opens the capture in an iFrame on your page. It works with any framework (React, Angular, Vue) or plain JavaScript.

Before you start, ask Unico support to register your domains. They must use HTTPS.

#### 1. Install

```bash
npm install unico-journey-sdk
```

The SDK follows semantic versioning, so minor and patch updates are safe to take. Keep it up to date: security and bug fixes ship in new versions.

#### 2. Allow the SDK in your headers

Skip this step if your page sends no `Content-Security-Policy` or `Permissions-Policy` header. Otherwise, add:

**Content-Security-Policy:**

```
frame-src https://*.unico.io https://*.unico.app;
script-src https://idpay.unico.io;
style-src https://idpay.unico.io;
connect-src https://api.cadastro.unico.app;
```

**Permissions-Policy:**

```
camera=(self "https://human-token.unico.io" "https://cadastro.unico.app" "https://id.unico.io" "https://retry.unico.io" "https://ultimatum.unico.io"),
geolocation=(self "https://human-token.unico.io" "https://cadastro.unico.app" "https://id.unico.io" "https://retry.unico.io" "https://ultimatum.unico.io")
```

#### 3. Initialize

Call `init` as soon as you know the capture is needed. It preloads the capture.

```javascript
import { ByUnicoSDK } from 'unico-journey-sdk';

ByUnicoSDK.init({ token });
```

#### 4. Open the capture

| Parameter | Required | Value |
|---|---|---|
| `transactionId` | Yes | The Human Plugin `id` |
| `token` | Yes | The `token` from the create |
| `onFinish` | Yes | Called when the capture ends or the person closes it |
| `onWidgetVisibilityChange` | No | Called when the capture is shown or hidden |

```javascript
ByUnicoSDK.open({
  transactionId: id, // the Human Plugin id
  token,
  onFinish: ({ type }) => {
    if (type === 'FINISH') {
      // The capture ended. Ask your back-end to read the outcome.
    }
    // type === 'CLOSE': the person closed it before the end.
  },
  // Optional: hide your own overlays while the capture is visible.
  onWidgetVisibilityChange: (visible) => {},
});

// To close it yourself:
ByUnicoSDK.close();
```

`onFinish` receives:

| Field | Description |
|---|---|
| `type` | `'FINISH'` when the capture ended, `'CLOSE'` when the person closed it first |
| `transaction.redirectUrl` | Where to send the person next. Present only for `'FINISH'` |

> **Warning:** `onFinish` says the capture ended, not how. The outcome comes only from `GET /v1/processes/{id}` on your back-end.

Use `onFinish` also to move the person to the next screen, and to avoid creating a new process right away if they start again.

```mermaid
sequenceDiagram
  participant F as Your front-end
  participant S as Journeys SDK
  participant B as Your back-end
  participant H as Human Plugin
  F->>B: start the capture
  B->>H: POST /v1/processes (Journey flow)
  H-->>B: 201, id + token + link
  B-->>F: id + token
  F->>S: init({ token })
  F->>S: open({ transactionId: id, token, onFinish })
  Note over S: capture
  S-->>F: onFinish
  F->>B: capture ended
  B->>H: GET /v1/processes/{id}
  H-->>B: 200, final state
```

#### Troubleshooting

- **The flow breaks or behaves oddly.** Make sure no script of yours changes the SDK's iFrame in the DOM.
- **The capture looks different.** Broad CSS rules, such as tag selectors, can reach the iFrame. The SDK's own ids and classes start with `unico`.
- **Unexpected errors after an update.** Install the SDK only through npm or yarn, and never edit its files.

> **Info: Testing**
>
> Close the browser DevTools before you test a capture end to end. An open DevTools blocks the submission.

> **Warning: Unsupported setups**
>
> - **The SDK inside a mobile WebView.** In an app, open the `link` instead. See the [Android](/human-plugin/integrations/android), [iOS](/human-plugin/integrations/ios), [Ionic](/human-plugin/integrations/ionic), [React Native](/human-plugin/integrations/react-native) and [Flutter](/human-plugin/integrations/flutter) guides.
> - **Your own `<iframe>` pointing to the capture.** Use the SDK, which manages the iFrame.