# IncodeFlow Component

<WebSDK2 />

`<incode-flow>` is the complete, orchestrated identity verification experience as a standard Web Component. It runs the flow sequence configured in your Incode dashboard end-to-end.

**Important:** create the session via `createSession()` (or your backend) and call `setup()` with the resulting token before mounting `<incode-flow>`. The component does not create sessions internally; it consumes the session token you provide.

## Tag

```ts
// Side-effect import — registers the <incode-flow> custom element
import '@incodetech/web/flow';
import '@incodetech/web/flow/styles.css';
```

## Properties

Set these as JavaScript properties on the element (not as HTML attributes):

| Property   | Type                                                       | Required | Description                           |
| ---------- | ---------------------------------------------------------- | -------- | ------------------------------------- |
| `config`   | `FlowConfig`                                               | ✅        | Flow configuration object (see below) |
| `onFinish` | `(result?: FinishStatus) => void`                          | ✅        | Called when the flow completes        |
| `onError`  | `(error: string \| undefined, errorCode?: number) => void` | ❌        | Called on fatal errors                |

### FlowConfig

`FlowConfig` has two shapes — a token-based variant (recommended) and a self-loading variant. The two are mutually exclusive.

**Token variant (recommended)** — pass a token obtained from `createSession()` (ideally created on your backend):

| Property                | Type                            | Required | Description                                                                                                                                                                                                                                           |
| ----------------------- | ------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`                 | `string`                        | ✅        | Session token                                                                                                                                                                                                                                         |
| `lang`                  | `string`                        | ❌        | Language code (e.g., `'en-US'`, `'es-MX'`)                                                                                                                                                                                                            |
| `enableHome`            | `boolean`                       | ❌        | Show the SDK's built-in home screen                                                                                                                                                                                                                   |
| `authHint`              | `string`                        | ❌        | QR/auth hint when re-entering a flow                                                                                                                                                                                                                  |
| `wasmConfig`            | `WasmConfig`                    | ❌        | WASM paths (see [WASM Configuration](doc:web-sdk-2-wasm))                                                                                                                                                                                             |
| `spinnerConfig`         | `SpinnerConfig`                 | ❌        | Loading spinner customization                                                                                                                                                                                                                         |
| `disableDashboardTheme` | `boolean`                       | ❌        | Skip applying the dashboard's theme tokens                                                                                                                                                                                                            |
| `urlUuid`               | `string`                        | ❌        | QR anti-phishing token from the URL (mobile leg)                                                                                                                                                                                                      |
| `useCPF`                | `boolean`                       | ❌        | Switch the orchestrator's `ID_OCR` step to `CPF_OCR` for Brazilian CPF flows. When `true`, `<incode-flow>` renders the CPF OCR module instead of the standard ID OCR module at that step. Has no effect when the dashboard flow has no `ID_OCR` step. |
| `onFlowEvent`           | `(event: FlowEvent) => void`    | ❌        | Curated flow milestones                                                                                                                                                                                                                               |
| `onModuleLoading`       | `(moduleKey: string) => void`   | ❌        | Module begins loading (lazy chunk)                                                                                                                                                                                                                    |
| `onModuleLoaded`        | `(moduleKey: string) => void`   | ❌        | Module loaded                                                                                                                                                                                                                                         |
| `onWasmWarmup`          | `(pipelines: string[]) => void` | ❌        | WASM warmup begins                                                                                                                                                                                                                                    |
| `onUrlUuidRefreshed`    | `(urlUuid: string) => void`     | ❌        | New `urlUuid` available — host should update the address bar                                                                                                                                                                                          |

**Self-loading variant** — convenient for prototyping; **avoid in production** because it puts the API key in the browser:

| Property                                                                                             | Type     | Required | Description                    |
| ---------------------------------------------------------------------------------------------------- | -------- | -------- | ------------------------------ |
| `apiKey` (or `clientId`)                                                                             | `string` | ✅        | Auth key for session creation  |
| `configurationId`                                                                                    | `string` | ✅        | Dashboard flow ID              |
| `externalId`, `externalCustomerId`, `customFields`, `uuid`, `urlUuid`, `interviewId`                 | various  | ❌        | Forwarded to `createSession()` |
| Plus any of the optional fields from the token variant above (`lang`, `enableHome`, callbacks, etc.) |          |          |                                |

> **`apiURL` is NOT in `FlowConfig`.** It belongs on the global `setup()` call. The component reuses the API URL configured there.

## Basic Usage

### Step 1: Create the session

Create the session via `createSession()` (typically from your backend) and configure the SDK:

```tsx
import { setup } from '@incodetech/core';
import { createSession, initializeSession } from '@incodetech/core/session';

