# Workflow Module Guide

This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation [here](https://developer.incode.com/docs/web-sdk-reference). Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade.

Full rollout to all clients still TBD.

The Workflow module runs server-driven multi-step workflows where step ordering and configuration come from the backend per session. Unlike the dashboard-defined Flow, a Workflow lets the server emit nodes dynamically — including custom-module nodes that callbacks back into your code.

Follows the [composite / orchestrator pattern](https://developer.incode.com/docs/web-sdk-2-module-patterns#5-composite--orchestrator-modules), with an additional `asyncResolution` state for server-side processing nodes. See the patterns page for the shared lifecycle.

## Tag

`<incode-workflow>` is a standard Web Component. Importing the UI subpath registers the custom element; importing the CSS applies the module's styles.

```ts
import '@incodetech/web/workflow';
import '@incodetech/web/workflow/styles.css';
```

## Self-loading vs. token mode

`<incode-workflow>` supports two bootstrap modes.

**Token mode** — create the session on your backend, then pass the resulting token to the component. This is the recommended production path because the API key stays server-side.

```ts
<incode-workflow config={{ token: sessionToken }} onFinish={handleFinish} />
```

**Self-loading mode** — the component creates its own session by calling `/omni/start` with a `configurationId` and either an `apiKey` or a `clientId`. The API key is exposed in the browser, so this mode is suitable for prototyping and internal tools only.

```ts
<incode-workflow
  config={{ apiKey: process.env.INCODE_API_KEY, configurationId: 'cfg-id' }}
  onFinish={handleFinish}
/>
```

When self-loading mode receives a 4028 error (workflow not activated), the SDK renders its built-in terminal error screen — no additional error handling is required from the host page.

## Properties

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `config` | `WorkflowComponentConfig` | ❌ | Token-mode or self-loading config |
| `onFinish` | `() => void` | ❌ | Called when the workflow completes |
| `onError` | `(error: string) => void` | ❌ | Called when an error occurs |

## Configuration

`WorkflowConfig` is fetched from the backend via `GET /omni/workflow/info`:

```typescript
type WorkflowConfig = {
  id: string;
  name: string;
  redirectDesktopToMobile: boolean;
  disableSmsOption?: boolean;
  addContinueToDesktop?: boolean;
  qrPhishingResistance?: boolean;
  disableUnsupportedBrowserScreen?: boolean;
  oauth2Secured?: boolean;
  ds?: boolean; // Deepsight enabled
  mergeSessionRecordings?: boolean;
  ageAssurance?: boolean;
  disableLaunchScreen?: boolean;
};
```

The shape is largely informational — the workflow nodes themselves drive what runs. The boolean flags propagate to sub-modules (e.g., `mergeSessionRecordings` and `ageAssurance` to ID Capture; `ds` to Selfie).

`disableLaunchScreen` controls the SDK's built-in launch / home screen. When the backend sets it to `true`, the SDK skips the launch screen and goes straight to the first workflow node. When `false` (or omitted), the workflow surfaces a `homeScreen.visible === true` state — the consumer must render the launch screen and call `continueFromHome()` on user confirmation.

## State Machine

`WorkflowState` is a discriminated union over `status`. `idle`, `loading`, and `ready` additionally carry a `homeScreen: { visible: boolean; isContinueLoading: boolean }` property — see [Launch screen](https://developer.incode.com/docs/web-sdk-2-module-workflow#launch-screen) below.

| Status | Description | Properties |
| --- | --- | --- |
| `idle` | Initial state before `load()` | `homeScreen` |
| `loading` | Fetching the next node from the workflow server | `homeScreen` |
| `completing` | Finishing up after the last module completes, just before the terminal node arrives. Shown as its own status so the UI can render a "Hold on…" processing screen instead of a text-less transition spinner. | `homeScreen` |
| `ready` | Current `WorkflowNode` is active. Render the corresponding sub-module. | `workflowConfig`, `currentNode`, `config`, `moduleState`, `homeScreen` |
| `asyncResolution` | Server is processing async; UI shows progress until the next node arrives. | `workflowConfig`, `currentNode` |
| `finished` | Terminal — workflow's last node was a `FINISH` node. | `workflowConfig`, `finishStatus` |
| `closed` | User dismissed | – |
| `error` | Fatal error | `error`, `errorCode?` |

### Launch Screen

When the backend does not set `WorkflowConfig.disableLaunchScreen`, the workflow exposes a launch / home screen overlay before running the first node. The overlay is signalled via `state.homeScreen.visible`:

- Render your launch screen when `homeScreen.visible === true`.
- Call `manager.continueFromHome()` on user confirmation; `homeScreen.isContinueLoading` becomes `true` while the orchestrator finishes loading the first module.
- When `disableLaunchScreen` is `true`, the SDK never sets `homeScreen.visible` and you can skip rendering the overlay entirely.

Each `WorkflowNode` carries:

```typescript
type WorkflowNode = {
  id: string;
  nodeType: 'MODULE' | 'FINISH' | 'ASYNC_RESOLUTION';
  moduleKey: string; // e.g., 'PHONE', 'SELFIE', 'CUSTOM_MODULE'
  moduleConfiguration: Record<string, unknown>;
  status: string;
  workflowId: string;
};
```

## API Methods

| Method | Description |
| --- | --- |
| `load()` | Start loading the workflow configuration and the first node from the backend. Call once. |
| `completeModule()` | Mark the current module as complete. Call from the active module's `onFinish` callback to advance to the next node. |
| `errorModule(error)` | Mark the current module as failed and transition the workflow to a terminal `error` state. |
| `completeFlow()` | Skip remaining nodes and go straight to the completion step. Used when the workflow was completed externally — e.g. via the desktop → mobile redirect handoff. |
| `continueFromHome()` | Advance past the SDK's launch / home screen. Only meaningful when `state.homeScreen.visible === true`; no-op otherwise (e.g. when `disableLaunchScreen` is `true`). |
| `getModuleConfig<T>()` | Returns the current node's `moduleConfiguration` merged with workflow-level flags (`ds`). Typed via the generic parameter. |
| `getState()` | Get the current workflow state synchronously. |
| `subscribe(callback)` | Subscribe to workflow state changes (returns an unsubscribe function). |

Plus the universal manager lifecycle: `reset`, `stop`.

## Custom Module Callback

Workflows can include `CUSTOM_MODULE` nodes that hand control back to your code. Wire a `CustomModuleCallback` to handle them — it receives `interviewId`, `nodeId`, and the configured `name`, plus `onSuccess` / `onError` hooks that advance the workflow.

```typescript
import { createWorkflowManager } from '@incodetech/core/workflow';

const manager = createWorkflowManager({
  config: {
    /* WorkflowConfig fetched server-side */
  },
  customModuleCallback: ({ interviewId, nodeId, name, onSuccess, onError }) => {
    // Run your custom logic here, then call one of:
    onSuccess('Custom check passed');
    // or onError('Something went wrong');
  },
});
```
