---
title: "Upgrade to Web SDK 2.4.0"
url: "https://developer.incode.com/sdk-reference/web-sdk-2-upgrade-2-4-0/"
section: "sdk-reference"
group: "Incode Web SDK 2 Reference"
version: "v1.1"
status: "live"
---
# Upgrade to Web SDK 2.4.0

This guide covers changes from 2.3.x to 2.4.0. The replacement behavior below targets **2.4.0 or later**. Keep core and web on matching versions. Removed APIs have no compatibility period in 2.4.0; complete the applicable migration before upgrading.

<Component slug="web-2-x-implementation-warning" />

Review the sections that apply to your configuration, custom UI, deployment, or styles. Applications using SDK components still need to check removed configuration options.

## Manual upload removal

**Before:** `manualUploadIdCapture` and `manualUploadSelfieCapture` let the user upload an ID or selfie image from a file. Custom UIs handled the `manualUpload` state and called the manual-upload methods.

**In 2.4.0:** both options are removed, together with their states, methods, and fields. Use camera capture or [Digital ID](/sdk-reference/web-sdk-2-module-id-capture/) for supported documents.

| Previous usage | Required action |
| --- | --- |
| `manualUploadIdCapture` in ID Capture configuration | Remove the option. |
| ID Capture `manualUpload` state, `manualUploadChangeTab(tab)`, `manualUploadSelectFile(side, file)`, `manualUploadContinue()`, and `manualUploadReset()` | Remove the state handler and the calls. |
| `manualUploadSelfieCapture` in Selfie configuration | Remove the option. |
| Selfie `manualUpload` state, `goToManualUpload()`, `manualUploadSelectFile(file)`, and `manualUploadContinue()` | Remove the state handler and the calls. |
| `showManualUploadLink` in Selfie `tutorial` state fixtures | Remove the field from typed fixtures. |

## WASM integrity checks

**Before:** the SDK ran the WASM binaries, glue files, and models that it downloaded.

**In 2.4.0:** the SDK checks each WASM file against the official files for its release before it runs the file. The check applies wherever the files are hosted: the Incode CDN or a self-hosted copy. If a file does not match, the SDK does not run it and WASM initialization fails.