await setup({ apiURL: 'https://demo-api.incodesmile.com' });

const session = await createSession(apiKey, {
  configurationId: 'your-config-id',
  language: 'en-US',
  externalId: 'optional-external-id',
});

await initializeSession({ token: session.token });
```

The `token` is still passed to the component itself via `flowElement.config.token` (see Step 2) — `initializeSession` activates it on the SDK's HTTP client; the component reads it from its own config. `setup({ apiURL, token })` is also supported as a one-shot convenience and delegates to `initializeSession` internally.

### Step 2: Mount the component

**HTML / vanilla TypeScript:**

```html
<incode-flow id="flow"></incode-flow>

<script type="module">
  import '@incodetech/web/themes/light.css';
  import '@incodetech/web/base.css';
  import '@incodetech/web/flow';
  import '@incodetech/web/flow/styles.css';

  const flow = document.getElementById('flow');
  flow.config = { token: session.token, lang: 'en-US', enableHome: true };
  flow.onFinish = (result) => {
    if (result?.redirectionUrl) {
      window.location.href = result.redirectionUrl;
    }
  };
  flow.onError = (error, code) => console.error('Flow error:', error, code);
</script>
```

**React:**

> **React 18 or earlier:** add a one-time JSX augmentation so TypeScript recognizes `<incode-flow>` and the other Incode tags. See [Framework Integration → TypeScript: JSX support for `incode-*` tags](doc:web-sdk-2-framework-integration#typescript-jsx-support-for-incode--tags). React 19+ doesn't need this.
>
> **React 19+:** you can also drop the ref ceremony entirely and pass `config`/`onFinish`/`onError` as ordinary JSX props. See [Framework Integration → React 19+ shortcut](doc:web-sdk-2-framework-integration#react-19-shortcut).

```tsx
import { useEffect, useRef } from 'react';
import type { FlowConfig } from '@incodetech/web/flow';
import type { FinishStatus } from '@incodetech/core/flow';
import '@incodetech/web/flow';
import '@incodetech/web/flow/styles.css';

type FlowElement = HTMLElement & {
  config: FlowConfig;
  onFinish: (result?: FinishStatus) => void;
  onError: (error: string | undefined, code?: number) => void;
};

export function FlowMount({ token }: { token: string }) {
  const ref = useRef<FlowElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.config = { token, lang: 'en-US', enableHome: true };
    el.onFinish = (result) => {
      if (result?.redirectionUrl) window.location.href = result.redirectionUrl;
    };
    el.onError = (error, code) => console.error('Flow error:', error, code);
  }, [token]);

  return <incode-flow ref={ref} style={{ display: 'block', height: '100vh' }} />;
}
```

For Angular and Vue, see [Framework Integration](doc:web-sdk-2-framework-integration).

### Complete React Example

> **React 18 or earlier:** add the JSX augmentation from [Framework Integration → TypeScript: JSX support for `incode-*` tags](doc:web-sdk-2-framework-integration#typescript-jsx-support-for-incode--tags). React 19+ doesn't need it, and can also use the simpler form from [Framework Integration → React 19+ shortcut](doc:web-sdk-2-framework-integration#react-19-shortcut).

```tsx
import { useState, useEffect, useRef } from 'react';
import { setup } from '@incodetech/core';
import { createSession, initializeSession } from '@incodetech/core/session';
import type { FlowConfig } from '@incodetech/web/flow';
import type { FinishStatus } from '@incodetech/core/flow';
import '@incodetech/web/themes/light.css';
import '@incodetech/web/base.css';
import '@incodetech/web/flow';
import '@incodetech/web/flow/styles.css';

