# Check-Driven Integration

## Overview

This guide walks you through integrating the **Android SDK** with ComplyCube using the check-driven approach, giving you direct control over individual verification steps.

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

{% hint style="warning" %}
You’re viewing the **check-driven** SDK guide - an approach that provides detailed control but is best suited for **partners** or **advanced use cases.**  We recommend using **workflow integration** for most implementations.
{% endhint %}

## 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 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%2FD0FaWEHn2LY42SNBlaQB%2Fdocumentation-mobile-sdk.png?alt=media&#x26;token=889e78a5-b688-491c-98c6-a3227f8656c9" alt=""><figcaption><p>Mobile SDK Integration flow</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-android-sdk).<br>

{% stepper %}
{% step %}

#### Install the SDK

Our SDK requires the repositories below.

```java
repositories {
  mavenCentral()
}

dependencies {
    implementation "com.complycube:complycube-sdk:+"
}
```

{% 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 %}

#### Prepare the stages

Set up the stages you wish to include in your flow.

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

```kotlin
val complyCubeBuilder = ComplyCubeSdk.Builder(this, callback =  { }).apply {
    withStages(
        Document(
            identityDocumentType = Passport(), DrivingLicence(),
            useLiveCaptureOnly = false
        ),
        SelfiePhoto()
    )
}
```

{% endtab %}

{% tab title="Java" %}

```java
IdentityDocumentType[] identityDocumentTypes;

identityDocumentTypes = new IdentityDocumentType[1];

identityDocumentTypes[0] = new DrivingLicence();

Document document = new Document(
    // documentType
    null,
    // documentTypes
    identityDocumentTypes,
    // isGuidanceEnabled
    true,
    // useLiveCaptureOnly
    false,
    // isMLAssistanceEnabled
    true,
    // retryLimit
    5,
    // isNFCEnabled
    true
);
```

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

{% step %}

#### Initialize the SDK

You can now initialize a flow. The sequence of stages you specify determines the order in which your client sees those stages.

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

```kotlin
val complyCubeBuilder = ComplyCubeSdk.Builder(this, callback =  { }).apply {
    withStages(
        Document(
            identityDocumentType = Passport(), DrivingLicence(),
            useLiveCaptureOnly = false
        ),
        SelfiePhoto()
    )
}.start(
    ClientAuth(
        token = "SDK_TOKEN",
        clientId = "CLIENT_ID"
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
ComplyCubeSdk.Builder complycubeFlow =
    new ComplyCubeSdk.Builder(this, result - > {});

complycubeFlow.withStages(
    document,
    new SelfiePhoto()
);

complycubeFlow.start(new ClientAuth(
    "SDK_TOKEN",
    "CLIENT_ID"
));
```

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

{% step %}

#### Perform checks

Using the `StageResults` returned by the flow, you can trigger your **mobile backend** to run the necessary checks on your client.

For example, use the result of a selfie and document capture as follows:

* `StageResult.Document.Id` to run a [Document Check](https://docs.complycube.com/api-reference/check-types/document-check).
* `StageResult.Document.Id` and `StageResult.LivePhoto.Id` to run an [Identity Check](https://docs.complycube.com/api-reference/check-types/identity-check).

**Example request for a Document Check**

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

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

{% endtab %}

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

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

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

const check = await complycube.check.create("CLIENT_ID", {
    type: "document_check",
    documentId: "DOCUMENT_ID"
});
```

{% endtab %}

{% tab title="Python" %}

```python
from complycube import ComplyCubeClient

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

check = cc_api.checks.create(
    "CLIENT_ID", 
    "document_check", 
    documentId="DOCUMENT_ID"
)
```

{% endtab %}

{% tab title="PHP" %}

```php
use ComplyCube\ComplyCubeClient;

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

$result = $ccapi->checks()->create('CLIENT_ID', [
    'type' => 'document_check',
    'documentId' => 'DOCUMENT_ID'
]);
```

{% endtab %}

{% tab title=".NET" %}

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

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

var checkRequest = new CheckRequest {
  clientId = "CLIENT_ID",
  type = "document_check",
  documentId = "DOCUMENT_ID"
};

var check = await checkApi.CreateAsync(checkRequest);
```

{% endtab %}
{% endtabs %}

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.

To retrieve the check results, you can perform a get [check request](https://docs.complycube.com/api-reference/checks/get-a-check).
{% endstep %}

{% step %}

#### Retrieve verification results

Your mobile backend can retrieve all 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.

To retrieve the check results,  you can perform a get [check request](https://docs.complycube.com/api-reference/checks/get-a-check).&#x20;

**Example 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 %}

## Stages

Each stage in the flow can be customized to create the ideal journey for your customers.

The snippet below demonstrates how to set up a customized flow using the ComplyCube Mobile SDK.

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

```kotlin
var complycubeFlow = ComplyCubeSdk.Builder(this, callback = ...)
  .withSDKToken("SDK_Token")
  .withClientId("CLIENT_ID")
  .withStages(
    Welcome(...),
    Consent(...),
    Document(...),
    SelfiePhoto(...),
    AddressCapture(...),
    ProofOfAddress(...),
    Complete(...)
  )
  .withLookAndFeel(...)
  .withCustomLanguage(...)

complycubeFlow.start()
```

{% endtab %}

{% tab title="Java" %}

```java
complycubeFlow.withStages(
    new Welcome(...),
    new UserConsent(...),
    new Document(...),
    new SelfiePhoto(...),
    new AddressCapture(...),
    new ProofOfAddress(...),
    new Complete(...)
);

complycubeFlow.withLookAndFeel(...);
complycubeFlow.withCustomLanguage(..);
```

{% endtab %}
{% endtabs %}

### **Welcome**

The Welcome screen is always shown first in the verification flow. It displays a welcome message along with a summary of the configured stages the user will complete.

You can customize the screen title to align with your app’s tone and branding.

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

```kotlin
var welcomeStage = Welcome(
    title = "Custom Screen Title",
    message = "Custom welcome message."
)
```

{% endtab %}

{% tab title="Java" %}

```java
 Welcome welcome = new Welcome(
     "Custom Screen Title",
     "Custom welcome message",
     null
 );
```

{% endtab %}
{% endtabs %}

### **Consent**

This stage is used to collect explicit user consent before proceeding with the verification flow. It helps you meet regulatory and compliance requirements where applicable.

You can customize the screen title and message to match your legal or branding needs.

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

```kotlin
var consentStage = Consent(
    title = "Custom Consent Screen Title"
)
```

{% endtab %}

{% tab title="Java" %}

```java
UserConsent userConsent = new UserConsent(
    "Custom Consent Screen Title",
    null,
    null
);
```

{% endtab %}
{% endtabs %}

### **Selfie photo and video**

You can request either a selfie photo  (i.e. a [Live Photo](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/live-photos)) or a selfie video (i.e. a [Live Video](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/live-videos)) from the customer as part of the biometric verification process.

* **Photo**: Captures a still image and performs a passive liveness check before allowing submission.
* **Video**: Records a short video where the user completes an on-screen challenge (e.g. head movement or spoken phrase).

{% hint style="warning" %}
If you attempt to add both types of stages, the SDK will throw a **`ComplyCubeErrorCode.BiometricStageCount`** error stating multiple conflicting stages.
{% endhint %}

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

```kotlin
var selfiePhoto = SelfiePhoto(
    // Enable ML assistance during capture
    isMLAssistantEnabled = false
)
```

{% endtab %}

{% tab title="Java" %}

```java
SelfiePhoto selfiePhoto = new SelfiePhoto(
    // isGuidanceEnabled
    true,
    // useLiveCaptureOnly
    true,
    // isMLAssistanceEnabled
    false,
    // retryLimit
    5
);
```

{% endtab %}
{% endtabs %}

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

```swift
var selfieVideo = SelfieVideo(
    // Enable ML assistance during capture
    isMLAssistantEnabled = false
)
```

{% endtab %}

{% tab title="Java" %}

```java
 SelfieVideo selfieVideo = new SelfieVideo(
     // isGuidanceEnabled
     true,
     // isMLAssistanceEnabled
     false,
     // retryLimit
     5
 );
```

{% endtab %}
{% endtabs %}

### **Document**

The Document stage allows users to select and capture an identity document for verification (e.g. passport, ID card, residence permit). You can customize these screens to:

* Limit the scope of document types the client can select, e.g., Passport only.
* Set the document issuing countries they are allowed for each document type.
* Add or remove automated capture using smart assistance.
* Show or hide the instruction screens before capture.
* Set a retry limit to allow clients to progress the journey regardless of capture quality.

{% hint style="info" %}
If you provide only one document type, the document type selection screen will be skipped. The country selection screen will be skipped if you provide only a single country for a given document type.
{% endhint %}

You can control whether instructional screens are shown before each camera capture by enabling or disabling guidance mode. Only disable guidance if your users are already clearly informed about the capture steps, as these screens help reduce user error and improve capture quality.

{% hint style="info" %}
Please note the `retryLimit` you set here will take precedence over the retry limit that has been set globally in your [automation settings](https://portal.complycube.com/automations).
{% endhint %}

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

```kotlin
var documentStage = Document(
    // Set document types and limit the enabled countries
    Passport(), DrivingLicence((Country.GB, Country.US)),

    // Enable or disable additional guidance for the user
    isGuidanceEnabled = true,

    // Enable or disable the ability to upload an image from file
    useLiveCaptureOnly = true,

    // Enable ML assistance during capture
    isMLAssistantEnabled = true,
    
    // Set a maximum for quality check attempts before uploadingoft checks
    retryLimit = 3
)
```

{% endtab %}

{% tab title="Java" %}

```java
IdentityDocumentType[] identityDocumentTypes;

identityDocumentTypes = new IdentityDocumentType[1];

identityDocumentTypes[0] = new DrivingLicence();

Document document = new Document(
    null,
    identityDocumentTypes, // documentTypes
    true, // isGuidanceEnabled
    false, // useLiveCaptureOnly
    true, // isMLAssistanceEnabled
    5, // retryLimit
    true // isNFCEnabled
);
```

{% endtab %}
{% endtabs %}

### 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

1. Start by adding your access credentials for the ComplyCube NFC-Enabled SDK repository to the `gradle.properties` file of your **mobile app**:

```gradle
artifactory_user= "USERNAME"
artifactory_password= "ENCRYPTED PASS"
artifactory_contextUrl= https://complycuberepo.jfrog.io/artifactory
```

2. Then, update your project level `build.gradle` file with the ComplyCube SDK repository Maven settings:

```gradle
buildscript {
    repositories {
        ...
    }
    dependencies {
        ....
        // Check for the latest version here: 
        // http://plugins.gradle.org/plugin/com.jfrog.artifactory
        classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
    }
    ...
}

allprojects {
    apply plugin: "com.jfrog.artifactory"
    ...
}

artifactory {
  contextUrl = "${artifactory_contextUrl}"  
  resolve {
    repository {
      repoKey = 'cc-gradle-release'
      username = "${artifactory_user}"
      password = "${artifactory_password}"
      maven = true
    }
  }
}
```

3. Update your module level `build.gradle` file with the SDK dependency:

```gradle
dependencies {
    implementation "com.complycube:complycube-sdk:+"
    implementation "com.complycube:nfc-plugin:+"
}
```

#### Enabling NFC capture

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

```kotlin
var documentStage = Document(
    // Add this property to enable NFC
    isNFCEnabled = true,
)
```

{% endtab %}

{% tab title="Java" %}

```java
IdentityDocumentType[] identityDocumentTypes;

identityDocumentTypes = new IdentityDocumentType[1];

identityDocumentTypes[0] = new Passport();

Document document = new Document(
    null,
    // documentTypes
    identityDocumentTypes,
    // isGuidanceEnabled
    true,
    // useLiveCaptureOnly
    false,
    // isMLAssistanceEnabled
    true,
    // retryLimit
    5,
    // isNFCEnabled
    true
);
```

{% endtab %}
{% endtabs %}

### **Address capture**

This stage allows users to manually enter their residential address. You can customize it to:

* Restrict input to specific countries
* Enable or disable the address autocomplete feature for faster and more accurate entry

{% tabs %}
{% tab title="Kotlin" %}
{% code fullWidth="false" %}

```kotlin
var addressCaptureStage = AddressCapture(
    // This enables our address autocomplete
    useAutoComplete = true,
    
    // A list of acceptable countries for address capture feature
    allowedCountries = setOf(Country.GB,Country.US)
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}

```java
Set<Country> countries = new HashSet<> ();

countries.add(Country.GB);
countries.add(Country.US);

AddressCapture addressCapture = new AddressCapture(
    // useAutoComplete
    true,
    // allowedCountries
    countries
);
```

{% endtab %}
{% endtabs %}

### **Proof of address**

This stage allows customers to submit a document verifying their residential address. You can configure which document types are accepted, such as utility bills or bank statements, and choose whether customers can upload a file or must capture the document live using their device camera.

{% hint style="info" %}
By default, this stage includes the **address capture stage** with autocomplete enabled to assist users during entry. Autocomplete can be disabled if not required.
{% endhint %}

{% tabs %}
{% tab title="Kotlin" %}
{% code fullWidth="false" %}

```kotlin
var poaStage = ProofOfAddress(
    // A list of supported document types that can be submitted
    UtilityBill(), BankStatement(),

    // When disabled, the client will be forced to perform a live capture
    useLiveCaptureOnly = false,

    // This enables address capture during the proof of address flow
    isAddressCaptureEnabled = false
    ...
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}

```java
LinkedHashSet<ProofOfAddressDocumentType> documentTypes =
    new LinkedHashSet<ProofOfAddressDocumentType> ();

documentTypes.add(new UtilityBill());
documentTypes.add(new BankStatement());

ProofOfAddress poaStage = new ProofOfAddress(
    documentTypes,
    // isGuidanceEnabled
    true,
    // useLiveCaptureOnly
    false,
    // isMLAssistanceEnabled
    true,
    // retryLimit
    5,
    // isAddressCaptureEnabled
    false
);
```

{% endtab %}
{% endtabs %}

### **Completion**

You can include a completion stage at the end of the flow to confirm that the process has been successfully finished. This screen provides a clear end point for the user and can be customized to display a confirmation message or next steps.

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

```kotlin
val completionStage = Complete(
    title = "Thank you!",
    message = "Your KYC submission has been completed"
)
```

{% endtab %}

{% tab title="Java" %}

```java
Complete complete = new Complete(
    "Custom Complete Title",
    "Custom Complete Message",
    null
);
```

{% endtab %}
{% endtabs %}

## 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).

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

```kotlin
complycubeFlow.withLookAndFeel(
    lookAndFeel = LookAndFeel(
        primaryButtonBgColor = Color.RED,
        /* Can also be set to UIInterfaceStyle.LIGHT 
        or UIInterfaceStyle.INHERITED */
        uiInterfaceStyle = UIInterfaceStyle.DARK
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
complycubeFlow.withLookAndFeel(new LookAndFeel(
    // primaryButtonBGColor
    Color.parseColor("#FF0000"),
    // uiInterfaceStyle
    UIInterfaceStyle.DARK,
    ...
));
```

{% endtab %}
{% endtabs %}

<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 verification checks, you must implement the **success**, **cancelled**, and **error** callbacks.

On a successful completion (`onSuccess`), you can trigger [check requests](https://app.gitbook.com/s/kAhgmUKSf8CFUFVL3GEe/core-resources/checks/create-a-check) from your backend using the resource IDs returned in the result object. These IDs correspond to the uploaded assets (e.g. documents, selfies) and can be used to initiate verification checks via the ComplyCube API.

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.

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

```kotlin
//Register a result handler to process the outcome of the flow.
var complycubeFlow =
    ComplyCubeSdk.Builder(
        this,
        callback = { result ->
            when (result) {
                is Result.Success -> { 
                /*
                Handling successful results:
                Our default flow includes three components: an Identity Document,
                a Selfie (Live Photo), and a Proof of Address. Upon successful 
                completion, the 'Result' will contain an array of stage results.
                Each stage result provides the ID of the uploaded item:
                  - 'StageResult.Document.Id': the uploaded Document ID.
                  - 'StageResult.LivePhoto.Id': the uploaded Live Photo ID.
                  - 'StageResult.ProofOfAddress.Id': the uploaded PA Document ID.
                */
                }
                is Result.Cancelled -> { /* Handle Cancelled result */ }
                is Result.Error -> {
                    // Handle errors
                    when (result.errorCode) {
                        ComplyCubeErrorCode.UploadError -> {
                            // Handle Upload error
                        }
                        ComplyCubeErrorCode.BiometricStageCount -> {
                            // Handle BiometricStageCount error
                        }
                        // ...other error handlers
                    }
                }
                else -> {}
            }
        }
    )

complycubeFlow.start(clientAuth)
```

{% endtab %}

{% tab title="Java" %}

```java
//Register a result handler to process the outcome of the flow.
ComplyCubeSdk.Builder complycubeFlow = new ComplyCubeSdk.Builder(this, result - > {
    if (result instanceof Result.Success) {
        /*
        Handling successful results:
        Our default flow includes three components: an Identity Document,
        a Selfie (Live Photo), and a Proof of Address. Upon successful 
        completion, the 'Result' will contain an array of stage results.
        Each stage result provides the ID of the uploaded item:
          - 'StageResult.Document.Id': the uploaded Document ID.
          - 'StageResult.LivePhoto.Id': the uploaded Live Photo ID.
          - 'StageResult.ProofOfAddress.Id': the uploaded PA Document ID.
        */
    } else if (result instanceof Result.Error) {
        switch (((Result.Error) result).getErrorCode()) {
            case ComplyCubeErrorCode.UploadError.INSTANCE:
                // Handle upload error
                break;
            case ComplyCubeErrorCode.BiometricStageCount.INSTANCE:
                // Handle BiometricStageCountError
                break;
                // ... other error handlers
            default:
        }
    } else if (result instanceof Result.Canceled) {
        // Handle Cancelled result
    }
    return null;
});
```

{% endtab %}
{% endtabs %}

<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 `Builder`:

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

```kotlin
complycubeBuilder.withEventHandler(true) { event ->
    // Insert custom user tracking here
}
```

{% endtab %}

{% tab title="Java" %}

```java
complycubeFlow.withEventHandler(true, analyticsEvent - >
    // Insert custom user tracking here
);
```

{% endtab %}
{% endtabs %}

<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.

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

```kotlin
var complyCubeBuilder = ComplyCubeSdk.Builder(this,
    callback = { result ->
        if (result is Result.Error) {
            if (result.errorCode is ComplyCubeErrorCode.ExpiredToken) {
                // Insert custom token renewal code here
            }
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
ComplyCubeSdk.Builder complyCubeBuilder =
    new ComplyCubeSdk.Builder(this, result - > {
        if (result instanceof Result.Error) {
            if (((Result.Error) result).getErrorCode() == ComplyCubeErrorCode.ExpiredToken.INSTANCE) {
                // Insert custom token renewal code here
            }
        }
        return null;
    });
```

{% endtab %}
{% endtabs %}