- **Strict Content-Security-Policy:** the SDK loads the verified glue from a `blob:` URL. Add `blob:` to your `script-src` directive.
- **Self-hosted WASM:** serve the unmodified distribution that matches your SDK version. Files from another release no longer load, so refresh the full distribution each time you upgrade. See [Hosting WASM Files](/sdk-reference/web-sdk-2-wasm/#hosting-wasm-files).
- **Secure pages:** the check needs `crypto.subtle`, which browsers expose only on pages served over HTTPS or from `localhost`.

For each integrity error code and its fix, see [Troubleshooting](/sdk-reference/web-sdk-2-troubleshooting/#wasm-issues).

## Capture-only ID responses

**Before:** `CaptureOnlyResponse.frontImage` was always present, and a back-only capture (`onlyBack: true`) did not call `onCapture`. With an `onCapture` callback, `createIdCaptureManager` and `<incode-id>` could run the regular upload flow instead of the capture-only flow.

**In 2.4.0:** `CaptureOnlyResponse` is a union. `frontImage` is `undefined` for a back-only capture, which now calls `onCapture` with the back image. TypeScript code that reads `response.frontImage.blob` no longer compiles. Check `frontImage` first:

```typescript
import type { CaptureOnlyResponse } from '@incodetech/core/id';

declare function uploadToMyBackend(front?: Blob, back?: Blob): Promise<void>;

async function handleCapture(response: CaptureOnlyResponse) {
  await uploadToMyBackend(response.frontImage?.blob, response.backImage?.blob);
}
```

An ID configuration with `onCapture` now always runs the capture-only flow. Nothing is uploaded, and only the capture-only fields apply: `showTutorial`, `captureAttempts`, `autoCaptureTimeout`, `deviceIdleTimeout`, `enableId`, `enablePassport`, `onlyFront`, `onlyBack`, and `showDocumentChooserScreen`. Runtime behavior for front and front-and-back captures does not change. See [ID Capture](/sdk-reference/web-sdk-2-module-id-capture/).

## Identity Reuse

**Before:** the user selected a document on a radio card, then confirmed with a separate button.

**In 2.4.0:** a tap on a document card or on "Scan a new document" submits that choice at once. The screen also adds a "What data will be shared?" sheet and a link to Incode's Privacy Policy.

- **Custom UIs:** call `submit(documentId)` with a document id or `SCAN_NEW_DOCUMENT`. `submit()` without an argument still submits the current selection, and `chooseDocument(documentId)` still works for two-step UIs. Both ignore ids that are not among the offered documents.
- **Organization name:** the `documentsFound` state has an optional `companyName`. Name the receiving organization when it is present and use generic copy when it is absent.
- **Loading state:** `loading` is now a parallel state, so its `snapshot.value` is an object. Use `snapshot.matches('loading')` instead of a value comparison.
- **Machine overrides:** if you override actors with `identityReuseMachine.provide()`, also provide the new `fetchCompanyName` actor, typed with `FetchCompanyNameResponse`. Otherwise, the real request runs.
- **Styles and tests:** the radio-picker class names `IncodeIdentityReuseCardSelected`, `IncodeIdentityReuseRadio`, `IncodeIdentityReuseRadioChecked`, `IncodeIdentityReuseRadioInner`, `IncodeIdentityReuseCardBody`, `IncodeIdentityReuseCardHeaderLeft`, and `IncodeIdentityReuseScanNewBody` are removed. The `identity-reuse-confirm-button` test id is removed. Update stylesheets and tests that target them.

```typescript
import {
  createIdentityReuseManager,
  SCAN_NEW_DOCUMENT,
} from '@incodetech/core/identity-reuse';

const manager = createIdentityReuseManager({ config: {} });

// Call from the "Scan a new document" card.
function scanNewDocument() {
  manager.submit(SCAN_NEW_DOCUMENT);
}
```

See [Identity Reuse](/sdk-reference/web-sdk-2-module-identity-reuse/).

## Flow completion redirects

**Before:** the flow completion screen navigated to any `redirectionUrl`.

**In 2.4.0:** the SDK navigates only to an absolute `https` URL. For any other value, the completion screen shows, and the console logs a warning. For local development, an `http://localhost` URL is allowed while the page itself runs on `localhost`. The [Digital ID bank redirect](/sdk-reference/digital-id-redirect) uses the same rule.

Change any `http` redirect URL in your flow configuration to `https`.

## Session expiry

**Before:** when the current session expired, the next request failed inside the module, and each module handled that failure separately.

**In 2.4.0:** when a request for the current session returns HTTP 401, the SDK ends every mounted module, Flow, and Workflow, and releases the camera. It then calls `onError` once, with `SESSION_EXPIRED` as the third argument and the error object as the fourth:

```typescript
onError('Session expired', 401, 'SESSION_EXPIRED', error);
```

- **Components:** handle `SESSION_EXPIRED` in `onError`, for example by starting a new session and mounting a new element.
- **Host-supplied managers:** the SDK stops a manager that you pass through the `manager` prop. A stopped manager cannot be reused, so create a new one for the replacement session.
- **Headless integrations:** subscribe with `onSessionExpired()`. Each subscription is called once.

```typescript
import { onSessionExpired } from '@incodetech/core/session';

const unsubscribe = onSessionExpired((error) => {
  // error.code is 'SESSION_EXPIRED' and error.statusCode is 401.
  // Stop your managers, start a new session, and create new managers.
});

// Call unsubscribe() when the owner of the subscription unmounts.
```

A connection failure with no HTTP status does not expire the session. Requests that do not carry the session token do not expire it either.

## Workflow completion copy

**Before:** `onFinish` for `<incode-workflow>` returned `endScreenTitle` and `endScreenText` as `null`, even when Dashboard had custom completion copy.

**In 2.4.0:** self-loading Workflows fill both fields from Dashboard. In token mode, the SDK does not see your session response. Forward both values from the response to the component:

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

See [Workflow](/sdk-reference/web-sdk-2-module-workflow/#self-loading-vs-token-mode).

## Field Comparison validation

**Before:** Field Comparison enabled Continue only after the user completed the first and last name.

**In 2.4.0:** Continue starts enabled. Submitting a blank first or last name shows a required-field error for each missing value and disables Continue until the user corrects them. In a custom UI, render the manager's validation feedback and do not add a separate completeness gate. See [Field Comparison](/sdk-reference/web-sdk-2-module-field-comparison/).