type FlowElement = HTMLElement & {
  config: FlowConfig;
  onFinish: (result?: FinishStatus) => void;
  onError: (error: string | undefined, code?: number) => void;
};

function MyVerificationFlow() {
  const [sessionToken, setSessionToken] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const flowRef = useRef<FlowElement>(null);

  useEffect(() => {
    const initialize = async () => {
      try {
        await setup({ apiURL: 'https://demo-api.incodesmile.com' });
        const session = await createSession('your-api-key', {
          configurationId: 'your-config-id',
          language: 'en-US',
        });
        await initializeSession({ token: session.token });
        setSessionToken(session.token);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Failed to create session');
      }
    };
    initialize();
  }, []);

  useEffect(() => {
    const el = flowRef.current;
    if (!el || !sessionToken) return;
    el.config = { token: sessionToken, lang: 'en-US', enableHome: true };
    el.onFinish = (result) => console.log('Flow completed:', result);
    el.onError = (error) => console.error('Flow error:', error);
  }, [sessionToken]);

  if (error) return <div>Error: {error}</div>;
  if (!sessionToken) return <div>Initializing...</div>;

  return <incode-flow ref={flowRef} style={{ display: 'block', height: '100vh' }} />;
}
```

## Custom Start and Completion Screens

The `IncodeFlow` component does not render start or completion screens internally — you control what the user sees before the flow begins and after it finishes.

### Start screen

Build your own. Render whatever start UI suits your product (logo, copy, terms, a "Begin verification" button), and only mount `<incode-flow>` once the user clicks through. There is no public SDK component for this, by design — the start screen is a branding/UX surface that belongs to the integrator.

### Completion screen

For completion, the SDK ships an optional drop-in web component, `<incode-flow-completed>`, that renders a simple "verification complete" screen with optional auto-redirect. It is registered as a side effect of `@incodetech/web/flow` (the same import you already have for `<incode-flow>`):

```html
<incode-flow-completed
  title="All Done!"
  message="Your identity has been verified."
  redirection-url="https://yourapp.com/return"
  action="approved"
  score-status="OK"
  redirect-delay="3000"
