# Workflow Integration

## Overview

This guide walks you through integrating the **iOS** **SDK** with ComplyCube [workflows](https://app.gitbook.com/s/KyFKMqftsmT6qln9zo5y/compliance-studio/workflows).

<figure><img src="https://content.gitbook.com/content/lv7UhJvTbxeq4s3KwQpn/blobs/25uAUIYwoHnsph9spPLb/mobile_sdk_illustrations.jpeg" alt=""><figcaption><p>Mobile SDK View</p></figcaption></figure>

## Integration flow

The Mobile SDK runs on your mobile application, but relies on your backend to create **secure tokens**. Here’s how it works:

{% stepper %}
{% step %}

#### **Create a client**

Register a new **client** (i.e. customer) using the ComplyCube API.
{% endstep %}

{% step %}

#### **Generate an SDK token**

Your backend requests a **JWT token** tied to that client.
{% endstep %}

{% step %}

#### Initialize the SDK in your mobile app

Pass the token and settings to the SDK initializer.
{% endstep %}

{% step %}

#### Capture data, documents, videos, and selfies

The SDK guides your customer through the required steps.
{% endstep %}

{% step %}

#### Perform checks

Captured data is securely sent to ComplyCube for verification checks, and results are delivered in real time through the API or webhooks.
{% endstep %}
{% endstepper %}

<figure><img src="https://648014528-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flv7UhJvTbxeq4s3KwQpn%2Fuploads%2F6roGj1GhVS9jqrJDm3AK%2Fdocumentation-Mobile%20SDK%20(WF).png?alt=media&#x26;token=236118bc-885e-4ea2-91a5-6abe18bd470a" alt=""><figcaption><p>Mobile SDK Integration via Workflows Guide</p></figcaption></figure>

## Integration guide

Explore the source code and sample projects on our repository: [![GitHub](https://img.shields.io/badge/View%20on-GitHub-black?logo=github)](https://github.com/complycube/complycube-ios-sdk).<br>

{% stepper %}
{% step %}

#### Install the SDK

1. Before using the ComplyCube SDK, install the **CocoaPods** plugin by running the following command in your terminal:<br>

   ```bash
   sudo gem install cocoapods
   ```

2. Add plugin repos and install the pod using your `Podfile`:<br>

   ```ruby
   source 'https://github.com/CocoaPods/Specs.git'
   …
   platform :iOS, ’13.0’

   target ‘YourApp’ do
       …
     pod 'ComplyCubeMobileSDK'
   end

   post_install do |installer|
       installer.pods_project.targets.each do |target|
           target.build_configurations.each do |build_configuration|
               build_configuration.build_settings['ENABLE_BITCODE'] = 'NO'
               build_configuration.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
               build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.1'
               build_configuration.build_settings['ARCHS'] = ['$(ARCHS_STANDARD)', 'x86_64']
               build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = ['arm64', 'arm64e', 'armv7', 'armv7s']
               build_configuration.build_settings['GENERATE_INFOPLIST_FILE'] = 'YES'
           end
       end
   end
   ```

\
**Application permissions**

Our SDK uses the device camera and microphone for capture. You must add the following keys to your application `Info.plist` file.

1. `NSCameraUsageDescription`

```xml
<key>NSCameraUsageDescription</key>
<string>Used to capture facials biometrics and documents</string>
```

2. `NSMicrophoneUsageDescription`

```xml
<key>NSMicrophoneUsageDescription</key>
<string>Used to capture video biometrics</string>
```

{% endstep %}

{% step %}

#### **Create a client**

Every verification flow starts with a **client** (i.e. customer). Use the API to [create the client](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/clients/create-a-client).

This must be done on your **mobile app backend** server, not the mobile app itself.

**Example request**

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST https://api.complycube.com/v1/clients \
     -H 'Authorization: <YOUR_API_KEY>' \
     -H 'Content-Type: application/json' \
     -d '{
          "type": "person",
          "email": "john.doe@example.com",
          "personDetails":{
               "firstName": "John",
               "lastName" :"Doe"
          }
        }'
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const { ComplyCube } = require("@complycube/api");

const complycube = new ComplyCube({ apiKey: "<YOUR_API_KEY>" });

const client = await complycube.client.create({
  type: "person",
  email: "john.doe@example.com",
  personDetails: {
    firstName: "John",
    lastName: "Doe"
  }
});
```

{% endtab %}

{% tab title="Python" %}

```python
from complycube import ComplyCubeClient
cc_api = ComplyCubeClient(api_key='<YOUR_API_KEY>')

new_client = {
    'type':'person',
    'email':'john.doe@example.com',
    'personDetails': {
        'firstName':'John',
        'lastName':'Doe'
    }
}

client = cc_api.clients.create(**new_client)
```

{% endtab %}

{% tab title="PHP" %}

```php
use ComplyCube\ComplyCubeClient;

$ccapi = new ComplyCubeClient('<YOUR_API_KEY>');

$result = $ccapi->clients()->create([
    'type' => 'person',
    'email' => 'john@doe.com',
    'personDetails' => [
        'firstName' => 'John',
        'lastName' => 'Doe'
    ]
]);
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using ComplyCube.Net;
using ComplyCube.Net.Resources.Clients;

var clientApi = new ClientApi(new ComplyCubeClient("<YOUR_API_KEY>"));
var newClient = new ClientRequest {
  type = "person",
  email = "john@doe.com",
  personDetails = new PersonDetails {
    firstName = "John",
    lastName = "Doe"
  }
}

var client = await clientApi.CreateAsync(newclientRequest);
```

{% endtab %}
{% endtabs %}

**Example response**

The response will contain an `id` (the Client ID). It is required for the next step.

```javascript
{
    "id": "5eb04fcd0f3e360008035eb1",
    "type": "person",
    "email": "john.doe@example.com",
    "personDetails": {
        "firstName": "John",
        "lastName": "Doe",
    },
    "createdAt": "2020-01-04T17:24:29.146Z",
    "updatedAt": "2020-01-04T17:24:29.146Z"
}
```

{% hint style="info" %}
See [Clients API Reference](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/clients) to learn more.
{% endhint %}
{% endstep %}

{% step %}

#### Generate an SDK token

Your backend must create an **SDK token** for each new flow. This token enable customers to send personal data securely from your mobile app to ComplyCube.&#x20;

Tokens are short-lived and must not be reused.

**Example request**

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST https://api.complycube.com/v1/tokens \
     -H 'Authorization: <YOUR_API_KEY>' \
     -H 'Content-Type: application/json' \
     -d '{
           "clientId":"CLIENT_ID",
           "appId": "com.complycube.SampleApp"
         }'
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const { ComplyCube } = require("@complycube/api");
​
const complycube = new ComplyCube({ apiKey: "<YOUR_API_KEY>" });
​
const token = await complycube.token.generate("CLIENT_ID", {
    appId: "com.complycube.SampleApp"
});
```

{% endtab %}

{% tab title="Python" %}

```python
from complycube import ComplyCubeClient
​
cc_api = ComplyCubeClient(api_key='<YOUR_API_KEY>')
​
token = cc_api.tokens.create('CLIENT_ID', appId='com.complycube.SampleApp')
```

{% endtab %}

{% tab title="PHP" %}

```php
use ComplyCube\ComplyCubeClient;

$ccapi = new ComplyCubeClient('<YOUR_API_KEY>');

$token = $ccapi->tokens()->generate('CLIENT_ID', 'com.complycube.SampleApp');
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using ComplyCube.Net;
using ComplyCube.Net.Resources.SDKTokens;

var sdkTokenApi = new SDKTokenApi(new ComplyCubeClient("<YOUR_API_KEY>"));

var sdkTokenRequest = {
  clientId = "CLIENT_ID",
  appId = "com.complycube.SampleApp"
}

var sdkToken = await sdkTokenApi.GenerateToken(sdkTokenRequest);
```

{% endtab %}
{% endtabs %}

**Example response**

```javascript
{
    "token": "<CLIENT_TOKEN>"
}
```

{% hint style="info" %}
See [SDK Token API Reference](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/other-resources/tokens) to learn more.
{% endhint %}
{% endstep %}

{% step %}

#### Initialize the SDK

As part of initializing the SDK, you are required to specify a [workflow template ID](https://portal.complycube.com/workflowTemplates). The SDK will automatically run the **active** version of the selected workflow.

```swift
let sdk = ComplyCubeMobileSDK.FlowBuilder()
  .withSDKToken("SDK_TOKEN")
  .withClientId("CLIENT_ID")
  .withWorkflowTemplateId("WORKFLOW_TEMPLATE_ID")
  .start(fromVc: self)

var clientAuth = ClientAuth("SDK_TOKEN", "CLIENT_ID")
```

{% endstep %}

{% step %}

#### Perform verifications

Once your customer starts the flow, a [workflow session](https://app.gitbook.com/s/KyFKMqftsmT6qln9zo5y/compliance-studio/workflows#workflow-session) is automatically created. This session contains all captured data (documents, images, and videos) as well as progress tracking. You can view all of them through the [workflow sessions page](https://portal.complycube.com/workflowSessions) on the portal or through the API.

When the flow finishes, the SDK triggers the `onSuccess` callback. The callback provides a `data` object that includes the `workflowSessionId`. Your **mobile backend** should use this ID to notify ComplyCube that the [workflow has completed](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/workflow-sessions/complete-a-workflow-session), which in turn runs the verification checks defined in the workflow.

If you have [set up webhooks ](https://docs.complycube.com/api-reference/other-resources/webhooks/create-a-webhook)as described in our [webhooks guide](https://docs.complycube.com/documentation/guides/webhooks),  you will be notified once a workflow session completes.

#### Example of a complete workflow session request

{% tabs %}
{% tab title="cURL" %}

```javascript
curl -X POST https://api.complycube.com/v1/workflowSessions/{:wfSessionId}/complete \
     -H 'Authorization: <YOUR_API_KEY>' \
     -H 'Content-Type: application/json'
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const { ComplyCube } = require("@complycube/api");

const complycube = new ComplyCube({ apiKey: "<YOUR_API_KEY>" });

const wfSession = await complycube.workflowSession.complete("WORKFLOW_SESSION_ID");
```

{% endtab %}

{% tab title="Python" %}

```python
from complycube import ComplyCubeClient

cc_api = ComplyCubeClient(api_key='<YOUR_API_KEY>')

workflow_session = cc_api.workflowsessions.complete('WORKFLOW_SESSION_ID')
```

{% endtab %}

{% tab title="PHP" %}

```php
use ComplyCube\ComplyCubeClient;

$ccapi = new ComplyCubeClient('<YOUR_API_KEY>');

$workflowSession = $ccapi->workflowSessions()->complete('WORKFLOW_SESSION_ID');
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using ComplyCube.Net;
using ComplyCube.Net.Resources.WorkflowSessions;

var wfSessionApi = new WorkflowSessionApi(new ComplyCubeClient("<YOUR_API_KEY>"));

var workflowSession = await wfSessionApi.CompleteAsync("WORKFLOW_SESSION_ID");
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Retrieve verification results

Your mobile backend can retrieve the workflow session details and associated check results using our API.

All our checks are **asynchronous.** If you have [set up webhooks ](https://docs.complycube.com/api-reference/other-resources/webhooks/create-a-webhook)as described in our [webhooks guide](https://docs.complycube.com/documentation/guides/webhooks),  you will be notified once a check completes.

You can retrieve the details of a workflow session by calling the [retrieve workflow session request](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/workflow-sessions/get-a-workflow-session).

To retrieve the check results, you can perform a [get check request](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/checks/get-a-check).

**Example of a workflow session retrieval request**

{% tabs %}
{% tab title="cURL" %}

```javascript
curl -X GET https://api.complycube.com/v1/workflowSessions/{:workflowSessionId} \
     -H 'Authorization: <YOUR_API_KEY>' 
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const { ComplyCube } = require("@complycube/api");

const complycube = new ComplyCube({ apiKey: "<YOUR_API_KEY>" });

const wfSession = await complycube.workflowSession.get("WORKFLOW_SESSION_ID");
```

{% endtab %}

{% tab title="Python" %}

```python
from complycube import ComplyCubeClient

cc_api = ComplyCubeClient(api_key='<YOUR_API_KEY>')

workflow_session = cc_api.workflowsessions.get('WORKFLOW_SESSION_ID')
```

{% endtab %}

{% tab title="PHP" %}

```php
use ComplyCube\ComplyCubeClient;

$ccapi = new ComplyCubeClient('<YOUR_API_KEY>');

$workflowSession = $ccapi->workflowSessions()->get('WORKFLOW_SESSION_ID');
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using ComplyCube.Net;
using ComplyCube.Net.Resources.WorkflowSessions;

var wfSessionApi = new WorkflowSessionApi(new ComplyCubeClient("<YOUR_API_KEY>"));

var workflowSession = await wfSessionApi.GetAsync("WORKFLOW_SESSION_ID");
```

{% endtab %}
{% endtabs %}

**Example of a check retrieval request**

{% tabs %}
{% tab title="cURL" %}

```javascript
curl -X GET https://api.complycube.com/v1/checks/{:checkId} \
     -H 'Authorization: <YOUR_API_KEY>' 
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const { ComplyCube } = require("@complycube/api");

const complycube = new ComplyCube({ apiKey: "<YOUR_API_KEY>" });

const check = await complycube.check.get("CHECK_ID");
```

{% endtab %}

{% tab title="Python" %}

```python
from complycube import ComplyCubeClient

cc_api = ComplyCubeClient(api_key='<YOUR_API_KEY>')

check = cc_api.checks.get('CHECK_ID')
```

{% endtab %}

{% tab title="PHP" %}

```php
use ComplyCube\ComplyCubeClient;

$ccapi = new ComplyCubeClient('<YOUR_API_KEY>');

$check = $ccapi->checks()->get('CHECK_ID');
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using ComplyCube.Net;
using ComplyCube.Net.Resources.Checks;

var checkApi = new CheckApi(new ComplyCubeClient("<YOUR_API_KEY>"));

var check = await checkApi.GetAsync("CHECK_ID");
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## NFC capture

The ComplyCube mobile SDK supports NFC-based **RFID chip** reading for identity documents equipped with embedded chips. This allows for secure data extraction and high-assurance document authentication.

{% hint style="info" %}
Please get in touch with your **Account Manager** or [support](https://support.complycube.com/hc/en-gb/requests/new) to get access to our NFC-enabled Mobile SDK.
{% endhint %}

#### Pre-requisites

{% hint style="info" %}
To use this feature, your app must have the **`Near Field Communication Tag Reading`** capability enabled. To add this capability to your app, refer to [Apple's guide here](https://help.apple.com/xcode/mac/current/#/dev88ff319e7).
{% endhint %}

1. Install the **CocoaPods Artifactory** plugin by running the following command in your terminal:<br>

   ```bash
   gem install cocoapods-art
   ```

2. To add the library, copy your repository credentials into a `.netrc` file to your home directory and setup the repository:<br>

   <pre class="language-bash" data-overflow="wrap"><code class="lang-bash">pod repo-art add cc-cocoapods-release-local "https://complycuberepo.jfrog.io/artifactory/api/pods/cc-cocoapods-release-local"
   </code></pre>

   \
   Remember to fetch your credentials from **JFrog** using the **Set Me Up** button [here](https://complycuberepo.jfrog.io/ui/repos/tree/General/cc-cocoapods-release-local).<br>

3. Add plugin repos and install the pod using your `Podfile`:<br>

   ```ruby
    plugin 'cocoapods-art', :sources => [
      'cc-cocoapods-release-local',
      'trunk'
    ]
    ...
    platform :ios, '13.0' # Or above

    target 'YourApp' do
      use_frameworks!
      use_modular_headers!
      ...
      pod 'ComplyCubeMobileSDK', '1.2.6-nfc'
      ...
    en
   ```

4. You must add the following keys to your application `Info.plist` file:

```xml
<key>NFCReaderUsageDescription</key>
<string>Required to read from NFC enabled documents</string>
```

5. To read NFC tags correctly, you need to add the following entries to your app target's `Info.plist` file:

```xml
<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>
```

## Branding

The SDK allows you to customize the UI to match your application’s visual identity. You can define primary and accent colors during configuration to align the verification flow with your brand guidelines. Learn more about our **appearance properties** (see below).

**Creating a custom appearance**

Before initiating the flow, create your own custom design and integrate it into your Flow Builder.

```swift
let ccLookAndFeel = LookAndFeel()
ccLookAndFeel.primaryButtonBgColor = .green
ccLookAndFeel.uiInterfaceStyle = .dark //can also be .light or .inherited
```

#### **Applying your custom appearance**

Set the custom appearance that you have created using the Flow Builder.

```swift
let sdk = ComplyCubeMobileSDK.FlowBuilder()
  .withLookAndFeel(ccLookAndFeel)
  .start(fromVc: self)
```

<details>

<summary>Appearance properties</summary>

<table><thead><tr><th width="371.70703125">Appearance Property</th><th>Description</th></tr></thead><tbody><tr><td><code>primaryButtonBgColor</code></td><td>Primary action button background color.</td></tr><tr><td><code>primaryButtonTextColor</code></td><td>Primary action button text color.</td></tr><tr><td><code>primaryButtonBorderColor</code></td><td>Primary action button border color.</td></tr><tr><td><code>secondaryButtonBgColor</code></td><td>Secondary button background color.</td></tr><tr><td><code>secondaryButtonPressedBgColor</code></td><td>Secondary action button pressed background color.<br><br><em>Not supported by Android.</em></td></tr><tr><td><code>secondaryButtonTextColor</code></td><td>Secondary action button text color.</td></tr><tr><td><code>secondaryButtonBorderColor</code></td><td>Secondary action button border color.</td></tr><tr><td><code>documentTypeSelectorBgColor</code></td><td>Document type selection button color.</td></tr><tr><td><code>documentTypeSelectorBorderColor</code></td><td>Document type selection button border color.</td></tr><tr><td><code>documentTypeSelectorTitleTextColor</code></td><td>Document type selection title text color.</td></tr><tr><td><code>documentTypeSelectorDescriptionTextColor</code></td><td>Document type selection description text color.</td></tr><tr><td><code>documentTypeSelectorIconColor</code></td><td>Document type selection icon color.</td></tr><tr><td><code>bodyTextColor</code></td><td>Screen body text color.</td></tr><tr><td><code>linkButtonTextColor</code></td><td>Links color.<br><br><em>Not supported by Android.</em></td></tr><tr><td><code>headingTextColor</code></td><td>Title heading text color.</td></tr><tr><td><code>subheadingTextColor</code></td><td>Subheading text color.</td></tr><tr><td><code>infoPanelTitleColor</code></td><td>Information panel title color.</td></tr><tr><td><code>infoPanelDescriptionTextColor</code></td><td>Information panel description text color.</td></tr><tr><td><code>infoPanelBgColor</code></td><td>Information panel background color.</td></tr><tr><td><code>infoPanelIconColor</code></td><td>Information panel icon color.</td></tr><tr><td><code>errorPanelTitleColor</code></td><td>Error panel title color.</td></tr><tr><td><code>errorPanelDescriptionTextColor</code></td><td>Error panel description text color.</td></tr><tr><td><code>errorPanelBgColor</code></td><td>Error panel background color.</td></tr><tr><td><code>errorPanelIconColor</code></td><td>Error panel icon color.</td></tr><tr><td><code>cameraButtonBgColor</code></td><td>Camera capture button background color.</td></tr><tr><td><code>uiInterfaceStyle</code></td><td>Set the SDK to use dark mode, light mode, or system Inherited.</td></tr></tbody></table>

</details>

## Localization

The SDK supports several languages, including those listed below.

{% columns %}
{% column %}

* Arabic - `ar` 🇦🇪
* Chinese (Simplified) - `zh` 🇨🇳
* Chinese (Traditional) - `hk` 🇨🇳
* Dutch - `nl` 🇳🇱
* English - `en` 🇺🇸
* French - `fr` 🇫🇷
* German - `de` 🇩🇪
* Hindi - `hi` 🇮🇳
* Indonesian -  `id` 🇮🇩
* Italian - `it` 🇮🇹
  {% endcolumn %}

{% column %}

* Japanese -  `ja` 🇯🇵
* Korean -  `ko` 🇰🇷
* Norwegian - `no` 🇳🇴
* Polish - `po` 🇵🇱
* Portuguese - `pt` 🇵🇹
* Spanish - `es` 🇪🇸
* Swedish - `sv` 🇸🇪
* Thai -  `th` 🇹🇭
* Vietnamese -  `vi` 🇻🇳
* and more...
  {% endcolumn %}
  {% endcolumns %}

## Result handling

To run a verification session, you must implement the **success**, **cancelled**, and **error** callbacks.

On a successful completion (`onSuccess`), you can trigger a [workflow session complete requests](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/workflow-sessions/complete-a-workflow-session) from your backend using the workflow session ID returned in the result object.

If the user exits the flow before completion, the `onCancelled` callback is invoked with a descriptive reason indicating why the session was cancelled (e.g. user exit, timeout, permission denied)

{% hint style="info" %}
In some cases, the customer may cancel the flow after completing one or more capture stages. If this occurs, any data captured prior to cancellation, such as documents or biometric media, may have already been uploaded to their client record.
{% endhint %}

If the SDK encounters an issue, the `onError` callback is triggered with a `ComplyCubeError` object containing the error type and message. Refer to the **error codes** (see below), for a full list of possible error cases.

```swift
extension ViewController: ComplyCubeMobileSDKDelegate {
  func onSuccess(_ result: ComplyCubeResult) {
   /*
    Handling successful results:
      The 'results' parameter will contain: 
      - "workflowSessionId": "xxxxx"
    */
    print("The flow has completed - here are the ID's returned")
  }
  
  func onError(_ error: ComplyCubeError) {
    // Handle errors
    print("An error has occurred")
  }

  func onCancelled(_ error: ComplyCubeError) {
    // Handle cancellations
    print("The user has cancelled the flow or not accepted the terms")
  }  
}
```

<details>

<summary>Error codes</summary>

<table data-full-width="false"><thead><tr><th width="312.48828125">Error Type</th><th>Description</th></tr></thead><tbody><tr><td><code>BiometricStageCount</code></td><td>The configuration includes duplicate <strong>selfie photo</strong> or <strong>selfie video</strong> stages.</td></tr><tr><td><code>Cancelled</code></td><td>The client cancelled the flow and exited the SDK (triggered the <code>onCancelled</code> callback).</td></tr><tr><td><code>Connectivity</code></td><td>A network error has occurred.</td></tr><tr><td><code>DocumentMandatory</code></td><td>A <strong>document stage</strong> is required based on the configured stages but has not been included.</td></tr><tr><td><code>ExpiredToken</code></td><td>The SDK token has expired. Generate a new token and restart the flow.</td></tr><tr><td><code>FlowError</code></td><td>An unrecoverable error occurred during the flow.</td></tr><tr><td><code>InvalidCountryCode</code></td><td>An invalid country code was provided.</td></tr><tr><td><code>JailBroken</code></td><td>The SDK cannot run on this device because it has been jailbroken or compromised.</td></tr><tr><td><code>NoDiskAccess</code></td><td>The client denied disk access permissions required by the SDK.</td></tr><tr><td><code>NoDocumentTypes</code></td><td>A <strong>document stage</strong> was initialized without specifying any document types.</td></tr><tr><td><code>NoResult</code></td><td>No result was returned to the callback. If this persists, please contact support.</td></tr><tr><td><code>NoUserConsent</code></td><td>The client has not provided consent to proceed with the SDK flow.</td></tr><tr><td><code>NotAuthorized</code></td><td>The SDK attempted to access an endpoint it is not authorized to use.</td></tr><tr><td><code>Unknown</code></td><td>An unexpected error occurred. If this happens repeatedly, contact support.</td></tr><tr><td><code>UnsupportedCountryTypeCombination</code></td><td>The selected country and document type combination is not supported.</td></tr><tr><td><code>UnsupportedDocumentType</code></td><td>The provided document type is not supported.</td></tr><tr><td><code>UploadError</code></td><td>An error occurred while uploading a document or selfie.</td></tr><tr><td><code>UploadRequireGuidance</code></td><td>If <code>useLiveCaptureOnly</code> is set to <code>false</code>, guidance must be enabled by setting <code>isGuidanceEnabled</code> to <code>true</code>.</td></tr></tbody></table>

</details>

## Events tracking

The SDK tracks a range of **events** throughout the verification flow, covering all key user interactions across stages. See below for the list of events.

If you need to implement custom analytics, you can hook into these events and trigger your own tracking logic accordingly.

To incorporate your own tracking, define a function and apply it using `withEventHandler` when initializing the `FlowBuilder`:

```swift
let sdk = ComplyCubeMobileSDK.FlowBuilder()
  .withEventHandler({ (event: Event) -> Void in
    // Insert custom user tracking code here
  })
```

<details>

<summary>Events</summary>

| Event                                                                                                                                                                                                    |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p><code>INTRO</code><br><br>The client reached the intro screen.</p>                                                                                                                                    |
| <p><code>CONSENT\_STAGE</code><br><br>The client reached the consent screen.</p>                                                                                                                         |
| <p><code>CONSENT\_STAGE\_WARNING</code><br><br>The client attempted to exit without giving consent.</p>                                                                                                  |
| <p><code>CAMERA\_ACCESS\_PERMISSION</code><br><br>The client reached the camera permission request screen.</p>                                                                                           |
| <p><code>DOCUMENT\_STAGE\_CAPTURE\_GUIDANCE</code><br><br>The client reached the document capture guidance screen.</p>                                                                                   |
| <p><code>DOCUMENT\_STAGE\_DOCUMENT\_TYPE</code><br><br>The client has reached the document type selection screen for a document capture.</p>                                                             |
| <p><code>DOCUMENT\_STAGE\_SELECT\_COUNTRY</code><br><br>The client reached the country selection screen for a document capture.</p>                                                                      |
| <p><code>DOCUMENT\_STAGE\_ONE\_SIDE\_CAMERA</code><br><br>The client reached the capture camera stage for a one-sided ID document.</p>                                                                   |
| <p><code>DOCUMENT\_STAGE\_ONE\_SIDE\_CAMERA\_MANUAL\_MODE</code><br><br>The client reached the manual camera capture screen of a one-sided ID document.</p>                                              |
| <p><code>DOCUMENT\_STAGE\_ONE\_SIDE\_CHECK\_QUALITY</code><br><br>The client reached the quality preview screen for a one-sided ID document.</p>                                                         |
| <p><code>DOCUMENT\_STAGE\_TWO\_SIDE\_CAMERA\_FRONT</code><br><br>The client reached the camera capture screen for the front side of a two-sided ID document.</p>                                         |
| <p><code>DOCUMENT\_STAGE\_TWO\_SIDE\_CAMERA\_FRONT\_MANUAL\_MODE</code><br><br>The client reached the manual camera capture screen for the front side of a two-sided ID document.</p>                    |
| <p><code>DOCUMENT\_STAGE\_TWO\_SIDE\_CHECK\_QUALITY\_FRONT</code><br><br>The client reached the quality preview screen for the front side of a two-sided ID document.</p>                                |
| <p><code>DOCUMENT\_STAGE\_TWO\_SIDE\_CAMERA\_BACK</code><br><br>The client reached the camera capture screen for the back side of a two-sided ID document.</p>                                           |
| <p><code>DOCUMENT\_STAGE\_TWO\_SIDE\_CAMERA\_BACK\_MANUAL\_MODE</code><br><br>The client reached the manual camera capture screen for the back side of a two-sided ID document.</p>                      |
| <p><code>DOCUMENT\_STAGE\_TWO\_SIDE\_CHECK\_QUALITY\_BACK</code><br><br>The client reached the quality preview screen for the back side of a two-sided ID document.</p>                                  |
| <p><code>BIOMETRICS\_STAGE\_SELFIE\_CAPTURE\_GUIDANCE</code><br><br>The client reached the selfie capture guidance screen.</p>                                                                           |
| <p><code>BIOMETRICS\_STAGE\_SELFIE\_CAMERA</code><br><br>The client reached the selfie photo capture camera screen.</p>                                                                                  |
| <p><code>BIOMETRICS\_STAGE\_SELFIE\_CAMERA\_MANUAL\_MODE</code><br><br>The client reached the manual selfie photo capture camera screen.</p>                                                             |
| <p><code>BIOMETRICS\_STAGE\_SELFIE\_CHECK\_QUALITY</code><br><br>The client reached the selfie capture photo review screen.</p>                                                                          |
| <p><code>BIOMETRICS\_STAGE\_VIDEO\_CAMERA</code><br><br>The client reached the video selfie camera screen.</p>                                                                                           |
| <p><code>BIOMETRICS\_STAGE\_VIDEO\_CAMERA\_MANUAL\_MODE</code><br><br>The client reached the manual capture camera screen for a video selfie.</p>                                                        |
| <p><code>BIOMETRICS\_STAGE\_VIDEO\_ACTION\_ONE</code><br><br>The client reached the first action in a video selfie capture.</p>                                                                          |
| <p><code>BIOMETRICS\_STAGE\_VIDEO\_ACTION\_TWO</code><br><br>The client reached the second action in a video selfie capture.</p>                                                                         |
| <p><code>BIOMETRICS\_STAGE\_VIDEO\_CHECK\_QUALITY</code><br><br>The client reached the manual selfie video capture camera screen.</p>                                                                    |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_CAPTURE\_GUIDANCE</code><br><br>The client reached the proof of address capture guidance screen.</p>                                                                 |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_DOCUMENT\_TYPE</code><br><br>The client reached the document type selection screen for a proof of address capture.</p>                                               |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_SELECT\_COUNTRY</code><br><br>The client reached the country selection screen for a proof of address capture.</p>                                                    |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_ONE\_SIDE\_CAMERA</code><br><br>The client reached the capture camera stage for a one-sided proof of address document.</p>                                           |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_ONE\_SIDE\_CAMERA\_MANUAL\_MODE</code><br>The client reached the manual capture camera stage for a one-sided proof of address document.</p>                          |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_ONE\_SIDE\_CHECK\_QUALITY</code><br><br>The client has reached the quality preview screen for a one-sided proof of address document.</p>                             |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_TWO\_SIDE\_CAMERA\_FRONT</code><br><br>The client reached the capture camera stage for the front side of a two-sided proof of address document.</p>                  |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_TWO\_SIDE\_CAMERA\_FRONT\_MANUAL\_MODE</code><br>The client reached the manual capture camera stage for the front side of a two-sided proof of address document.</p> |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_TWO\_SIDE\_CHECK\_QUALITY\_FRONT</code><br>The client reached the quality preview screen for the front side of a two-sided proof of address document.</p>            |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_TWO\_SIDE\_CAMERA\_BACK</code><br><br>The client reached the capture camera stage for the back side of a two-sided proof of address document.</p>                    |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_TWO\_SIDE\_CAMERA\_BACK\_MANUAL\_MODE</code><br>The client reached the manual capture camera stage for the back side of a two-sided proof of address document.</p>   |
| <p><code>PROOF\_OF\_ADDRESS\_STAGE\_TWO\_SIDE\_CHECK\_QUALITY\_BACK</code><br>The client reached the quality preview screen for the back side of a two-sided proof of address document.</p>              |
| <p><code>COMPLETION\_STAGE</code><br><br>The client has reached the completion screen.</p>                                                                                                               |

</details>

## Token expiry handling

To handle token expiration gracefully, you can provide a callback function that generates a new SDK token when needed. This allows the flow to continue seamlessly without requiring the user to restart the session manually.

```swift
let sdk = ComplyCubeMobileSDK.FlowBuilder()
  .withTokenExpiryHandler({ () -> String in
    // Insert custom token renewal code here
  })
```
