# @okyc/web

> Open the hosted flow from any website — modal, inline, popup or redirect. Zero dependencies, under 10 KB.

Source: https://docs.okyc.stream/web/web-sdk

## Install

```bash
npm install @okyc/web
```

Your backend creates the session and returns `clientToken` and `url` (see [sessions](https://docs.okyc.stream/backend/sessions.md)).
Before embedding, add your site's origin under **Settings → Embedding & redirects → Allowed embed origins** in the
dashboard; the hosted flow refuses to render inside other origins.

## Start the flow

`Okyc.start(options)` opens the flow and returns a `Flow` with `close()`, `on(event, handler)` and the `url`.
This example uses the React wrapper, which calls `Okyc.start` for you:

```jsx title="main.jsx"
function App() {
  const [status, setStatus] = useState('Not verified');
  return (
    <main style={{ fontFamily: 'system-ui', maxWidth: 480, margin: '64px auto' }}>
      <h1>Acme onboarding</h1>
      <p>Status: {status}</p>
      <OkycButton
        // Your backend creates the session and returns { clientToken, sessionUrl }.
        getClientToken={() => fetch('/api/kyc/session', { method: 'POST' }).then((r) => r.json())}
        {...flowHandlers(setStatus)}
      >
        Verify identity
      </OkycButton>
    </main>
  );
}
```

## Handle events

```jsx title="main.jsx"
// UX only — the webhook on your backend is the source of truth.
function flowHandlers(setStatus) {
  return {
    onStepStarted: (e) => console.debug('step started', e.step),
    onComplete: (e) => setStatus(`Submitted (${e.status}) — we'll confirm shortly`),
    onExit: () => setStatus('Closed before finishing'),
    onError: (e) => setStatus(`Error: ${e.code}`),
  };
}
```

| Callback          | Event            | Payload                                                                                            |
| ----------------- | ---------------- | -------------------------------------------------------------------------------------------------- |
| `onReady`         | `ready`          | —                                                                                                  |
| `onStepStarted`   | `step_started`   | `{ sessionId, step }`                                                                              |
| `onStepCompleted` | `step_completed` | `{ sessionId, step }`                                                                              |
| `onComplete`      | `complete`       | `{ sessionId, status }` — `processing`, `approved`, `declined` or `review`                         |
| `onExit`          | `exit`           | `{ sessionId, reason }` — `user_closed` or `expired`                                               |
| `onError`         | `error`          | `{ code, message }` — e.g. `token_expired`, `camera_denied`, `origin_not_allowed`, `popup_blocked` |

`flow.on('complete', handler)` is the same as the callback. Events are for your UI only: grant access after the
[webhook](https://docs.okyc.stream/backend/webhooks.md).

## Options

| Option                     | Default                                           |                                                                                                                                                                                                                        |
| -------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clientToken`              | required                                          | `st_…` from your backend. A secret key is rejected.                                                                                                                                                                    |
| `sessionUrl` / `sessionId` | one required                                      | The session `url` (its origin and id are used) or the `ses_…` id.                                                                                                                                                      |
| `verifyUrl`                | `https://verify.okyc.stream`                      | Hosted flow origin, when you pass `sessionId`. Must be https except on `localhost`.                                                                                                                                    |
| `mode`                     | `modal`                                           | See below.                                                                                                                                                                                                             |
| `container`                | —                                                 | Element or selector; required for `inline`.                                                                                                                                                                            |
| `locale`                   | —                                                 | Override the flow language.                                                                                                                                                                                            |
| `theme`                    | [workflow branding](https://docs.okyc.stream/concepts/workflows.md#branding) | Colour scheme: `'light'`, `'dark'` or `'system'`. Brand colour, radius and font come from the workflow branding. (The older `{ colorScheme }` object still works; its `primaryColor` and `radius` were never applied.) |

| Mode       | Behaviour                                                                                                                                           |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `modal`    | Full-screen overlay on phones, a centred dialog on desktop in the flow's own card style. Esc or the flow's ✕ closes it (`exit` with `user_closed`). |
| `inline`   | An iframe in your `container`; it resizes to the flow's height.                                                                                     |
| `popup`    | A new window; falls back to `redirect` (and emits `popup_blocked`) when blocked.                                                                    |
| `redirect` | Navigates to the flow; it returns to the session's `redirect_url` with `?session_id=&status=`.                                                      |

## Security

* The client token goes in the URL fragment (`#t=…`), which browsers never send to servers.
* Messages are accepted only from the flow's exact origin and window, with `source: "okyc"` and `v: 1`.
* The iframe gets `allow="camera; microphone; fullscreen; clipboard-write"` and nothing else.
* Expired token? Get a new one from your backend (`POST /v1/sessions/:id/client_token`) and call `start` again;
  the flow resumes at the first unfinished step.