></incode-flow-completed>
```

**Attributes:**

| Attribute              | Type                                                                    | Description                                                                        |
| ---------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `title`                | `string`                                                                | Heading text. Falls back to a translated default.                                  |
| `message`              | `string`                                                                | Subtitle text. Falls back to a translated default.                                 |
| `redirection-url`      | `string`                                                                | If set, the page redirects here after `redirect-delay` ms.                         |
| `redirect-delay`       | `number`                                                                | Milliseconds to wait before redirecting. Default `3000`.                           |
| `disable-redirect`     | `boolean`                                                               | Skip the redirect even if `redirection-url` is set.                                |
| `redirect-origin-only` | `boolean`                                                               | Suppress redirect when the page is already a redirect target (`?isRedirect=true`). |
| `action`               | `'approved' \| 'rejected' \| 'none'`                                    | The verification outcome (used for analytics/styling hooks).                       |
| `score-status`         | `'OK' \| 'WARN' \| 'MANUAL_OK' \| 'FAIL' \| 'UNKNOWN' \| 'MANUAL_FAIL'` | The score classification.                                                          |
| `flow-id`              | `string`                                                                | Appended to the redirect URL as a query param.                                     |
| `interview-id`         | `string`                                                                | Appended to the redirect URL as a query param when redirecting cross-origin.       |

If you'd rather build your own completion screen, the result data you need is on the `onFinish` payload from `<incode-flow>` (`redirectionUrl`, `action`, `scoreStatus`, `flowId`, `interviewId`). Render whatever you want and skip `<incode-flow-completed>` entirely.

### Language

Set the UI language via the `lang` property in your config:

```ts
flow.config = { token: session.token, lang: 'es' /* Spanish */ };
```

The SDK ships translations for ~85 locales (including regional variants like `en-DG`, `es-MX`, `pt-BR`, `zh-TW`, `fr-CA`). See [Internationalization](doc:web-sdk-2-i18n) for the full list, fallback behavior, and dashboard interaction.

## Event Callbacks

### onFinish

**Required callback** called when the flow finishes successfully. The component does not render a completion screen — you must handle the finish status yourself:

```ts
flow.onFinish = (result) => {
  console.log('Action:', result?.action);        // 'approved' | 'rejected' | 'none'
  console.log('Status:', result?.scoreStatus);   // 'OK' | 'WARN' | 'FAIL' | ...
  console.log('Redirect:', result?.redirectionUrl);

  if (result?.action === 'approved' && result.redirectionUrl) {
    window.location.href = result.redirectionUrl;
  } else if (result?.action === 'rejected') {
    // Show rejection message
  }
};
```

The `result` object (typed as `FinishStatus`) contains:

* `redirectionUrl`: URL to redirect to (if configured)
* `action`: `'approved' | 'rejected' | 'none'`
* `scoreStatus`: `'OK' | 'WARN' | 'MANUAL_OK' | 'FAIL' | 'UNKNOWN' | 'MANUAL_FAIL'`

Note that `result` may be `undefined` (the callback signature is `(result?: FinishStatus) => void`), so always null-check before reading fields.

### onError

Called when an error occurs:

```ts
flow.onError = (error, code) => {
  console.error('Verification error:', error, code);
  // Show your own error UI or retry logic
};
```

### onModuleLoading / onModuleLoaded

Track lazy-loading of each module's UI chunk via config callbacks:

```ts
flow.config = {
  token: session.token,
  onModuleLoading: (moduleKey) => console.log(`Loading module: ${moduleKey}`),
  onModuleLoaded: (moduleKey) => console.log(`Loaded module: ${moduleKey}`),
};
```

### onWasmWarmup

Called when ML models begin loading:

```ts
flow.config = {
  token: session.token,
  onWasmWarmup: (pipelines) => {
    console.log('Warming up:', pipelines); // e.g., ['selfie', 'idCapture']
  },
};
```

## WASM Configuration

For ML-powered modules (selfie, ID capture), preload WASM with the `wasm` option on `setup()` so models are ready by the time the user reaches a camera step. The Incode CDN serves sensible defaults — you only need to pick which pipelines to preload:

```ts
await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: session.token,
  wasm: { pipelines: ['selfie', 'idCapture'] },
});

