> For the complete documentation index, see [llms.txt](/llms.txt)

# Introduction

Entrust Identity Services (formerly Onfido) are introducing a new collection of SDKs that bring closer together:

- Entrust's Identity Verification products
- Workflow Studio, Entrust's bespoke orchestration platform
- Haromonized SDK APIs driving our mobile (Android, iOS, React Native) and Web SDKs

<br/>

This guide is intended to offer instructions for the integration of, and migration to, the following major versions of the Entrust IDV SDKs:

- Android SDK
- iOS SDK
- React Native SDK
- Flutter SDK (planned for October 2026)

## Minimum dependencies and SDK distribution

The SDKs are available from the main distribution channel for their respective platforms:

  
### Android

    |                 |                                                                                                         Details                           |
    | --------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
    | Distribution    | Sonatype/Maven Central: [`com.entrust.identity.verification.sdk`](https://central.sonatype.com/namespace/com.entrust.identity.verification.sdk) <br /> *Note*: the minimum package required is [`:core`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/core)                                                            |
    | Target Version  | Android 15 (API level 35) <br /> Compatible with Android 16 (API level 36)<br />Kotlin 2.1.10<br />Compose 2025.02.00<br />Java 11 |
    | Minimum Version | Android 5 (API level 21)                                                                                                           |

    **Note**: SDK supports [16KB page file size](https://developer.android.com/guide/practices/page-sizes)

  

  
### iOS

    |                 |  Details                                                                            |
    | --------------- | ---------------------------------------------------------------------------- |
    | Distribution    | Swift Package Manager (SPM): [`entrustCorporation/Idv-SDK-iOS`](https://github.com/entrustCorporation/IdvSdk-iOS) |
    | Target Version  | iOS 15<br />Xcode 16                                                         |
    | Minimum Version | iOS 15                                                                       |

    **Note**: The SDK has also been tested with the developer builds of the upcoming iOS 26.0 operating system

  

  
### React Native

    |                         |  Details                                                               |
    | ----------------------- | --------------------------------------------------------------- |
    | Distribution            | Node Package Manager (NPM): [`@entrust.corporation/idvsdk-reactnative`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative) |
    | Library Version         | 0.79                                                            |
    | Minimum Android Version | Android 7 (API level 24)                                        |
    | Minimum iOS Version     | iOS 15                                                          |

    **Note**: The SDK is now built using the React Native 'New Architecture'.

  

> ⚠️ **Warning:** The Entrust IDV SDKs are now published under the 'Entrust' domain across all distribution platforms instead of the original Onfido domain.
> The migration includes the [Entrust GitHub](https://github.com/EntrustCorporation) domain that now contains our latest sample apps

## Environments and testing with the SDK

Two environments exist to support the SDK integrations:

- `sandbox` - to be used for testing with sample documents
- `live` - to be used only with real documents and in production apps

The environment being used is determined by the API token that is used to generate the necessary SDK token.

## Sample integration apps

To assist with the SDK integration effort, repositories containing a sample app are provided for each SDK on Entrust's Github account. Their purpose is to provide an example of a basic SDK integration using the minimal resources and dependencies:

| Repository with sample app                                                            |
| ------------------------------------------------------------------------------------- |
| [`Android Sample App`](https://github.com/EntrustCorporation/IdvSdk-Android)          |
| [`iOS Sample App`](https://github.com/EntrustCorporation/IdvSdk-iOS)                  |
| [`React Native Sample App`](https://github.com/EntrustCorporation/IdvSdk-ReactNative) |

> ℹ️ **Note:** These repositories are updated automatically with each SDK release. Subscribing to their 'Notifications' is an easy way to track releases

## Promotion to production

Once you are satisfied with your integration and are ready to go live, please contact [Customer Support](mailto:identity-client-support@entrust.com) to obtain a live API token.

Check that you have entered correct billing details inside your [Dashboard](https://dashboard.onfido.com/), before starting to send requests in the 'live' environment.

## Staying up-to-date

We recommend you update your SDK to the latest version release as frequently as possible. Customers on newer versions of the SDK consistently see better performance across completion rates and fraud mitigation, so we strongly advise keeping your SDK integration up-to-date.

You can review our full [SDK versioning policy](/sdk/sdk-version-releases).

## Support

Should you encounter any technical issues during integration, please contact [Customer Support](mailto:identity-support@entrust.com).

Alternatively, you can search the support documentation available via the [Customer Experience Portal](https://support.identity.entrust.com/).

# SDK Bundle and Product Availability

The new SDKs will support the full Entrust Identity Verification Suite.

A key innovation introduced with the Entrust IDV SDKs is that the new SDK bundles only include the minimum assets required to initialize verification flows. By default, remote (Web) assets (UI customization elements such as fonts and icons) and modules (or identity verification products) are used.

As a result, integrators have the ability to specify the exact composition of the local SDK bundle by only including the required native modules. This ensures that the bundle size and efficiency remains optimal.

## Availability of native and Web modules

The table below lists all identity verification products (referred to as modules) available via the SDKs, and whether they are available natively within the SDK bundles or only as Web variants of the modules.

**Please note**: All products in our Onfido Smart Capture SDKs for mobile will continue to be supported for a period of 9 months from the upcoming release of the Document Capture, NFC Capture and Authentication native modules (which will close the existing parity gaps with the Onfido Smart Capture SDKs for mobile), in line with our [SDK versioning policy](/sdk/sdk-version-releases/#major). Based on current release expectations, this means all products in the Onfido SDKs will remain supported until September 2027.

Some modules are available in early adopter access. Please contact your account manager if you are interested in becoming an early adopter.

<table border="1">
  <thead>
    <tr>
      <th colspan="3"></th>
      <th colspan="3">Entrust IDV SDK for Mobile</th>
      <th colspan="3">Onfido Smart Capture SDKs for Mobile</th>
    </tr>
    <tr>
      <th>Category</th>
      <th>SDK Module</th>
      <th>Module Configuration</th>
      <th>Web module</th>
      <th>Native Android module</th>
      <th>Native iOS module</th>
      <th>Web module</th>
      <th>Native Android module</th>
      <th>Native iOS Module</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Document Verification</td>
      <td>Document Capture</td>
      <td></td>
      <td>✅</td>
      <td>✅<br></br>(early adopter access)</td>
      <td>✅<br></br>(early adopter access)</td>
      <td>-</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td></td>
      <td>NFC Capture</td>
      <td></td>
      <td>N/A</td>
      <td>✅</td>
      <td>✅</td>
      <td>N/A</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td></td>
      <td>Proof of Address</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>Biometric Verification</td>
      <td>Selfie (Photo)</td>
      <td></td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td></td>
      <td>Liveness (Video)</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td></td>
      <td>Motion</td>
      <td>Motion<br></br>(2 head turns)</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td>Randomness<br></br>(4 random head turns)</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td></td>
      <td>Authentication Biometrics</td>
      <td>Motion<br></br>(2 head turns)</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td>Randomness<br></br>(4 random head turns)</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td>Face Authentication<br></br>(no head turn)</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td></td>
      <td>Liveness Verification</td>
      <td></td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>Electronic Signature (contract signing)</td>
      <td>Advanced / Simple Electronic Signature</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td></td>
      <td>Qualified Electronic Signature (QES)</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>Compliance Suite</td>
      <td>One-Time Password (OTP)</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td></td>
      <td>Qualified Electronic Signature (QES)</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>Miscellaneous</td>
      <td>Profile Data Capture</td>
      <td></td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
      <td>✅</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

**Please note:** All products will have the same configuration and customization interface across their native and Web variants and across platforms.

> **Note:** The current version of the SDK no longer supports native variants of the Face
> Video and Proof of Address modules. Web variants are provided instead

### Web module versioning and pinning

When a Web module is loaded dynamically at runtime from the Entrust CDN, the version corresponding to the underlying SDK will be used.

<br/>
**Note that**: the ability to target different compatible versions will not be provided to integrators directly.

> **Note:** The ability to remotely override the use of native modules by selecting
> specific web module versions will be introduced in an upcoming release

### Importing native modules

While the SDKs are capabable of initializing any module at runtime without the module being available locally as part of the bundle (apart from NFC that is only available as a native dependency), each module can be included individually if required.

  
### Android

    Modules are released as individual packages on Maven Central and can be added to a project alongside the base [`:core`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/core) package:

    | Module      | Description                                                                                               | Android package |
    | ----------- | --------------------------------------------------------------------------------------------------------- | --------------- |
    | Welcome     | Optional welcome screen shown to the user with preliminary instructions                                   | [`:welcome`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/welcome)      |
    | Document    | Set of screens that control the capture of the user's identity document (not for address verification)    | [`:document`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/document)      |
    | NFC         | Set of screens that control the scanning of the user's NFC-capable document. Requires the Document module | [`:nfc`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/nfc)              |
    | Face Photo  | Set of screens that control the capture of a selfie of the user                                           | [`:face.photo`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/face.photo)   |
    | Face Motion | Set of screens that controls the motion capture of the user's face                                        | [`:face.motion`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/face.motion)  |
    | BiometricToken | Handles on-device storage and retrieval of encrypted biometric authentication tokens               | [`:biometric.token`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/biometric.token) |
    | Consent     | Consent notice shown to the user before capture, recording whether they accept or decline                 | [`:consent`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/consent)      |
    | Retry       | Status screen shown when a workflow Retry task runs, giving the reason and offering a further attempt     | [`:retry`](https://central.sonatype.com/artifact/com.entrust.identity.verification.sdk/retry)        |

  

  
### iOS

    Modules are packaged as independent Swift modules that can be added to your main project alongside the base `EntrustIdv` package:

    | Module      | Description                                                                                               | iOS framework                    |
    | ----------- | --------------------------------------------------------------------------------------------------------- | -------------------------------- |
    | Welcome     | Optional welcome screen shown to the user with preliminary instructions                                   | `Welcome` |
    | Document    | Set of screens that control the capture of the user's identity document (not for address verification)    | `Document`                       |
    | NFC         | Set of screens that control the scanning of the user's NFC-capable document. Requires the Document module | `NFC`                            |
    | Face Photo  | Set of screens that control the capture of a selfie of the user's face                                    | `FacePhoto`                      |
    | Face Motion | Set of screens that controls the motion capture of the user's face                                        | `FaceMotion`                     |
    | BiometricToken | Handles on-device storage and retrieval of encrypted biometric authentication tokens               | `BiometricToken`                 |
    | Consent     | Consent notice shown to the user before capture, recording whether they accept or decline                 | `Consent`                        |
    | Retry       | Status screen shown when a workflow Retry task runs, giving the reason and offering a further attempt     | `Retry`                          |

  

  
### React Native

    The Entrust IDV SDK for React Native is available from [Node Package Manager (NPM)](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative). It contains the necessary integration API of the Entrust IDV SDK which automatically links the core libraries required for the project's respective Android and iOS applications.

    Importing the NPM package should be executed from the root of your React Native project as detailed below:

    ```shell
    $ npm install @entrust.corporation/idvsdk-reactnative

    or

    $ yarn add @entrust.corporation/idvsdk-reactnative
    ```

    **Note**: You cannot use this SDK with React Native Expo. If your project already uses Expo, you will need to import the SDK manually as described in the [official Expo guide](https://docs.expo.dev/workflow/customizing/).

    Native modules are published as separate NPM packages that are installed alongside the core package. Each one bundles the native iOS framework and the Android artifact for a single module, and exposes no JavaScript API of its own — modules are still driven through the core package.

    | Module         | Description                                                                                               | React Native package |
    | -------------- | --------------------------------------------------------------------------------------------------------- | -------------------- |
    | Welcome        | Optional welcome screen shown to the user with preliminary instructions                                   | [`@entrust.corporation/idvsdk-reactnative-welcome`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-welcome) |
    | Document       | Set of screens that control the capture of the user's identity document (not for address verification)    | [`@entrust.corporation/idvsdk-reactnative-document`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-document) |
    | NFC            | Set of screens that control the scanning of the user's NFC-capable document. Requires the Document module | [`@entrust.corporation/idvsdk-reactnative-nfc`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-nfc) |
    | Face Photo     | Set of screens that control the capture of a selfie of the user                                           | [`@entrust.corporation/idvsdk-reactnative-face-photo`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-face-photo) |
    | Face Motion    | Set of screens that controls the motion capture of the user's face                                        | [`@entrust.corporation/idvsdk-reactnative-face-motion`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-face-motion) |
    | BiometricToken | Handles on-device storage and retrieval of encrypted biometric authentication tokens                      | [`@entrust.corporation/idvsdk-reactnative-biometric-token`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-biometric-token) |
    | Consent        | Consent notice shown to the user before capture, recording whether they accept or decline                 | [`@entrust.corporation/idvsdk-reactnative-consent`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-consent) |

    The Retry module is native on Android and iOS, but has no React Native package. On React Native it runs as a Web module, so no installation is required.

    Two further packages are not verification modules. They carry native frameworks shared by several modules, and are installed as peer dependencies of the modules that require them:

    | Shared dependency     | Required by                              | React Native package |
    | --------------------- | ---------------------------------------- | -------------------- |
    | Device Security       | Document, NFC, Face Photo, Face Motion   | [`@entrust.corporation/idvsdk-reactnative-device-security`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-device-security) |
    | Analytics Events Face | Face Photo, Face Motion                  | [`@entrust.corporation/idvsdk-reactnative-analytics-events-face`](https://www.npmjs.com/package/@entrust.corporation/idvsdk-reactnative-analytics-events-face) |

  

> ℹ️ **Note:** Whether imported as a native module or not, all modules are available at runtime as Web modules

#### Adding modules to the base bundle

  
### Android

    Native kotlin modules can be added manually to the embedding app's `build.gradle.kts` file by including the required dependencies as shown below:

    ```kotlin
    dependencies {
      ...
      // SDK core libraries
      implementation("com.entrust.identity.verification.sdk:core:<<version>>")       // Always required

      // Native modules

      implementation("com.entrust.identity.verification.sdk:document:<<version>>")
      implementation("com.entrust.identity.verification.sdk:nfc:<<version>>")
      implementation("com.entrust.identity.verification.sdk:face.photo:<<version>>")
      implementation("com.entrust.identity.verification.sdk:face.motion:<<version>>")
      implementation("com.entrust.identity.verification.sdk:biometric.token:<<version>>")
      implementation("com.entrust.identity.verification.sdk:consent:<<version>>")
      implementation("com.entrust.identity.verification.sdk:retry:<<version>>")
    }
    ```

  

  
### iOS

    The native IDV Swift modules are managed via Swift Package Manager and can be added to your app's project as regular Swift "Package Dependencies" via Xcode. The official [Xcode documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) details the required steps.

    The following Entrust IDV SDK packages can be retrieved by searching for package `https://github.com/entrustCorporation/IdvSdk-iOS` in the "Add Package" interface:

    <img src="./add-swift-package-screen.png" alt="Adding a Swift Package" />

  

  
### React Native

    Module packages are installed from the root of your React Native project, alongside the core package. Each module must be installed together with its peer dependencies:

    | Module package                                             | Peer dependencies to install alongside it |
    | ---------------------------------------------------------- | ----------------------------------------- |
    | `@entrust.corporation/idvsdk-reactnative-welcome`          | `@entrust.corporation/idvsdk-reactnative` |
    | `@entrust.corporation/idvsdk-reactnative-document`         | `@entrust.corporation/idvsdk-reactnative`, `@entrust.corporation/idvsdk-reactnative-device-security` |
    | `@entrust.corporation/idvsdk-reactnative-nfc`              | `@entrust.corporation/idvsdk-reactnative`, `@entrust.corporation/idvsdk-reactnative-device-security` |
    | `@entrust.corporation/idvsdk-reactnative-face-photo`       | `@entrust.corporation/idvsdk-reactnative`, `@entrust.corporation/idvsdk-reactnative-device-security`, `@entrust.corporation/idvsdk-reactnative-analytics-events-face` |
    | `@entrust.corporation/idvsdk-reactnative-face-motion`      | `@entrust.corporation/idvsdk-reactnative`, `@entrust.corporation/idvsdk-reactnative-device-security`, `@entrust.corporation/idvsdk-reactnative-analytics-events-face` |
    | `@entrust.corporation/idvsdk-reactnative-biometric-token`  | `@entrust.corporation/idvsdk-reactnative` |
    | `@entrust.corporation/idvsdk-reactnative-consent`          | `@entrust.corporation/idvsdk-reactnative` |

    For example, to add the Face Motion module to a project that already has the core package installed:

    ```shell
    $ npm install @entrust.corporation/idvsdk-reactnative-face-motion @entrust.corporation/idvsdk-reactnative-device-security @entrust.corporation/idvsdk-reactnative-analytics-events-face

    or

    $ yarn add @entrust.corporation/idvsdk-reactnative-face-motion @entrust.corporation/idvsdk-reactnative-device-security @entrust.corporation/idvsdk-reactnative-analytics-events-face
    ```

    > ⚠️ **Warning:** Peer dependencies must be installed explicitly. On iOS, every package vendors only its own frameworks, and React Native autolinking links a package only when it is present in `node_modules`. If a peer is missing, the shared framework it provides is left out of the app and the build fails to link. On Android, Gradle resolves the shared artifacts transitively, so an omitted peer breaks only the iOS build.

    Module packages track the version of the core package, so install the same version of each.

    <p><strong>Building the app</strong></p>

    No manual Xcode or Gradle changes are required — autolinking configures both platforms:

    - **iOS**: run `pod install` from the `ios/` directory. CocoaPods reads each package's podspec and embeds the vendored `.xcframework` binaries into your target.
    - **Android**: build the project as usual. Each package contributes a Gradle module that resolves the matching `com.entrust.identity.verification.sdk` artifact from Maven Central.

    **Note**: the React Native Consent package carries the Android artifact only. It vendors no iOS framework, so installing it has no effect on an iOS build, and the Consent module runs as a Web module there. The Entrust IDV SDK for iOS does ship a native `Consent` framework for direct iOS integrations.

  

#### Adding language/translation files to the base bundle

  
### Android

     Similarly to adding native Kotlin modules, language bundles can be added manually to the embedding app's `build.gradle.kts` file by including the required dependencies as shown below:

    ```kotlin
    dependencies {
      // `en-gb` can be replaced with any of the supported locales
      implementation("com.entrust.identity.verification.sdk:translations-complete-en-gb:<<version>>")
    }
    ```
    **Note**: By default, the SDK is able to render its screens in any of the supported languages by retrieving the translations files dynamically from the Entrust CDN. Including a file in the local bundle will increase the SDK's responsiveness but will contribute to the overall app size. We recommend to only include the most commonly used languages in your app.

    For the full list of supported locales, please refer to the [Appendix](#list-of-supported-languages-and-locales).

  

  
### iOS

    The ability to add language files in the app bundle will be made available in an upcoming release.

  

  
### React Native

    > **Note:** The Entrust IDV SDK for React Native does not yet support embedding translation files in the app bundle
> Translations are retrieved dynamically from the Entrust CDN at runtime. Native verification modules can be embedded — see [Adding modules to the base bundle](#adding-modules-to-the-base-bundle).
> Upcoming versions of the React Native SDK will bring translation bundling to parity with the underlying iOS and Android SDKs.

  

# SDK Initialization and Orchestration

The SDK is initialized in three steps:

- Applicant generation
- Workflow run and credential generation
- SDK runtime initialization

The process is optimized for SDK sessions based on workflows predefined in Studio. Workflow-based SDK sessions may contain the full IDV product suite and benefit from a simplified API integration.

## Applicant generation

'Applicant IDs' are used to track an IDV session across the capture and report verification stages. An `applicant_id` is required to generate the SDK token.
This step applies to both Studio workflow and non-Studio-workflow-based verification flows.

Please refer to the [Create applicant](/api/latest/#create-applicant) section of our API documentation for more details.

⚠️ As this request is authenticated with an API token, it is imperative to perform it from a secure backend.

### Applicant reuse

When defining workflows and creating identity verifications, we highly recommend saving the `applicant_id` against a specific user for potential reuse. This helps to keep track of users should you wish to run multiple identity verifications on the same individual, or in scenarios where a user returns to and resumes a verification flow.

An `applicant_id` however must not be reused for different users.

## Workflow run and authentication token generation

The integration of the latest SDK versions has been simplified by only requiring a Studio SDK token for authentication and orchestration. This token is available from the successful response to the workflow run creation API request. This request combines the instantiation of a Studio workflow predefined in the Workflow Builder with the linking to a unique applicant.

**Please note:** Studio SDK tokens are bound in time and scope to the validity of the underlying workflow run.
Please refer to the [Create worfklow run](/api/latest/#create-workflow-run) section of our API documentation for more details.

For integrations that are yet to migrate to workflow-based orchestration in Studio, a manual SDK token must be generated as defined in the [Generate SDK token](/api/latest/#generate-sdk-token) section of our API documentation. Generic SDK tokens are limited to a 90-minute expiry.
It is highly recommended for integrators to adopt Studio workflows for a simpler, more secure and future-proof integration.

⚠️ As both the Create workflow run and Generate SDK token API requests are authenticated with an API token, it is imperative to perform them from a secure backend.

### Handling expired tokens

The ability to automatically refresh expired tokens has been discontinued from the new SDKs.

It is highly recommended to adopt workflow-based flows and tokens to ensure that tokens remain valid for the same duration as the underlying workflow and thus reduce the impact to end users.

## SDK runtime initialization

This new generation of SDKs provide a common API for initialization and customization of SDK sessions across all platforms.

While the overall bootstrapping code is platform-specific, its contents are consistent in their structure and typing.
The minimum integration code for the SDK to function requires an SDK token for authentication and the implementation of the three key callbacks that notify the embedding app when the SDK has completed its flow or has encountered errors:

  
### Android

    ```kotlin
    class MainActivity : AppCompatActivity() {
      ...
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        EntrustIdv.start(
          StudioParameters(
            sdkToken = studioToken
            )
          /* For clients that are yet to migrate to workflow-based verification flows, ClassicParameters would need to be provided instead of StudioParameters
          ClassicParameters(
            sdkToken = sdkToken,
            steps = listOf>(
                Welcome(),
                FaceMotion(),
                Document(),
          ))
          */
      )}

      private val entrustIdv = EntrustIdv(
        activity = this,
        callbacks = Callbacks(
          onComplete = { result ->
              // Note that the `result` payload will be an empty Map object for workflow-based sessions as the data is provided by dedicated webhooks to your integration
              Log.d("MainActivity", "The verification flow was completed successfully with result $result")
          },
          onError = { error ->
              Log.e("MainActivity", "Error category:${error.type.category} / error type ${error.type.name}/${error.type.message} / metadata: ${error.message}")
          },
          onUserExit = { userAction ->
              Log.d("MainActivity", "The user exited the verification flow. Reason: $exitReason")
          },
      ))

    }
    ```

    ### Entrust IDV Android Process - Custom Application Class

    **Note**: You can skip this step if you do not have any custom application class.

    The Entrust IDV SDK for Android runs in a separate process. This means that when the SDK in initialized, a new application instance will be created.
    To prevent re-executing the initializations you have in the Android application class, you can use the `isEntrustIDVProcess` extension function and return from `onCreate` as shown below:

    ```kotlin
    import com.onfido.sdk.api.isEntrustIDVProcess

    class YourCustomApplication : MultiDexApplication() {
      override fun onCreate() {
          super.onCreate()
          if (isEntrustIDVProcess()) {
              return
          }

          // Your custom initialization calls ...
      }
    }
    ```

    This will prevent initialization-related crashes such as: `FirebaseApp is not initialized in this process`

    > ⚠️ **Warning:** The Entrust IDV process uses your custom `Application` class for its own initialization. If you decide to use `isEntrustIDVProcess` to selectively skip the initialization of certain instances during the Entrust IDV process, be cautious not to access these uninitialized instances elsewhere in your `Application` class, such as in the `onTrimMemory` method.
> Furthermore, instances initialized by providers such as Firebase will not be reinitialized in the Entrust IDV process. If you wish to use such instances within the Entrust IDV process, you will need to manually initialize them as described [in the official Firebase documentation](https://firebase.google.com/docs/reference/android/com/google/firebase/FirebaseApp#initializeApp\(android.content.Context\)).

    ### Dynamic Feature Module (DFM)

    The latest Entrust IDV SDK for Android has been optimized for use within a Dynamic Feature Module (DFM). The advantage of this configuration is that it reduces the SDK size to essentially zero, as it is only initialized at runtime.
    While the responsibility of the delivery and installation of the DFM is still with the integrator, the process of adding the Entrust IDV dependencies has been simplified as described below:

    1. Create a Dynamic Feature module for your application
    2. In your main applications's gradle file, add `implementation("com.onfido.sdk:api:<<version>>")` in the list of dependencies. This dependency ensures that once declared (next step), all Entrust IDV modules can be located by the DFM process and that the SDK can be configured and launched directly from your application module
    3. Add all required native Entrust IDV modules in the dynamic module's gradle file, as explained in the [Adding modules to the base bundle](#adding-modules-to-the-base-bundle) section
    4. Complete the DFM configuration:

    * You can decide how the module will be installed and this won’t affect Entrust IDV SDK behavior
    * Configure [install-time delivery](https://developer.android.com/guide/playcore/feature-delivery/install-time)
    * Configure [conditional delivery](https://developer.android.com/guide/playcore/feature-delivery/conditional)
    * Configure [on-demand delivery](https://developer.android.com/guide/playcore/feature-delivery/on-demand)

    5. Before launching the Entrust IDV SDK, make sure your dynamic feature is [installed and ready](https://developer.android.com/guide/playcore/feature-delivery/on-demand#manage_installed_modules)
    6. Launch the Entrust IDV SDK normally

  

  
### iOS

    ```swift

    @MainActor
      func startEntrustIdvSDK(from viewController: UIViewController) {

        let parameters = StudioParameters(
          sdkToken: studioToken
        )

        /* For clients that are yet to migrate to workflow-based verification flows, ClassicParameters would need to be provided instead of StudioParameters
        let parameters = ClassicParameters(
          sdkToken: sdkToken,
          steps: [
            Welcome(),
            FaceMotion(),
            Document()
          ]
        )
        */

        let callbacks :Callbacks = Callbacks(
            onComplete: { (results: [String: CaptureResult]) in
              // Note that the `result` payload will be an empty object for workflow-based sessions as the data is provided by dedicated webhooks to your integration
              print("onComplete - Verification flow completed with results per module: ")
              for (key, result) in results {
                  print(" - \(key): \(result.type)")
              }
            },
            onError: { (error: IdvError) in
              print("onError - Verification flow failed with error \(error.type) (category: \(error.type.category)) with (optional) message \(error.message)")
            },
            onUserExit: { (userAction: UserAction) in
              print("onUserExit - The user exited the verification flow: \(userAction)")
            }
        )

        let entrustIdv = EntrustIdv(sdkParameters: parameters, callbacks: callbacks)
        entrustIdv.start(from: viewController)
      }
    ```

  

  
### React Native

    ```javascript
    const entrustIdv = new EntrustIdv({
      onComplete: (result) => {
        console.log('onComplete - Verification flow completed: ', result)
      },
      onError: (error) => {
        console.log(
          `onError - Verification flow failed with error ${error.type} (category: ${error.type.category}) with (optional) message ${error.message}`
        )
      },
      onUserExit: (userAction) => {
        console.log(
          'onUserExit - The user exited the verification flow: ',
          userAction
        )
      },
    })

    entrustIdv.start({
      sdkToken: 'your token here',

      /* For clients with integrations that are yet to migrate to workflow-based verification flows, the required 'steps' must be provided alongside the SDK token. They are available in the '@onfido/capture-api/steps/<step>' packages that must be imported at the start of the file.
      steps: [
        Welcome(),
        Document(),
        FacePhoto(),
        FaceVideo(),
        FaceMotion(),
      ],
      */
    })
    ```

  

### `onComplete(result)` callback

The `onComplete` callback is triggered as soon as the last possible capture step defined in the underlying verification workflow has been completed without error.
The `result` payload is an empty Map object (or equivalent) as information about the files uploaded by the end user during the session should be retrieved via the dedicated [Document](/api/latest/#list-documents), [Face Photo](/api/latest/#list-live-photos) and [Face Video](api/latest/#list-live-videos) API endpoints.

**Please note:** for integrations that are yet to migrate to workflow-based sessions in Studio, the `onComplete` callback will return a payload that details the identifiers of the media resulting from this flow.
In this construct, the `result` is a Map (or equivalent) object, mapping the module identifier (key) to an object containing the unique `id` of the uploaded media. These identifiers can then be used to retrieve the full document or face capture using the corresponding `document`, `live_photos` (for 'standard' selfies) or `live_videos` (for 'video' or 'motion' captures) endpoints defined in the API reference.

The `json` example below gives a serialized representation of the `result` payload returned at the end of non-workflow-based flows.

**Note**:

- The Map's keys are the same as the corresponding step's name
- At runtime, this payload will be typed for ease of integration

```json
{
  "document": {
    "type": "driving_licence",
    "sides": {
      "front": {
        "id": ""
      },
      "back": {
        "id": ""
      },
      "front_video": {
        "id": ""
      },
      "back_video": {
        "id": ""
      }
    }
  },
  "facePhoto": {
    "id": ""
  },
  "faceVideo": {
    "id": ""
  },
  "faceMotion": {
    "id": ""
  },
  "poa": {
    "type": "utility_bill",
    "sides": {
      "front": {
        "id": ""
      },
      "back": {
        "id": ""
      },
      "front_video": {
        "id": ""
      },
      "back_video": {
        "id": ""
      }
    }
  }
}
```

### `onError(error)` callback

The `onError` callback is triggered when the SDK flow can no longer proceed due to an underlying operating system, browser or library issue.
The `error` payload contains an error `category` property, paired with a more granular error `type`. This is intended to help integrators with handling errors over time as new codes are introduced under the existing `category` values.
The `message` field is optional and may contain additional debug information.

The `json` example below gives a serialized representation of the `error` payload returned at the end of non-workflow-based flows. At runtime, this payload will be typed for ease of integration.

```json
"error": {
    "errorType":{
        "category": <>,
        "type": <>
    },
    "message": string
}
```

| Category             | Type                                         | Description                                                                                                                                        |
| -------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Initialisation`     | `InitialisationInvalid`                      | Generic flow initialization error                                                                                                                  |
| `Initialisation`     | `SdkVersionInsufficient`                     | Flow cannot be initialized due to invalid SDK version                                                                                              |
| `Initialisation`     | `UnsupportedError`                           | Flow cannot be initialized due to a selected module not being supported (environment or client)                                                    |
| `Initialisation`     | `UnsupportedFeatureError`                    | Flow cannot be initialized due to a selected feature not being supported (environment or client)                                                   |
| `Initialisation`     | `InvalidSdkParameter`                        | Flow cannot be initialized due to invalid initialization parameters                                                                                |
| `Initialisation`     | `FeaturesNotAuthorized`                      | The flow cannot be initialized as selected feature is not authorized for this account. Please talk to Customer Support to get this feature enabled |
| `Initialisation`     | `MissingSteps`                               | Flow cannot be initialized due to missing 'steps' declaration or lack of workflow-run-id                                                           |
| `Initialisation`     | `DuplicateStep`                              | Flow cannot be initialized due to duplicate declaration in 'steps'                                                                                 |
| `Initialisation`     | `WelcomeMustBeFirstStep`                     | Flow cannot be initialized if 'Welcome' step is present but not first in the list of steps                                                         |
| `Initialisation`     | `WorkflowVersionMismatch`                    | Flow cannot be initialized due to target workflow requiring a higher version of the SDK                                                            |
| `Initialisation`     | `WorkflowInputError`                         | Flow error due to invalid or missing inputs provided as part of the workflow initialization                                                        |
| `Initialisation`     | `MissingLogoCobrandingParameter`             | Flow cannot be initialized as logo cobranding requires both a light and dark mode logo image                                                       |
| `Initialisation`     | `InvalidCustomTranslations`                  | Flow cannot be initialized due to an invalid translation key being provided                                                                        |
| `Initialisation`     | `InvalidCountryCode`                         | Flow cannot be initialized due to an invalid country code provided in the document step configuration                                              |
| `Initialisation`     | `InvalidDocumentFormatAndCountryCombination` | Flow cannot be initialized due to an invalid country code / document type combination being provided in the document step configuration            |
| `Initialisation`     | `InvalidDocumentTypeException`               | DocumentType.UNKNOWN should not be used                                                                                                            |
| `Initialisation`     | `InvalidDocumentTitle`                       | Flow cannot be initialized due to an invalid generic document title provided in the document step configuration                                    |
| `Initialisation`     | `DuplicateGenericDocument`                   | Flow cannot be initialized due to a duplicate generic document definition provided in the document step configuration                              |
| `Authentication`     | `InvalidToken`                               | Flow cannot be initialized due to invalid authentication token                                                                                     |
| `Authentication`     | `ExpiredToken`                               | Flow cannot be initialized due to expired authentication token                                                                                     |
| `Authentication`     | `ExpiredTrial`                               | Flow error due to expired account (or exceeded trial attempts)                                                                                     |
| `DeviceCapabilities` | `CameraNotDetected`                          | Flow error due to camera not found                                                                                                                 |
| `DeviceCapabilities` | `CameraException`                            | Flow error due to generic camera-related issue                                                                                                     |
| `Processing`         | `FailedToWriteToDisk`                        | (Mobile-only) Flow error due to inability to write to device                                                                                       |
| `Processing`         | `InvalidImageData`                           | Flow error due to error in image compression or on-device processing                                                                               |
| `Processing`         | `WorkflowTaskAbandoned`                      | Flow error due to workflow already been completed or expired                                                                                       |
| `Processing`         | `WorkflowTaskError`                          | Flow error due to generic workflow error                                                                                                           |
| `Processing`         | `BiometricTokenRetrievalCustomerUserHashMissing`           | Flow error due to customer user hash missing when retrieving local face authentication token             |
| `Processing`         | `BiometricTokenRetrievalEncryptedBiometricTokenNotFound`   | Flow error due to no token found for the provided customer user hash when retrieving local face authentication token |
| `Processing`         | `BiometricTokenStorageCustomerUserHashMissing`             | Flow error due to customer user hash missing when storing local face authentication token               |
| `Processing`         | `BiometricTokenStorageEncryptedBiometricTokenMissing`      | Flow error due to encrypted biometric token missing when storing local face authentication token        |
| `Processing`         | `BiometricTokenStorageError`                               | Flow error due to failure when storing local face authentication token                                  |
| `Network`            | `NetworkException`                           | Generic flow error due to network issue                                                                                                            |
| `Network`            | `UploadError`                                | Flow error due to failed media upload                                                                                                              |
| `General`            | `AppTerminated`                              | Flow error due to app containing SDK being externally terminated                                                                                   |
| `General`            | `GeoBlocked`                                 | Flow error due to request coming from geo-blocked country                                                                                          |
| `General`            | `GenericException`                           | Generic flow error due to unknown error type                                                                                                       |

### `onExit(userAction)` callback

The `onExit` callback is triggered when the end user manually exits the verification flow.
It is intended to provide the integrator with the opportunity to re-engage with the end user and potentially offer alternative verification flows.
The `userAction` payload can be one of the following:

| `userAction`              | Description                                                                                              |
| ------------------------- | -------------------------------------------------------------------------------------------------------- |
| `UserExit`                | The end user exited the flow by pressing the UI's 'close' (X) button                                     |
| `ConsentDenied`           | For US-based end users, the applicant declined to proceed when presented with the BIPA consent statement |
| `RequiredNfcNotCompleted` | The end user decided not to proceed with NFC verification in flows that require it                       |

### Optional callbacks

#### Biometric token handlers

When using Studio [authentication tasks](/getting-started/workflow-studio-product/#authentication-tasks) with on-device storage, the SDK stores encrypted biometric tokens on the end user's device or browser local storage. The SDK also allows clients to take control of the token lifecycle and exposes a callback to override the default implementation to read and write the token, so it can be stored on device, in cloud, in a keystore or in your infrastructure.

**Please note:**

- Defining the callback will prevent the SDK from storing the encrypted biometric token
- To store the encrypted biometric token in your infrastructure, it is recommended to [rely on webhooks](/getting-started/workflow-studio-product/#authentication-on-customer-infrastructure) instead

  
### Android

    ```kotlin
    private val entrustIdv = EntrustIdv(
      ...
      callbacks = Callbacks(
        ...
        biometricsTokenHandler = object : BiometricsTokenHandler {
          override suspend onTokenGenerated(customerUserHash: String, biometricToken: String) {
              // Called when a new biometric token is generated during enrollment
              // Please ensure that customerUserHash to biometricToken relationship is 1:1
          }

          override suspend onTokenRequested(customerUserHash: String, provideToken: (String) -> Unit) {
              // Called when the biometric token is requested during re-authentication
          }
        },
      ),
    )
    ```

  

  
### iOS

    ```swift
    public protocol BiometricsTokenHandler {
    // Called when a new biometric token is generated during enrollment
    // Please ensure that customerUserHash to biometricToken relationship is 1:1
    func onTokenRequested(customerUserHash: String) async -> String

    // Called when the biometric token is requested during re-authentication
    func onTokenGenerated(customerUserHash: String, encryptedBiometricToken: String)
    }
    ```

  

  
### React Native

    ```javascript
    // Called when a new biometric token is generated during enrollment
    // Please ensure that customerUserHash to biometricToken relationship is 1:1
    func onTokenRequested(customerUserHash: string, encryptedBiometricToken: string) => string;

    // Called when the biometric token is requested during re-authentication
    func onTokenGenerated(customerHash: string) => string;
    ```

  

#### `onAnalyticsEvent(event)` callback

The `onAnalyticsEvent` callback is an optional integration feature that requires activation from the [Customer Support team](mailto:identity-client-support@entrust.com).
It is triggered whenever the end user reaches a new screen and is intended to provide integrators with a high-level view of the flow's progress and allow for some targeted progress and drop-off analysis.
This mechanic is **not recommended** for most integrations.

  
### Android

    ```kotlin
    Callbacks(
      ...
      onAnalytics = { event: IdvEvent ->
          Log.d("onAnalytics", "User Analytics triggered: $event")
      },
    )
    ```
  

  
### iOS

    ```swift
    let callbacks :Callbacks = Callbacks(
      ...
      onAnalytics: { (event: IdvEvent) in
          print("onAnalytics - User Analytics triggered: \(event)")
      },
    )
    ```
  

  
### React Native

    ```javascript
    const entrustIdv = new EntrustIdv(
      {
        ...
        onAnalytics: (event: IdvEvent) => {
          console.log('userEvent - User Analytics triggered: ', event.identifier)
        }
      },
    );
    ```
  

The `IdvEvent` payload is typed according to each platform but can be generalized in the following JSON payload:

```json
{
  "identifier": <>,
  "eventMetadata": {
    "runtimeId": <>,
    "generatedAt": "2025-10-05T20:02:00+01:00"
  }
}
```

Additionally, optional payloads may be returned with any event within the `eventMetadata` object. More details will be documented in future releases.

#### `onMedia` callback

The `onMedia` callback is an optional integration feature that requires activation from the [Customer Support team](mailto:identity-client-support@entrust.com).
The callback is triggered at the end of every applicable module execution once the feature has been enabled for a given account and the callback described in this section has been implemented.

**Note**:

- Only the Document, Face Photo and Face Video modules return a payload
- The payload is always sent to the Entrust backend, independently from the execution of this callback

> ⚠️ **Warning:** This mechanic is **not recommended** for most integrations for security and performance reasons. The recommendation is to retrieve the media payloads from the corresponding Studio webhooks and API requests

  
### Android

    ```kotlin
    Callbacks(
      ...
      onMedia = { media: MediaResult ->
        Log.d("onMedia", "The onMedia callback has been triggered: $media")
      },
    )
    ```
  

  
### iOS

    ```swift
    let callbacks :Callbacks = Callbacks(
      ...
      onMedia: { (media: MediaResult) in
          print("onMedia - The onMedia callback has been triggered: \(media)")
      },
    )
    ```
  

  
### React Native

    More information will be added in the coming months regarding the **React Native** platform requirements.
  

The `MediaResult` payload is structured as follows, with the `documentMetadata` only present as a result of document capture:

```json
{
  "key": string,            // Studio task ID or step
  "mediaFile": {
    "fileData": <<>Blob>>,
    "fileType": string,
    "fileName": string,
  }
  "documentMetadata": {
    "type": string,
    "issuingCountry": string?,
    "side": string,
  }
}
```

## SDK permissions

In order to launch successfully and for individual capture modules that require the camera or microphone to function, the SDK requires specific App or browser permissions.

| Module           | Permissions required                                          |
| ---------------- | ------------------------------------------------------------- |
| Document         | Camera (back)<br />NFC<br />Storage                           |
| Proof of Address | Camera (back)<br />Storage                                    |
| Face Photo       | Camera (front)                                                |
| Face Video       | Camera (front)<br />Audio                                     |
| Face Motion      | Camera (front)<br />Audio (unless capture option is disabled) |

Modules not listed above do not require dedicated permissions.

### SDK permission inheritance

User permissions have been optimized to reduce the number of requests made to the end user. By default, if the embedding app has already requested a given permission from the end user, the SDK will automatically inherit that permission and not request it again.

Additionally, permissions are shared across all modules invoked by the SDK, whether in their native or Web form.

**Note** that permissions still need to be declared as described in the next section.

### SDK permission requests

  
### Android

    For Android apps, permissions don't need to be explicitly declared since they are handled by the SDK.
  

  
### iOS

    For iOS apps, permissions must be declared in the application's `Info.plist` file.

    <table>
      <tr>
        <th> Permissions </th>
        <th> OS instruction </th>
      </tr>

      <tr>
        <td> Camera (combined front/back) </td>

        <td>
          <pre lang="xml">
            <key>NSCameraUsageDescription</key>
            <string>Required for document and face capture</string>
          </pre>
        </td>
      </tr>

      <tr>
        <td>Audio</td>

        <td>
          <pre lang="xml">
            <key>NSMicrophoneUsageDescription</key>
            <string>Required for video capture</string>
          </pre>
        </td>
      </tr>

      <tr>
        <td> NFC </td>

        <td>
          <pre lang="xml">
            ```xml
            <key>NFCReaderUsageDescription</key>
            <string>Required to read ePassports</string>
            <key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
            <array>
              <string>12FC</string>
            </array>
            <key>
              com.apple.developer.nfc.readersession.iso7816.select-identifiers
            </key>
            <array>
              <string>A0000002471001</string>
              <string>A0000002472001</string>
              <string>00000000000000</string>
              <string>D2760000850101</string>
            </array>
            ```
          </pre>
        </td>
      </tr>

      <tr>
        <td> Storage </td>

        <td>
          <pre lang="xml">
            <key>NSPhotoLibraryAddUsageDescription</key>

            <string>
              Required to read images from the photo library, when uploading documents
            </string>
          </pre>
        </td>
      </tr>
    </table>

    **Please note:**

    * The iOS SDK requires `CoreNFC` to run (regardless of whether you use NFC or not). Since Xcode 12, there is a bug where `libnfshared.dylib` is missing from simulators. Refer to [Stack Overflow](https://stackoverflow.com/questions/63915728/xcode12-corenfc-simulator-library-not-loaded) for a solution to this problem
    * In the event that you disable the NFC feature, Apple might ask you to provide a video to demonstrate NFC usage because NFC-related code is part of the SDK binary, regardless of your configuration. You can find a video demonstrating our NFC feature that you can submit to Apple [here](https://github.com/onfido/onfido-ios-sdk/blob/32.5.2/assets/nfc_demo.mov)

    ### Apple entitlements for NFC

    In addition to the app permissions defined in the app's `Info.plist` file, NFC requires the `Near Field Communication Tag Reading` capability in your app target. If you haven't added it before, please follow the steps in [Apple's documentation](https://help.apple.com/xcode/mac/current/#/dev88ff319e7).

    Additionally, to support NFC PACE documents (such as NFC-capable national identity cards), additional app entitlements must be specified:

    * Add a new entry nested under the `Near Field Communication Tag Reader Session Formats` key
    * Select `Password Authenticated Connection Establishment (PACE)` from the dropdown list
    * Alternatively you can also edit your entitlements, with the following entries:

    ```xml
    <key>com.apple.developer.nfc.readersession.formats</key>
    <array>
        <string>PACE</string>
        <string>TAG</string>
    </array>
    ```

  

  
### React Native

    In the context of the Entrust IDV SDK for React Native, permissions are declared and handled by the underlying Android and iOS SDKs.

    Please refer to the Android and iOS tabs of this section for instructions on how to declare the permissions and entitlements required by the Entrust IDV SDK.

  

### Declined Permissions

If the embedding app has correctly declared the required permissions, the end user will be prompted at runtime to grant them:

- If the user declines any of the operating system or browser prompts, the SDK will attempt to recover the required permissions by providing instructions to the user
- If the user permanently blocks the required permissions (App level), the SDK will not be able to proceed and terminate in error

**Please note**:

- When the user declines permissions on iOS, regranting them requires the embedding app to be restarted. Therefore, when the SDK encounters a declined permission on iOS it will error with the dedicated "permissions-unavailable" code
- In rare cases where the embedding is offloaded from memory (for example after being in the background for a long duration) and the user has granted permissions only for the current session, the end user might be prompted again for permissions when the App is brought back into the foreground

## SDK configuration

This section details the optional SDK configurations that can be applied during the initialization of any given capture flow.

### User Interface (UI) customization

Until made available via Workflow Studio, customization of the SDK UI is possible as part of the SDK initialization script under the property `theme`.

By default, the SDK uses the operating system or browser theme (i.e. Dark or Light) when rendering screens. The **optional** `theme` object allows integrators to preselect the overall theme and override specific UI properties.

#### Pre-selecting Light or Dark mode

- **`mode {ThemeMode}` - optional**

The optional `mode` property is used to pre-select the base theme used by the SDK. It accepts either `Light` or `Dark` as values. By default, the SDK applies the operating system or browser theme (i.e. Dark or Light) when rendering screens.

**Note**: The camera capture screens of the SDK are always displayed in 'Dark' mode.

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      EntrustIdv.start(
      ...
        configuration = Configuration(
          ...
          theme = Theme(
            mode = ThemeMode.Light
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        theme: .init(
          ...
          mode: ThemeMode.light
        )
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        theme: {
          mode: ThemeMode.Light,
          ...
        },
      }
    });
    ```
  

#### Customizing UI colors

To avoid the duplication of color overrides when customizing the SDK's UI, the optional `lightColors` and `darkColors` properties are defined under the `theme` interface.
By modifying a color in both colors sets, all UI components that use that base color token will be affected automatically.

The full list of available `lightColors` / `darkColors` tokens is listed in the dedicated [SDK UI Customization Guide](#/sdk/sdk-ui-customization).

**Note**: As the camera capture screens of the SDK are always displayed in 'Dark' mode, it is highly recommended to define all custom colors for both Light and Dark mode.

- **`lightColors [BaseColorToken:String]` - optional**
- **`darkColors  [BaseColorToken:String]` - optional**

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      EntrustIdv.start(
      ...
        configuration = Configuration(
          ...
          theme = Theme(
            ...
            lightColors = mapOf(
              ColorTokens.BackgroundColorBrandDefault to "#FF00FF85",
              ColorTokens.ContentColorBase to "#0B1B0B",
            ),
            darkColors = mapOf(
              ColorTokens.BackgroundColorBrandDefault to "#0000FF85",
              ColorTokens.ContentColorBase to "#FF1CFF",
            ),
        ))
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv( 
      ... 
      configuration: .init( 
        ... 
        theme: .init( 
          ...
          lightColors: [ 
            ColorTokens.backgroundColorBrandDefault: "#FF00FF85",
            ColorTokens.contentColorBase: "#0B1B0B" 
          ], 
          darkColors: [
            ColorTokens.backgroundColorBrandDefault: "#0000FF85",
            ColorTokens.contentColorBase: "#FF1CFF" 
          ], 
      )) 
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        theme: {
          ...
          lightColors: {
            backgroundColorBrandDefault: '#FF00FF85',
            contentColorBase: '#0b1c0b'
          },
          darkColors: {
            backgroundColorBrandDefault: '#0000FF85',
            contentColorBase: '#FF1cFF'
          }
        },
      }
    });
    ```
  

#### Customizing UI assets (fonts, icons, images)

More information will be documented on the process and scope to customize the UI's assets in a future version of this guide.

#### Customizing the brand footer

- **`branding {Object}` - optional**

The optional `branding` object allows integrators to customize the screen's footer to provide brand continuity when the SDK flow is embedded in a 3rd party application. Note that by default, no brand footer is displayed.

The following options are available:

| Property                  | Purpose                                                                                                                      |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `text: String` - Optional | Simple text label that can be provided in conjunction with a logo. When combined, the text will be displayed before the logo |
| `logo: String` - Optional | URL to a publicly accessible SVG image                                                                                       |

**Notes**: The ability to use local assets will be enabled in a future release of the SDK.

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      EntrustIdv.start(
      ...
        configuration = Configuration(
          ...
          theme = Theme(
            ...
            branding = Branding(
              text = "Custom Text",
              logo = "https://upload.wikimedia.org/wikipedia/commons/d/d7/Android_robot.svg",
            ),
        ))
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        theme: .init(
          ...
          branding: .init(
            text: "Custom Text",
            logo: "https://commons.wikimedia.org/wiki/File:Apple_Logo.svg",
          ),
      ))
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        theme: {
          ...
          branding: {
            text: "Custom Text",
            logo: "https://commons.wikimedia.org/wiki/File:React_Native_Logo.png"
          },
        },
      }
    });
    ```
  

**Please note:**

- The integrator needs to consider the theme (Light/Dark) currently applied to the screen when custom text and logo are provided
- The color of the text and logo provided in the `branding` object can be customized via the base color tokens `content-color.subtle` and `background-color.surface.primary.default`
- ⚠️ As a change from previous versions of the SDK, by default, no brand name or logo is shown. It is also no longer required to ask for activation of this feature

### Localization and text customization

Until made available via Workflow Studio, customization of the SDK text and language selection is possible as part of the SDK initialization script under the property `localisation`.

The SDK supports more than 40 languages and regional variants. By default the SDK uses the end user's device or browser locale to determine the language to be rendered on screen. If the locale is not supported or cannot be derived, all screens will be rendered in `en-US` English.

The list of available languages is available in the [Appendix](#list-of-supported-languages-and-locales).

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      EntrustIdv.start(
      ...
        configuration = Configuration(
          ...
          localisation = Localisation(
            language = "en"
            allowedLanguages = listOf("en", "el")
            overrides = mapOf(
                "en" to mapOf(
                    "welcome.title" to "Custom welcome title",
                ),
            ),
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        localisation: .init(
          language: "en",
          allowedLanguages: ["en", "el"],
          overrides: [
            "en": [
              "welcome.title": "Custom welcome title",
            ]
          ]
        )
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      sdkToken: 'your token here',
      configuration: {
        ...
        localisation: {
          language: 'en',
          allowedLanguages: ['en', 'el'],
          overrides: {
            en: {
              'welcome.title': 'Custom welcome title',
            },
          },
        },
      }
    });
    ```
  

#### Explicit language selection

- **`language {locale}` - optional (default `en-US`)**

For integrators that want to preserve their own app session's language across the verification flow, the `language` property can be set to any of the supported locales.
Additionally, the integrator may define a custom language and use its identifier as the value for the `language` property. Refer to [Custom Languages](#custom-languages) section below for more information on custom languages.

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(
      ...
        configuration = Configuration(
        ...
          localisation = Localisation(
            language = "en"
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        localisation: .init(language: "en")
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        localisation: {
          language: 'en',
        },
      }
    });
    ```
  

**Please note:** if the end user's language is not one of the supported languages or if the locale provided under `language` is invalid, `en-US` will be selected as the fallback.

#### Restricting possible languages

- **`availableLanguages [locale]` - optional**

If the integrator wants to limit the possible languages that could be rendered at runtime, a **list** of locales (including custom identifiers) can be passed to the optional property `availableLanguages`.
This feature is intended for integrators that want to ensure that all languages shown to end users are limited to those supported by their own business and support teams.

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(
        ...
        configuration = Configuration(
          ...
          localisation = Localisation(
            allowedLanguages = listOf("en", "el")
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        localisation: .init(
          allowedLanguages: ["en", "el"]
        )
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      sdkToken: 'your token here',
      configuration: {
        ...
        localisation: {
          ...
          allowedLanguages: ['en', 'el'],
        },
      }
    });
    ```
  

#### Custom text overrides

- **`overrides {Map}` - optional**

Custom text can be added by overriding individual text strings for all languages required or allowed by your implementation.

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(
        ...
        configuration = Configuration(
          ...
          localisation = Localisation(
            overrides = mapOf(
                "en" to mapOf(
                    "welcome.title" to "Custom welcome title (EN)",
                    "welcome.subtitle" to "Custom welcome subtitle (EN)",
                ),
                "el" to mapOf(
                    "welcome.title" to "Custom welcome title (EL)",
                    "welcome.subtitle" to "Custom welcome subtitle (EL)",
                ),
            ),
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
      EntrustIdv(
        ...
        configuration: .init(
          ...
          localisation: .init(
            overrides: [
              "en": [
                "welcome.title" to "Custom welcome title (EN)",
                "welcome.subtitle" to "Custom welcome subtitle (EN)",
              ],
              "el": [
                "welcome.title" to "Custom welcome title (EL)",
                "welcome.subtitle" to "Custom welcome subtitle (EL)",
              ]
            ]
          )
        )
      )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      sdkToken: 'your token here',
      configuration: {
        ...
        localisation: {
          overrides: {
            en: {
              "welcome.title": "Custom welcome title (EN)",
              "welcome.subtitle": "Custom welcome subtitle (EN)",
            },
            el: {
              "welcome.title": "Custom welcome title (EL)",
              "welcome.subtitle": "Custom welcome subtitle (EL)",
            }
          },
        },
      }
    });
    ```
  

The full list of keys that can be customized, and their hierarchy, is available on the Entrust Content Delivery Network (CDN) and is split by module and language.

| File URL                                                                                                                                                   | Description                                                                         |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [https://sdk.onfido.com/capture/i18n/common/en_US.json](https://sdk.onfido.com/capture/i18n/common/en_US.json)                                             | Flow level keys, country names, document types and other keys shared across modules |
| [https://sdk.onfido.com/capture/i18n/welcome/en_US.json](https://sdk.onfido.com/capture/i18n/welcome/en_US.json)                                           | Welcome screen                                                                      |
| [https://sdk.onfido.com/capture/i18n/complete/en_US.json](https://sdk.onfido.com/capture/i18n/complete/en_US.json)                                         | "Thank you" screen                                                                  |
| [https://sdk.onfido.com/capture/i18n/consent/en_US.json](https://sdk.onfido.com/capture/i18n/consent/en_US.json)                                           | Consent screen                                                                      |
| [https://sdk.onfido.com/capture/i18n/error/en_US.json](https://sdk.onfido.com/capture/i18n/error/en_US.json)                                               | General error screens                                                               |
| [https://sdk.onfido.com/capture/i18n/retry/en_US.json](https://sdk.onfido.com/capture/i18n/retry/en_US.json)                                               | Retry screen                                                                        |
| [https://sdk.onfido.com/capture/i18n/crossdevice/en_US.json](https://sdk.onfido.com/capture/i18n/crossdevice/en_US.json)                                   | Cross-device screens for both desktop and mobile sessions                           |
| [https://sdk.onfido.com/capture/i18n/document/en_US.json](https://sdk.onfido.com/capture/i18n/document/en_US.json)                                         | Document Capture screens                                                            |
| [https://sdk.onfido.com/capture/i18n/proofOfAddress/en_US.json](https://sdk.onfido.com/capture/i18n/proofOfAddress/en_US.json)                             | Proof of Address screens                                                            |
| [https://sdk.onfido.com/capture/i18n/face/en_US.json](https://sdk.onfido.com/capture/i18n/face/en_US.json)                                                 | Face Capture - Photo screens                                                        |
| [https://sdk.onfido.com/capture/i18n/faceVideo/en_US.json](https://sdk.onfido.com/capture/i18n/faceVideo/en_US.json)                                       | Face Capture - Video screens                                                        |
| [https://sdk.onfido.com/capture/i18n/motion/en_US.json](https://sdk.onfido.com/capture/i18n/motion/en_US.json)                                             | Face Capture - Motion screens                                                       |
| [https://sdk.onfido.com/capture/i18n/profileData/en_US.json](https://sdk.onfido.com/capture/i18n/profileData/en_US.json)                                   | Profile Data screens                                                                |
| [https://sdk.onfido.com/capture/i18n/electronicId/en_US.json](https://sdk.onfido.com/capture/i18n/electronicId/en_US.json)                                 | Electronic ID verification screens                                                  |
| [https://sdk.onfido.com/capture/i18n/ial2/en_US.json](https://sdk.onfido.com/capture/i18n/ial2/en_US.json)                                                 | IAL2 screens                                                                        |
| [https://sdk.onfido.com/capture/i18n/qualifiedElectronicSignature/en_US.json](https://sdk.onfido.com/capture/i18n/qualifiedElectronicSignature/en_US.json) | Qualified Electronic Signature (QES) screens                                        |
| [https://sdk.onfido.com/capture/i18n/oneTimePassword/en_US.json](https://sdk.onfido.com/capture/i18n/oneTimePassword/en_US.json)                           | One-Time-Password (OTP) screens                                                     |

**Please note:** the same keys are available across all supported languages and are accessible by specifying the appropriate language name in the URL (`en_US.json` in the example above).

#### Clickable links in custom text

An overridden string can carry inline link markup. The SDK parses the markup and renders a styled, underlined link that opens in the device browser when tapped.

Wrap the link text in a `link` tag and place the destination in its `href` attribute:

```text
Read our <link href="https://example.com/privacy">privacy policy</link> before continuing.
```

- `link` is the only permitted tag, and `href` is the only permitted attribute. There is no `target` attribute
- `href` must start with a lowercase `https://`
- The markup is recognized only in the exact form `<link href="url">text</link>`: double quotes around the URL, no other attributes, and a matching closing tag
- Link text is styled with the `content-color.link.default` token and underlined, so no additional styling is required
- The URL is part of the translated string. Repeat it in every language that is overridden, pointing at the localized destination where one exists
- Links cannot be nested
- Markup is rendered in screen body text. Titles, buttons and other elements display it as written, so restrict links to body copy

The copy itself is never dropped. A recognized tag that is not an allowed link loses its tags and keeps its text, and markup the SDK does not recognize at all stays in the copy exactly as written:

| Overridden value                                          | Rendered on screen                                        |
| ---------------------------------------------------------- | ----------------------------------------------------------- |
| `<link href="https://example.com">terms</link>`           | `terms`, as a tappable link                               |
| `<link href="http://example.com">terms</link>`            | `terms`, as plain text — the URL is not `https://`        |
| `<a href="https://example.com">terms</a>`                 | `terms`, as plain text — `a` is not an allowed tag        |
| `<a href="https://example.com" target="_blank">terms</a>` | Exactly as written — a second attribute is not recognized |
| `<link href="https://example.com">terms`                  | Exactly as written — the closing tag is missing           |

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(
        ...
        configuration = Configuration(
          ...
          localisation = Localisation(
            overrides = mapOf(
                "en" to mapOf(
                    "welcome.subtitle" to
                        """Read our <link href="https://example.com/privacy">privacy policy</link> before continuing.""",
                ),
                "fr" to mapOf(
                    "welcome.subtitle" to
                        """Consultez notre <link href="https://example.com/fr/privacy">politique de confidentialité</link> avant de continuer.""",
                ),
            ),
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        localisation: .init(
          overrides: [
            "en": [
              "welcome.subtitle": #"Read our <link href="https://example.com/privacy">privacy policy</link> before continuing."#,
            ],
            "fr": [
              "welcome.subtitle": #"Consultez notre <link href="https://example.com/fr/privacy">politique de confidentialité</link> avant de continuer."#,
            ]
          ]
        )
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      sdkToken: 'your token here',
      configuration: {
        ...
        localisation: {
          overrides: {
            en: {
              'welcome.subtitle':
                'Read our <link href="https://example.com/privacy">privacy policy</link> before continuing.',
            },
            fr: {
              'welcome.subtitle':
                'Consultez notre <link href="https://example.com/fr/privacy">politique de confidentialité</link> avant de continuer.',
            },
          },
        },
      }
    });
    ```
  

**Please note:** the React Native SDK passes overrides to the underlying Android and iOS SDKs unchanged, so no escaping is required beyond the quoting rules of the host language. Support for Web modules will follow in a later release.

#### Custom languages

The SDK allows the use of custom languages alongside all officially supported languages. This functionality is achieved by:

- defining a unique language identifier
- using the unique language identifier within the `localisation` object, mapping any required strings to the existing language file structure

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
    ...
    entrustIdv.start(
      ...
      configuration = Configuration(
        ...
        localisation = Localisation(
          language = "fr_CUSTOM"
          overrides = mapOf(
              "fr_CUSTOM" to mapOf(
                  "welcome.title" to "Custom welcome title in fr_CUSTOM",
              ),
          ),
        )
      )
    )}
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        localisation: .init(
          language: "fr_CUSTOM",
          overrides: [
            "fr_CUSTOM": [
              "welcome.title" to "Custom welcome title in fr_CUSTOM",
            ]
          ]
        )
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        localisation: {
          language: 'fr_CUSTOM',
          overrides: {
            fr_CUSTOM: {
              "welcome.title": "Custom welcome title in fr_CUSTOM",
            },
          }
        }
      }
    });
    ```
  

**Please note:** custom languages, in a similar way to regular text overrides, work as 'deltas' to the base language file they relate to. For example, a custom language defined as `fr_Custom` would be based off of `fr` for missing keys.

### Additional SDK configuration

The following section details additional configuration options that can be applied to the overall SDK flow.
For implementations that are yet to migrate to workflow-based orchestration in Studio, the options can be added to the object `configuration` at the root of the `EntrustIdv` initialization object.

#### Privacy and analytics

By default, the SDK collects telemetry used to monitor the performance of the SDK, defend against fraud and help improve the accessibility of screens.

While core analytics are essential to the functioning of the overall service and are always sent to the Entrust services, behavioral analytics can be disabled during the initialization of the SDK.

- **`disableAnalytics {Boolean}` - optional (default `false`)**

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(
      ...
        configuration = Configuration(
          ...
          disableAnalytics = false
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        disableAnalytics: false
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        disableAnalytics: false,
      }
    });
    ```
  

**Please note:**

- Disabling analytics will also prevent any analytics returned via the `onAnalytics` callback

For more information about the scope of the SDK analytics and privacy implications, please refer to the [our SDK Data Collection Overview](/sdk/sdk-data-collection/) guide.

#### Navigation bar and options

> ℹ️ **Note:** The ability to modify the navigation settings in Studio, as a workflow-level
> configuration option, will be introduced in an upcoming version of the SDK

The user navigation experience of the SDK can be customized via the `navigation` property, which accepts one of the following values:

| Use Case                            | Value         |
| ----------------------------------- | ------------- |
| Back & exit buttons shown (default) | `BackAndExit` |
| Only back button shown              | `BackOnly`    |
| Hidden                              | `Hidden`      |

**Note**:

- By default, the back button is present on all SDK module screens to allow end users to navigate back within any module. It is however **not possible** to navigate between SDK modules, as the state of the previously completed task cannot be modified
- The back navigation behavior is now applicable to both workflow-based and non-workflow-based flows (back navigation was previously possible between modules in non-workflow-based flows)
- When the back button is not displayed, the operating system, hardware or browser 'back' navigation is also disabled
- Forward navigation is disabled throughout the user flow

> ⚠️ **Warning:** It is **not** recommended to hide the navigation bar unless a strong alternative is provided to the user

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(
      ...
        configuration = Configuration(
          ...
          navigation = Navigation(
              navigationBar = NavigationBar.BackAndExit
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      ...
      configuration: .init(
        ...
        navigation: .init(navigationBar: .backAndExit)
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      configuration: {
        ...
        navigationBar: NavigationBar.BackAndExit,
      }
    });
    ```
  

#### SSL certificate pinning

> ⚠️ **Warning:** This configuration option will be introduced in a future version of the SDK

## SDK module configuration

This section details, per module, the configuration options that can be applied at runtime for a given flow.
**While certain options are currently only applicable to implementations that are yet to migrate to workflow-based orchestration, they will be gradually introduced in Workflow Studio in 2026**

### Welcome

This module enables the display of the introduction screen shown at the beginning of a flow. It introduces the process and prepares the user for the steps that they will need to complete.

While this screen is optional, we only recommend its removal if you already have your own identity verification introduction screen in place.

> ⚠️ **Warning:** Workflow-based sessions currently do not have the option to display the
> Welcome screen at the start of a workflow. The option to display the screen
> will be introduced in early 2026 as a workflow-level configuration option

#### Configuration for non-workflow based sessions

For non-workflow-based flows, the display of the screen is controlled by the inclusion (or omission) of the `Welcome` step from the initialization code.

### Document and NFC capture

The Document module allows for the image and NFC capture of the end user's identity document. The module combines both live capture and NFC scanning.

The execution of this module is controlled by the presence of a ["Document Capture" task](/getting-started/workflow-studio-product/#document-capture-task) in your Studio workflow. The task has the following configuration options:

- Restricting the document type and country of issuance that can be selected by the end user
- Whether NFC is available. When enabled the option to force NFC can be selected

<img src="./documentTask.png" alt="Document Capture task" width="400px" />

#### Document filtering

As part of the flow, the end user is prompted to select the issuing country and document type before proceeding to the document's capture. This information is used to optimize the capture experience, as well as inform the end user about which documents they are allowed to use.

**Please note:**

- The selection screen is dynamic and will be automatically hidden when the document filtering configuration doesn't require the end user to indicate which document will be captured
- In a very limited number of cases, the end user may also be asked if they have a card or paper version of their document via an additional prompt

As part of the workflow configuration, it is possible to restrict the documents that can be selected in two ways:

- Within a Document Capture task, documented in the [Studio Product guide](/getting-started/workflow-studio-product/#document-capture-task)
- Otherwise, the recommended approach is to apply this configuration globally in your Dashboard under Accounts \ Supported Documents, as this configuration is also applied to your Document Reports. Any document that has been uploaded by an end user against your guidance will result in a Document Report sub-result of "rejected" and be flagged as `Image Integrity` > `Supported Document`

![Supported Documents in Dashboard](./dashboard-supported-docs.png 'Supported Documents in Dashboard')

##### Document filtering for non-workflow based sessions

For integrations that are yet to migrate to workflow-based flows in Studio, the `Document` step defined in SDK initialization configuration may optionally include:

- `documentFiltering: {Filters}` - default all documents allowed

The `documentFiltering` parameter accepts two optional lists of `DocumentSelection` objects: one to `exclude` particular document types or countries and one to only `include` certain document types or countries. The two can be used together to provide the appropriate selection to the user.

The `DocumentSelection` object is defined as below:

```kotlin
    DocumentSelection(
      documentType: <>,  // "Generic" is defined in the following section
      issuingCountry: String,    // Issuing country of the document in 3-letter ISO 3166-1 alpha-3 format. Must not be used in conjuction with "allCountries" option
      allCountries: Boolean,     // Whether the documentType selected should be applied to all possible countries
      id: String,                // Used to uniquely identify a "Generic" documentType
    )
```

In the example below, the user will only be able to select a passport or driving license from any country (as a result of the `include` definition), but for France only passports will be accepted (`exclude` removes the option of a driving license for that country):

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(ClassicParameters(
        ...
        steps = listOf>(
          ...
          Document(options = Options(
            documentFiltering = DocumentFiltering(
              include = listOf(
                DocumentSelection(
                  documentType = DocumentType.Passport,
                  allCountries = true
                ),
                DocumentSelection(
                  documentType = DocumentType.DrivingLicence,
                  allCountries = true
                )),
              exclude = listOf(
                DocumentSelection(
                  documentType = DocumentType.DrivingLicence,
                  issuingCountry = "FRA"
              )),
            )
          )),
        )
      ))
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          Document( options: .init(
            documentFiltering: .init(
              exclude: [ .init(
                documentType: .drivingLicense,
                issuingCountry: "FRA"
              )],
              include: [
                .init(
                  documentType: .passport,
                  allCountries: true
                ),
                .init(
                  documentType: .drivingLicense,
                  allCountries: true
                )
              ]
          ))
      )]
    ))
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      steps: [
        ...
        Document({
          documentFiltering: {
            include: [{
                documentType: DocumentType.Passport,
                allCountries: true,
              },
              {
                documentType: DocumentType.DrivingLicence,
                allCountries: true,
              },
            ],
            exclude: [{
                documentType: DocumentType.DrivingLicence,
                issuingCountry: 'FRA',
              },
            ],
          },
        }),
      ],
    });
    ```
  

An additional option to `extend` the `DocumentFiltering` list can be used to add custom document types to the existing list of documents. This is explained in the following section.

**Please note the following SDK behaviors:**

- Hard-coding any document type and issuing country configuration in your SDK integration will fully override the Dashboard-based settings
- Passports have the same format worldwide, so the SDK will not show the country selection screen if `include` only contains passports and they are not restricted by country (e.g. `allCountries = true`)
- Currently only the following document types are supported by the SDK. If you select other document types in your Dashboard (visa, for example), these will not be displayed in the SDK selection screen. If you need to add other document types to the document selection screen, you can mitigate this limitation in the short-term, using the [Generic Document feature](#inclusion-of-generic-document-types):
- Driving License
- National Identity Card
- Passport
- Passport Card
- Residence Permit
- Generic - more information in the next section

##### Inclusion of generic document types

This feature is currently only available for non-workflow-based flows. It allows for an arbitrary document type to be shown to the end user for selection.
To use this feature:

- Generic documents must be defined in the `genericDocuments` property

- Using their unique identifiers, the newly defined generic document types can be added either to the `extend` property of the `DocumentFiltering` property (to add it to the default selection list) or the `include` property (to add it to a specific filtered list)

- `genericDocuments: {List}` - defaults to no generic documents

The `GenericDocument` object is defined as below:

```kotlin
GenericDocument(
id: String,         // Unique identifier of the document type
country: String,    // Issuing country of the document in 3-letter ISO 3166-1 alpha-3 format
pages: Int,         // Number of pages that should be captured for this document. Can either be 1 or 2
title: String,      // Name of the document as it will appear on the screen
subTitle: String,   // Short description of the document that will appear on the document selection screen
)
```

In the example below, a custom, one-sided, document type is added to the default document selection list. It will only be available for French documents (as per its definition):

  
### Android

    ```kotlin
    ...
    entrustIdv.start(ClassicParameters(
      ...
      steps = listOf>(
        ...
        Document(options = Options(
          documentFiltering = DocumentFiltering(
            extend = listOf(
              GenericDocument(
                id = "custom_lunch_card_FR",
                country = "FRA",
                pages = 1,
                title = "Lunch Card",
                subtitle = "Front picture of your lunch card"
              )
            )
          )
        ))
      )
    ))
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          Document( options: .init(
            documentFiltering: .init(
              extend: [ .init(
                id: "custom_lunch_card_FR",
                title: "Lunch Card",
                subtitle: "Front picture of your lunch card",
                country: "FRA",
                pages: 1
              )],
            )
          ))
        ]
      ))
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      steps: [
        ...
        Document({
          ...
          documentFiltering: {
            extend: [{
              id: "custom_lunch_card_FR",
              country: "FRA",
              pages: 1,
              title: "Lunch Card",
              subTitle: "Front picture of your lunch card"
            }]
          }
        }),
      ],
    });
    ```
  

#### NFC

> **Note:** NFC capture will be introduced in the new mobile SDKs in early 2026. The
> following section is intended to provide an overview of the expected
> capabilities

Recent passports, national identity cards and residence permits contain a chip that can be accessed using Near Field Communication (NFC). The SDK provides a set of screens and functionalities to extract this information, verify its authenticity and provide the results as part of a Document report.

The behavior of NFC capture is controlled within the Document Capture Studio task by one of the following options:

- Disabled: NFC reading will not be asked of end users
- Optional (Default): NFC reading will be attempted, if possible
- Required: NFC reading will be enforced, preventing end users from completing the flow without a successful reading

For more information on how to configure NFC and the list of supported documents, please refer to the NFC for [Document Report guide](/guide/document-report-nfc).

#### Configuration for non-workflow based sessions

For non-workflow-based flows, the `Document` step may optionally include the following options:

- `nfcPolicy: { Disabled | Optional | Required }` - default: `Optional`

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(ClassicParameters(
          ...
          steps = listOf>(
            ...
            Document(options = Options(
              nfcPolicy = NFCPolicy.Required
              ...
            )),
          )
      ))
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          Document( options: .init(
            nfcPolicy: NFCPolicy.required
          ))
        ]
    ))
    ```
  

  
### React Native

    Information to be added shortly.
  

### Proof of Address capture

The Proof of Address capture module allows for the verification of an end user's address document. Users will be asked to select the issuing country of their document, the document type, and to provide images of their selected document. They will also have a chance to check the quality of the images before confirming.

<img src="./poaTask.png" alt="Proof of Address Capture task" width="400px" />

There are currently no customization options for this step in Workflow Studio.

#### Proof of Address document filtering

As part of the flow, the end user is prompted to select the issuing country and document type before proceeding to the capture of the proof of address. This information is used to optimize the capture experience, as well as inform the end user about which documents they are allowed to use.

> ⚠️ **Warning:** This set of configuration options is not yet available in Studio Workflow

#### Configuration for non-workflow based sessions

For integrations that are yet to migrate to workflow-based flows in Studio, the `ProofOfAddress` step defined in SDK initialization configuration may **optionally** include:

- `proofOfAddressFiltering: {ProofOfAddressFiltering}` - default all types are allowed

The `proofOfAddressFiltering` parameter accepts a list of `ProofOfAddressSelection` objects used to `include` certain document types or countries.

**It is necessary to only provide types that are applicable in a given country for this functionality to work.** Please refer to the [Proof of Address product guide](/guide/proof-of-address-report/) for the full list of documents available for each country.

In the example below, the user will only be able to select a bank statement or utility bill from the USA or a bank statement for France:

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(ClassicParameters(
        ...
        steps = listOf>(
          ...
          ProofOfAddress(
            options = ProofOfAddress.Options(
              proofOfAddressFiltering = ProofOfAddressFiltering(
                include = (
                  listOf(
                    ProofOfAddressSelection(
                      proofOfAddressType = ProofOfAddressType.BankBuildingSocietyStatement,
                      issuingCountry = "USA"
                    ),
                    ProofOfAddressSelection(
                      proofOfAddressType = ProofOfAddressType.UtilityBill,
                      issuingCountry = "USA"
                    ),
                    ProofOfAddressSelection(
                      proofOfAddressType = ProofOfAddressType.BankBuildingSocietyStatement,
                      issuingCountry = "FRA"
                    )
                  ))
              ))
          ))
      ))
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          ProofOfAddress( options: ProofOfAddress.ProofOfAddressOptions(
            proofOfAddressFiltering: ProofOfAddress.ProofOfAddressFiltering(
              include: [
                ProofOfAddress.ProofOfAddressSelection(
                  proofOfAddressType: .bankBuildingSocietyStatement,
                  issuingCountry: "USA"
                ),
                ProofOfAddress.ProofOfAddressSelection(
                  proofOfAddressType: .utilityBill,
                  issuingCountry: "USA"
                ),
                ProofOfAddress.ProofOfAddressSelection(
                  proofOfAddressType: .bankBuildingSocietyStatement,
                  issuingCountry: "FRA"
                )
              ]
          )),
        )]
    ))
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      steps: [
        ...
        ProofOfAddress({
          documentFiltering: {
            include: [{
                proofOfAddressType: ProofOfAddressType.BankBuildingSocietyStatement,
                issuingCountry: 'USA',
              },
              {
                proofOfAddressType: ProofOfAddressType.UtilityBill,
                issuingCountry: 'USA',
              },
              {
                proofOfAddressType: ProofOfAddressType.BankBuildingSocietyStatement,
                issuingCountry: 'FRA',
              }],
          },
        }),
      ],
    });
    ```
  

### Face Photo

The Face Photo module enables the capture of the end user's face in the form of a photo. For performance and compliance purposes, the Face Motion module is recommended instead.

|                                                                                                                                                                                                                                                                                                                                                                        |                                                                             |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| The execution of this module is controlled by the presence of a ["Face Capture: Photo" task](/getting-started/workflow-studio-product/#face-capture-photo-task) in your Studio workflow. The task has the following configuration options: <br />- Show introduction screen (default `true`). When disabled, the end user would be taken directly to the capture screen. | ![Face Capture: Photo task](./facePhotoTask.png 'Face Capture: Photo task') |

⚠️ As the Intro screen provides valuable information on how to successfully get verified, it is recommended to display it unless the integrator decides to provide their own instruction screen.

#### Configuration for non-workflow based sessions

For non-workflow-based flows, the `FacePhoto` step may optionally include the `showIntro: Boolean` property (default `true`) in its `Options`.

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(ClassicParameters(
          ...
          steps = listOf>(
            ...
            FacePhoto(options = Options(showIntro = true)),
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          FacePhoto( options: .init(
            showIntro: true
          ))
        ]
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      steps: [
        ...
        FacePhoto({
          showIntro: true,
        }),
      ],
    });
    ```
  

### Face Video

> **Note:** The current version of the SDK no longer contains a native variant of the Face
> Video module. A Web variant can still be used in workflow and
> non-workflow-based sessions

The Face Video module enables the capture of the end user's face in the form of a video. For performance and compliance purposes, the Face Motion module is recommended instead.

|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |                                                                             |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| The execution of this module is controlled by the presence of a ["Face Capture: Video" task](/getting-started/workflow-studio-product/#face-capture-video-task) in your Studio workflow. The task has the following configuration options:<br />- Show introduction screen (default `true`). When disabled, the end user would be taken directly to the capture screen.<br />- Show confirmation video preview (default `true`). When disabled, the video file is automatically submitted after the capture finishes.<br />- (coming soon to Studio) Record Audio (default `false`). When enabled, the video capture will include sound capture. | ![Face Capture: Video task](./faceVideoTask.png 'Face Capture: Video task') |

⚠️ As the Intro screen provides valuable information on how to successfully get verified, it is recommended to display it unless the integrator decides to provide their own instruction screen.

#### Configuration for non-workflow based sessions

For non-workflow-based flows, the `FaceVideo` step may optionally include the following options:

- `showIntro: Boolean` (default `true`)
- `showConfirmation: Boolean` (default `true`)
- `recordAudio: Boolean` (default: `false`)

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(ClassicParameters(
          ...
          steps = listOf>(
            ...
            FaceVideo(options = Options(
              showIntro = true,
              showConfirmation = true,
              recordAudio = false
              )),
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          FaceVideo(options: .init(
            showIntro: true,
            recordAudio: false,
            showConfirmation: true
          ))
        ]
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      steps: [
        ...
        FaceVideo({
          showIntro: true,
          showConfirmation: true,
          recordAudio: false,
        }),
      ],
    });
    ```
  

### Face Motion

The Face Motion module enables the capture of the end user's face in the form of motion capture.

|                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |                                                                                |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| The execution of this module is controlled by the presence of a ["Face Capture: Motion" task](/getting-started/workflow-studio-product/#face-capture-motion-task) in your Studio workflow. The task has the following configuration options:<br />- Show introduction screen (default `true`). When disabled, the end user would be taken directly to the capture screen.<br />- Record Audio (default `false`). When enabled, the video capture will include sound capture. | ![Face Capture: Motion task](./faceMotionTask.png 'Face Capture: Motion task') |

⚠️ As the Intro screen provides valuable information on how to successfully get verified, it is recommended to display it unless the integrator decides to provide their own instruction screen.

#### Configuration for non-workflow based sessions

For non-workflow-based flows, the `FaceMotion` step may optionally include the following options:

- `showIntro: Boolean` (default `true`)
- `recordAudio: Boolean` (default: `false`)

  
### Android

    ```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
      ...
      entrustIdv.start(ClassicParameters(
          ...
          steps = listOf>(
            ...
            FaceMotion(options = Options(
              showIntro = true,
              recordAudio = false
              )),
          )
        )
      )
    }
    ```
  

  
### iOS

    ```swift
    EntrustIdv(
      sdkParameters: ClassicParameters(
        ...
        steps:[
          ...
          FaceMotion( options: .init(
            showIntro: true,
            recordAudio: false
          ))
        ]
      )
    )
    ```
  

  
### React Native

    ```javascript
    entrustIdv.start({
      ...
      steps: [
        ...
        FaceMotion({
          showIntro: true,
          recordAudio: false,
        }),
      ],
    });
    ```
  

### One-time Password (OTP)

The [One-time Password task](/getting-started/workflow-studio-product/#one-time-password-capture-task) allows for the verification of a mobile phone number when used in conjuction with the Qualified Electronic Signature task.

<img src="./otpTask.png" alt="One Time Password task" width="400px" />

This task has no SDK configuration options and is available only via Studio.

### Qualified Electronic Signature (QES)

The [Qualified Elentronic Signature task](/getting-started/workflow-studio-product/#qualified-electronic-signature-capture-task) embeds the process provided by a Qualified Trust Service Provider to digitally sign a document.

<img src="./qesTask.png" alt="Qualified Electronic Signature task" width="400px" />

This task has no SDK configuration options and is available only via Studio.

### Retry task

The Studio [Retry task](/getting-started/workflow-studio-product/#retry-task) allows orchestration between workflow tasks based on logical checks.
When the task's condition fails, the SDK will display a dedicated screen to the user, prompting them to proceed to the next logical task (or repeat a previous one).

<img src="./retryTask.png" alt="Retry task" width="400px" />

This task has no additional configuration option beyond selecting a "Retry Reason". It is only available via Studio.
**Please note**: when a custom reason is provided in the task definition, it cannot be automatically translated by the SDK.

# More Information

## Accessibility

All SDKs have been optimized to provide the following accessibility support by default:

- Screen reader support: accessible labels for textual and non-textual elements available to aid TalkBack navigation, including dynamic alerts
- Dynamic font size support: all elements scale automatically according to the device's font size setting
- Sufficient color contrast: default colors have been tested to meet the recommended level of contrast
- Sufficient touch target size: all interactive elements have been designed to meet the recommended touch target size

Refer to our [accessibility statement](/sdk/sdk-accessibility-statement) for more details.

## Licensing

Due to API design constraints, and to avoid possible conflicts during the integration, we bundle some of our 3rd party dependencies as repackaged versions of the original libraries.

Please refer to the [SDK license acknowledgements](/sdk/sdk-licenses/) guide for more information.

# Appendix

## List of supported languages and locales

Entrust supports and maintains translations for 44 languages that can be implemented directly inside the SDK.

All of these languages are supported across Android, iOS and Web, under the following locales:

| Language                 | Locale    | Android | iOS              | Web         |
| ------------------------ | --------- | ------- | ---------------- | ----------- |
| Arabic                   | `ar`      | ✔       | ✔                | ✔           |
| Armenian                 | `hy`      | ✔       | ✔                | ✔           |
| Bulgarian                | `bg`      | ✔       | ✔                | ✔           |
| Chinese (Simplified)     | `zh_CN`   | ✔       | ✔ (as `zh_Hans`) | ✔           |
| Chinese (Traditional)    | `zh_TW`   | ✔       | ✔ (as `zh_Hant`) | ✔           |
| Croatian                 | `hr`      | ✔       | ✔                | ✔           |
| Czech                    | `cs`      | ✔       | ✔                | ✔           |
| Danish                   | `da`      | ✔       | ✔                | ✔           |
| Dutch                    | `nl`      | ✔       | ✔                | ✔           |
| English (United Kingdom) | `en_GB`   | ✔       | ✔                | ✔           |
| English (United States)  | `en_US`   | ✔       | ✔                | ✔           |
| Estonian                 | `et`      | ✔       | ✔                | ✔           |
| Finnish                  | `fi`      | ✔       | ✔                | ✔           |
| French                   | `fr`      | ✔       | ✔                | ✔           |
| French (Canadian)        | `fr_CA`   | ✔       | ✔                | ✔           |
| German                   | `de`      | ✔       | ✔                | ✔           |
| Greek                    | `el`      | ✔       | ✔                | ✔           |
| Hebrew                   | `he`      | ✔       | ✔                | ✔           |
| Hindi                    | `hi`      | ✔       | ✔                | ✔           |
| Hungarian                | `hu`      | ✔       | ✔                | ✔           |
| Indonesian               | `id`      | ✔       | ✔                | ✔           |
| Italian                  | `it`      | ✔       | ✔                | ✔           |
| Japanese                 | `ja`      | ✔       | ✔                | ✔           |
| Korean                   | `ko`      | ✔       | ✔                | ✔           |
| Latvian                  | `lv`      | ✔       | ✔                | ✔           |
| Lithuanian               | `lt`      | ✔       | ✔                | ✔           |
| Malay                    | `ms`      | ✔       | ✔                | ✔           |
| Norwegian                | `nb`      | ✔       | ✔                | ✔           |
| Persian                  | `fa`      | ✔       | ✔                | ✔           |
| Polish                   | `pl`      | ✔       | ✔                | ✔           |
| Portuguese               | `pt`      | ✔       | ✔                | ✔           |
| Portuguese (Brazil)      | `pr_BR`   | ✔       | ✔                | ✔           |
| Romanian                 | `ro`      | ✔       | ✔                | ✔           |
| Russian                  | `ru`      | ✔       | ✔                | ✔           |
| Serbian                  | `sr_Latn` | ✔       | ✔                | ✔ (as `sr`) |
| Slovak                   | `sk`      | ✔       | ✔                | ✔           |
| Slovenian                | `sl`      | ✔       | ✔                | ✔           |
| Spanish                  | `es`      | ✔       | ✔                | ✔           |
| Spanish (Latin America)  | `es_419`  | ✔       | ✔                | ✔           |
| Swedish                  | `sv`      | ✔       | ✔                | ✔           |
| Thai                     | `th`      | ✔       | ✔                | ✔           |
| Turkish                  | `tr`      | ✔       | ✔                | ✔           |
| Ukrainian                | `uk`      | ✔       | ✔                | ✔           |
| Vietnamese               | `vi`      | ✔       | ✔                | ✔           |

# Changelog

  
### Android

    
  

  
### iOS

    
  

  
### React Native