# Workflow Module

<WebSDK2 />

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](doc: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';
```

## Properties

| Property   | Type                      | Required | Description                            |
| ---------- | ------------------------- | -------- | -------------------------------------- |
| `config`   | `WorkflowConfig`          | ❌        | Workflow configuration (server-driven) |
| `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](#launch-screen) below.

| Status            | Description                                                                | Properties                                                             |
| ----------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `idle`            | Initial state before `load()`                                              | `homeScreen`                                                           |
| `loading`         | Fetching the next node from the workflow server                            | `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');
  },
});
```

## See also

* [IncodeFlow Component](doc:web-sdk-2-incodeflow-component): the dashboard-driven flow alternative
* [Module Patterns → composite](doc:web-sdk-2-module-patterns#5-composite--orchestrator-modules)
* [Headless Mode → Orchestrated Flow Manager](doc:web-sdk-2-headless-mode#orchestrated-flow-manager)
* [Individual Modules](doc:web-sdk-2-individual-modules)