// Then mount <incode-flow> as shown in Basic Usage above.
```

If you omit the `wasm` option, the SDK loads WASM lazily when the user reaches the first selfie or ID-capture step. Pass `wasm: false` to explicitly disable preload (useful when this page only runs phone/email flows). For self-hosted paths, custom model files, or the lower-level `warmupWasm()` API, see [WASM Configuration](doc:web-sdk-2-wasm).

## Loading Spinner Customization

`<incode-flow>` shows a transition spinner during initialization, while a module's lazy chunk loads, and between flow steps. The same spinner is used in every phase.

**Default text behavior:** if you don't pass `spinnerConfig`, the spinner renders with **no title or subtitle** — only the spinning icon. As soon as you set either `spinnerConfig.title` or `spinnerConfig.subtitle`, the missing field falls back to the SDK's translated defaults (i18n keys `loadingCircle.hangOn` for the title, `loadingCircle.validating` for the subtitle, which display as "Hang on" / "Validating" in English).

### SpinnerConfig Options

You can override the default text using `spinnerConfig`:

| Property   | Type                             | Description                                    |
| ---------- | -------------------------------- | ---------------------------------------------- |
| `title`    | `string`                         | Custom title text (overrides default)          |
| `subtitle` | `string`                         | Custom subtitle text (overrides default)       |
| `size`     | `'small' \| 'medium' \| 'large'` | Size of the spinner icon (default: `'medium'`) |

### Custom Text

```ts
flow.config = {
  token: session.token,
  spinnerConfig: {
    title: 'Verifying your identity',
    subtitle: "This won't take long...",
    size: 'large',
  },
};
```

### CSS Variable Customization

You can also customize the spinner appearance using CSS variables:

```css
:root {
  /* Spinner colors */
  --spinner-surface-primary: #0066cc;
  --spinner-surface-secondary: #e6f0ff;
  
  /* Spinner text */
  --spinner-text-title: #1a1a1a;
  --spinner-text-subtitle: #666666;
  
  /* Spinner overlay background */
  --spinner-surface-overlay: #ffffff;
  --spinner-surface-overlay-opacity: 1;
}
```

See [Theming & Styling](doc:web-sdk-2-theming) for more details on CSS customization.

## Web Component

`IncodeFlow` is also available as a standard Web Component for framework-agnostic usage:

```html
<!DOCTYPE html>
<html>
<head>
  <style>
    html, body, incode-flow { height: 100%; margin: 0; }
    incode-flow { display: block; }
  </style>
</head>
<body>
  <incode-flow></incode-flow>

  <script type="module">
    import { setup } from '@incodetech/core';
    import { createSession, initializeSession } from '@incodetech/core/session';
    import '@incodetech/web/themes/light.css';
    import '@incodetech/web/base.css';
    import '@incodetech/web/flow';
    import '@incodetech/web/flow/styles.css';

    async function initializeFlow() {
      try {
        await setup({ apiURL: 'https://demo-api.incodesmile.com' });

        const session = await createSession('your-api-key', {
          configurationId: 'your-config-id',
          language: 'en-US',
        });

        await initializeSession({ token: session.token });

        const flow = document.querySelector('incode-flow');
        flow.config = {
          token: session.token,
          lang: 'en-US',
          enableHome: true,
        };
        flow.onFinish = (result) => {
          if (result.action === 'approved' && result.redirectionUrl) {
            window.location.href = result.redirectionUrl;
          }
        };
        flow.onError = (error, code) => console.error('Error:', error, code);
      } catch (error) {
        console.error('Failed to initialize:', error);
      }
    }

    initializeFlow();
  </script>
</body>
</html>
```

## TypeScript

Full type definitions are included. Import `FlowConfig` from `@incodetech/web/flow` and `FinishStatus` from `@incodetech/core/flow`:

```typescript
import type { FlowConfig } from '@incodetech/web/flow';
import type { FinishStatus } from '@incodetech/core/flow';
import '@incodetech/web/flow';

// Automatically typed via HTMLElementTagNameMap augmentation
const flow = document.createElement('incode-flow');
flow.config = {
  // ✅ typed as FlowConfig
  token: 'session-token-from-createSession',
  lang: 'en-US',
};
flow.onFinish = (result) => {
  // ✅ result typed as FinishStatus | undefined
  console.log(result?.action);
};
```

See [TypeScript Types](doc:web-sdk-2-typescript-types#web-component-element-types) for more details.

## Unsupported Modules

If the orchestrated flow returns a step whose UI module isn't bundled in this SDK build, `<incode-flow>` renders a fallback screen titled **"Module not available"** with a **Next** button. Clicking Next calls `flowManager.completeModule()` and advances the flow.

This keeps the rest of a multi-step flow working when one step is unrecognized — useful when the dashboard configuration runs ahead of the SDK release. It applies only to the `<incode-flow>` component; in headless mode (`createOrchestratedFlowManager` with your own UI), an unrecognized step key throws `"No registered module found for: <KEY>"`.
