Full SDK Integration

This page describes on how to do a straightforward Incode Mobile Onboarding implementation.

Prerequisites

It is first required to Setup Environment install the SDK into the project follow the details outlined in the Setup Environment

The minimum supported Android version is 21, which supports 99.6% of devices.

The minimum supported iOS version is iOS 13, which supports 99.6% of devices.

High level

The below diagram illustrates from a high level what happens during a straightforward integration.
A full example of the straightforwrd integration can be see at this github repo. Alternatively, view the recipe.

Step 1: Initialize the Incode SDK

Fast way SDK Initialize

Before the Incode SDK can be utilized, it must be initialized. Check the following code on how to initialize the Incode SDK on each platform.

public static void IncodeSDKInitialize(Application app) {
  try {

    new IncodeWelcome.Builder(app, Constants.API_URL, Constants.API_KEY)
      .setLoggingEnabled(true)
      .build();

    incodeSDKInitialized = true;
  } catch (Exception exception) {
    incodeSDKInitialized = false;
  }
}

private void setIncodeCommonConfig() {
  CommonConfig commonConfig = new com.incode.welcome_sdk.CommonConfig.Builder()
    .setShowExitConfirmation(true)
    .setShowCloseButton(true)
    .build();

  IncodeWelcome.getInstance().setCommonConfig(commonConfig);
}
func incodeSDKInitialize(api_url: String, api_key: String) {
  IncdOnboardingManager.shared.initIncdOnboarding(url: api_url,
                                                  apiKey: api_key,
                                                  loggingEnabled: true,
                                                  testMode: testMode) { (success, error) in
        print("IncdOnboarding SDK initialization, success: \(success == true), error: \(error?.description ?? "nil")")
        self.dispatchGroup.leave()
    }
}

func setIncodeCommonConfig() {
  IncdOnboardingManager.shared.allowUserToCancel = true
  IncdOnboardingManager.shared.idBackShownAsFrontCheck = true
  IncdOnboardingManager.shared.idFrontShownAsBackCheck = true
}
const IncodeSDKInitialize = (API_URL, API_KEY) => {
    IncodeSdk.initialize({
      testMode: false,
      apiConfig: {
        url: API_URL,
        key: API_KEY,
      },
      waitForTutorials: true,
    })
      .then((_) => {
        startStraightforwardOnboarding();
      })
      .catch((e) => {
        console.error('Incode SDK failed init', e);
      });
  };
void IncodeSDKInitialize(API_URL,API_KEY) {
	IncodeOnboardingSdk.init(
      apiKey: API_KEY,
      apiUrl: API_URL,
      onError: (String error) {
        print('Incode SDK init failed: $error');
      },
      onSuccess: () {
        onStartOnboarding();
      },
    );
}
// NOTE:
//
// To Initialize the SDK in Xamarin, you need to add the next code into the Native project
// that Xamarin creates.

// ANDROID
// **************************************************

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using Incode.Onboarding;

namespace IncodeOnboardingApplication.Droid
{
    [Activity(Label = "IncodeOnboardingApplication", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            // TODO - 1 - Set environment configuration
            var API_URL = "";
            var API_KEY = "";

            IncodeOnboarding.Current.Init(
                url: API_URL,
                apiKey: API_KEY,
                isLoggingEnabled: true,
                isTestMode: false
            );

            LoadApplication(new App());
        }
        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

// iOS
// **************************************************
using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using Incode.Onboarding;
using UIKit;

namespace IncodeOnboardingApplication.iOS
{
    // The UIApplicationDelegate for the application. This class is responsible for launching the 
    // User Interface of the application, as well as listening (and optionally responding) to 
    // application events from iOS.
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        //
        // This method is invoked when the application has loaded and is ready to run. In this 
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            // TODO - 1 - Set environment configuration
            var API_URL = "";
            var API_KEY = "";

            IncodeOnboarding.Current.Init(
                url: API_URL,
                apiKey:API_KEY,
                isLoggingEnabled: true,
                isTestMode: false
            );

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

function incodeSDKInitialize() {
             cordova.exec(function(param) {
             console.log("Success: "+param);
             // start the incode session here
             startSession();
             }, function(err) {
              console.log("Error: "+ err);
               callbackError('Nothing to echo.' +err);
           }, "Cplugin", "initializeSDK", [apiKey,apiUrl,loggingEnabled,testMode]); //apikey,apiurl,loggingenabled, last testmode should be false for production


   }
incodeSDKInitialize(): void {
    cordova.exec((param: any) => {
      console.log("Success: ", param);
      this.startSession();
    }, (err: any) => {
      console.log("Error:::: ", err);
    }, "Cplugin", "initializeSDK", [Constants.API_KEY, Constants.API_URL, "true", "false", "true"]);
  }

SDK Initialize with session created at backend

If you are creating the session in the backend, the API_URL needs to end with /0/ in order to let our bakcned knows how to manage the request using only the token of the session. You need to initialize the SDK in this way:

public static void IncodeSDKInitialize(Application app) {
  try {

    new IncodeWelcome.Builder(app, Constants.API_URL+"/0/")
      .setLoggingEnabled(true)
      .build();

    incodeSDKInitialized = true;
  } catch (Exception exception) {
    incodeSDKInitialized = false;
  }
}

private void setIncodeCommonConfig() {
  CommonConfig commonConfig = new com.incode.welcome_sdk.CommonConfig.Builder()
    .setShowExitConfirmation(true)
    .setShowCloseButton(true)
    .build();

  IncodeWelcome.getInstance().setCommonConfig(commonConfig);
}
func incodeSDKInitialize(api_url: String, api_key: String) {
  IncdOnboardingManager.shared.initIncdOnboarding(url: api_url+"/0/",
                                                  loggingEnabled: true,
                                                  testMode: testMode) { (success, error) in
        print("IncdOnboarding SDK initialization, success: \(success == true), error: \(error?.description ?? "nil")")
        self.dispatchGroup.leave()
    }
}

func setIncodeCommonConfig() {
  IncdOnboardingManager.shared.allowUserToCancel = true
  IncdOnboardingManager.shared.idBackShownAsFrontCheck = true
  IncdOnboardingManager.shared.idFrontShownAsBackCheck = true
}
const IncodeSDKInitialize = (API_URL, API_KEY) => {
    IncodeSdk.initialize({
      testMode: false,
      apiConfig: {
        url: API_URL+"/0/",
      },
      waitForTutorials: true,
    })
      .then((_) => {
        startStraightforwardOnboarding();
      })
      .catch((e) => {
        console.error('Incode SDK failed init', e);
      });
  };
void IncodeSDKInitialize(API_URL,API_KEY) {
	IncodeOnboardingSdk.init(
      apiUrl: API_URL+"/0/",
      onError: (String error) {
        print('Incode SDK init failed: $error');
      },
      onSuccess: () {
        onStartOnboarding();
      },
    );
}
// Not possible to do it in Xamarin for now
function incodeSDKInitialize() {
             cordova.exec(function(param) {
                console.log("SDK initializeSDKWithToken Success: "+param);
                startSession();
             }, function(err) {
                    console.log("Error initializeSDK: "+ err);
           }, "Cplugin", "initializeSDK", ["",apiUrlfortoken+"/0/",loggingEnabled,testMode]); //apikey,apiurl,loggingenabled, testmode should be false for production
   }
incodeSDKInitialize(): void {
    cordova.exec((param: any) => {
      console.log("Success: ", param);
      this.startSession();
    }, (err: any) => {
      console.log("Error:::: ", err);
    }, "Cplugin", "initializeSDK", ["", Constants.API_URL+"/0/", "true", "false", "true"]);
  }

Step 2: Create the session

Next, a session needs to be created in order for the data to be captured. This requires a Configuration ID, which is also known as a flow ID. To configure a flow, check out the getting started guide.

Fast way session creation

private SessionConfig getSimpleSession() {
        SessionConfig sessionConfig = new SessionConfig.Builder()
                .setRegionIsoCode("ALL")
                .setConfigurationId(Constants.CONFIGURATION_ID)
                .build();

        return sessionConfig;
    }
func createSessionConfiguration() -> IncdOnboardingSessionConfiguration {
  return IncdOnboardingSessionConfiguration(regionCode: "ALL",
                                            configurationId: "PASTE__HERE_CONFIGURATION_ID")
}
function getSimpleSession() {
    let sessionConfig: {
      region: 'ALL',
      configurationId: CONFIGURATION_ID,
    };

    return sessionConfig;
  }
OnboardingSessionConfiguration getSimpleSession() {
  // Common configs
  IncodeOnboardingSdk.showCloseButton(allowUserToCancel: true);
  
  // Session config
  OnboardingSessionConfiguration sessionConfig = OnboardingSessionConfiguration(configurationId: CONFIGURATION_ID);
  
	return sessionConfig;
}
var config = new OnboardingConfiguration()
                .AddRegion("ALL")
                .AddConfigurationId(CONFIGURATION_ID)
                .AllowUserToCancel(true);
function startSession(){
              cordova.exec(function(data) {
                       // Start the Incode moudles or sections from here
                       startOnboardingSection();                       
                       }, function(err) {
                        console.log(" startSession Error: "+ err);
                         // handle the error by showing some UI or alert.
                         callbackError('Nothing to echo.' +err);
                     }, "Cplugin", "setupOnboardingSession", [configId,null,null,null]);
      }
                     
startSession(): void {
    const configId = ''; 
    cordova.exec((data: { interviewId: any; token: any; }) => {
      console.log("OnSession created Success: ", data.interviewId , " Token:" , data.token);
      this.startOnboardingSection(); 
    }, (err: string) => {
      console.log(" startSession Error: ", err);
      this.callbackError(err);
    }, "Cplugin", "setupOnboardingSession", [Constants.CONFIGURATION_ID,null,null,null]);
  }

Setup a session created at backend

If you are creating the session in the backend, you need to set the session token in order to let the SDK save all the information captured during the onboarding to that session.

You need to create a way to share that token from your backend to your frontend, usually this is done by an additional API that you need to create. Once the frontend has the token (SESSION_TOKEN_FROM_BACKEND), you just need to pass it to the SDK. You can do that in this way:

private SessionConfig getSimpleSession() {
        SessionConfig sessionConfig = new SessionConfig.Builder()
                .setRegionIsoCode("ALL")
                .setExternalToken(SESSION_TOKEN_FROM_BACKEND)
                .build();

        return sessionConfig;
    }
func createSessionConfiguration() -> IncdOnboardingSessionConfiguration {
  return IncdOnboardingSessionConfiguration(regionCode: "ALL",
                                            token: SESSION_TOKEN_FROM_BACKEND)
}
function getSimpleSession() {
    let sessionConfig: {
      region: 'ALL',
      token: SESSION_TOKEN_FROM_BACKEND,
    };

    return sessionConfig;
  }
OnboardingSessionConfiguration getSimpleSession() {
  // Common configs
  IncodeOnboardingSdk.showCloseButton(allowUserToCancel: true);
  
  // Session config
  OnboardingSessionConfiguration sessionConfig = OnboardingSessionConfiguration(token: SESSION_TOKEN_FROM_BACKEND);
  
	return sessionConfig;
}
// Not possible to do it in Xamarin for now

function startSession(){

        cordova.exec(function(data) {
         // Start the Incode moudles or sections from here
         startOnboardingSection(); //userdefined
         }, function(err) {
          console.log("Error startSession: "+ err);
       }, "Cplugin", "setupOnboardingSession", [null,null,null,externalToken]);
}
                     
startSession(): void {
    const configId = ''; 
    cordova.exec((data: { interviewId: any; token: any; }) => {
      console.log("OnSession created Success: ", data.interviewId , " Token:" , data.token);
      this.startOnboardingSection(); 
    }, (err: string) => {
      console.log(" startSession Error: ", err);
      this.callbackError(err);
    }, "Cplugin", "setupOnboardingSession", [ null,null,null,Constants.TOKEN]);  //TOKEN CREATED IN BACKEND HERE
  }

Step 3: Configure the User Experience

The code snippet below creates an experience which the user scans their ID, the ID is processed, the Selfie is scanned, and finally face match is performed. This is the default onboarding experience and can be customized as needed.

private FlowConfig getStraightforwardFlowConfig() {
    FlowConfig flowConfig = null;
    try {
        flowConfig = new FlowConfig.Builder()
                .addID(new IdScan.Builder()
                        .setIdType(IdScan.IdType.ID)
                        .setShowIdTutorials(true)
                        .setWaitForTutorials(true)
                        .setEnableFrontShownAsBackCheck(true)
                        .setEnableBackShownAsFrontCheck(true)
                        .setIdCategory(IdCategory.FIRST)
                        .build())
                .addProcessId(new ProcessId.Builder()
                        .setIdCategory(IdCategory.FIRST)
                        .build()
                )
                .addSelfieScan(new SelfieScan.Builder()
                        .setShowTutorials(true)
                        .setWaitForTutorials(true)
                        .setMaskCheckEnabled(true)
                        .setLensesCheckEnabled(true)
                        .build()
                )
                .addFaceMatch(new FaceMatch.Builder()
                        .setShowUserExists(false)
                        .build()
                )
                .build();
    } catch (ModuleConfigurationException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return flowConfig;
}
func createFlowConfiguration() -> IncdOnboardingFlowConfiguration {
  let flowConfig = IncdOnboardingFlowConfiguration(waitForTutorials: true)
  flowConfig.addIdScan()
  flowConfig.addProcessId()
  flowConfig.addSelfieScan()
  flowConfig.addFaceMatch()
  return flowConfig
}
function getStraightforwardFlowConfig(){
    let flowConfig = [
      { module: 'IdScanFront', showTutorial: true, idType: 'id' },
      { module: 'IdScanBack', showTutorial: true, idType: 'id' },
      { module: 'ProcessId'},
      { module: 'SelfieScan', showTutorial: true, lensesCheck: false },
      { module: 'FaceMatch' },
    ];

    return flowConfig;
}
OnboardingFlowConfiguration getStraightforwardFlowConfig(){
 	OnboardingFlowConfiguration flowConfig = OnboardingFlowConfiguration();
  
  flowConfig.addIdScan(scanStep: ScanStepType.front, idType: IdType.id, showTutorials: true);
  flowConfig.addIdScan(scanStep: ScanStepType.back, idType: IdType.id, showTutorials: true);
  flowConfig.addProcessId();
  flowConfig.addSelfieScan(showTutorials: true);
  flowConfig.addFaceMatch();
  
  return flowConfig;
}
cofig.AddIdScan(new IdScanParams(showTutorials: true), result => ProcessIdScanCallback(result));
config.AddSelfieScan(new SelfieScanParams(showTutorials: true, lensesCheck: true, maskCheckEnabled:true, brightnessThreshold: null), result => ProcessSelfieScanCallback(result));
config.AddFaceMatch(new FaceMatchParams(), result => ProcessFaceMatchCallback(result));

function startOnboardingSection(){

              cordova.exec(function(winParam) {
                                    console.log("startOnboardingSection Success: ");
                                    finishOnboarding();//user defined
                                    }, function(err) {
                                     console.log("Error: "+ err);
                                     // handle the error by showing some UI or alert.
                                      callbackError('Nothing to echo.' +err);
                                  }, "Cplugin", "startOnboardingSection", [ 
                                  {"module":"addId","showTutorials":"true","waitForTutorials":"true",
                                    "idCategory":"FIRST","enableBackShownAsFrontCheck":"true",
                                    "enableFrontShownAsBackCheck":"true","autocaptureUXMode":"HOLDSTILL"},
                                  {"module":"addSelfieScan","showTutorials":"true","waitForTutorials":"true",
                                    "maskCheckEnabled":"true","lensesCheckEnabled":"true"},
                                  {"module":"addFaceMatch"}]);
}
                                  
startOnboardingSection(): void{
    console.log("startOnboardingSection");

    cordova.exec((winParam: any) => {
      console.log("startOnboardingSection Success: ");
      const myObj = JSON.stringify(winParam);
      console.log("myObj Success: ", myObj);
      const myObj1 = JSON.parse(myObj);
      console.log("data Success: ", myObj1.processIdData);

      this.finishOnboarding();
    }, (err: string) => {
      console.log("Error: ", err);
      this.callbackError(err);
    }, "Cplugin", "startOnboardingSection", [
      {"module":"addId","showTutorials":"false","waitForTutorials":"false","idCategory":"FIRST","enableBackShownAsFrontCheck":"true","enableFrontShownAsBackCheck":"true","autocaptureUXMode":"HOLDSTILL"},
      {"module":"addSelfieScan","showTutorials":"false","waitForTutorials":"false","maskCheckEnabled":"true"},
      {"module":"addFaceMatch"}]);
  }

Step 4: Implement the callbacks

During the user's journey to the flow, various callbacks are executed. Custom business logic can be added in these call backs, but showing a custom screen in between steps is not supported.

private IncodeWelcome.OnboardingListener getStraightforwardOnboardingListener() {
        IncodeWelcome.OnboardingListener onboardingListener = new IncodeWelcome.OnboardingListener() {
            @Override
            public void onOnboardingSessionCreated(String token, String interviewId, String region) {
                super.onOnboardingSessionCreated(token, interviewId, region);

                ((MainActivity) activity).interviewId = interviewId;
            }
           
            @Override
            public void onIdFrontCompleted(IdScanResult result) {
                super.onIdFrontCompleted(result);

                Timber.d("onIdFrontCompleted: %s", result);
            }

            @Override
            public void onIdBackCompleted(IdScanResult result) {
                super.onIdBackCompleted(result);

                Timber.d("onIdBackCompleted: %s", result);
            }

            @Override
            public void onIdProcessed(IdProcessResult result) {
                super.onIdProcessed(result);

                Timber.d("onIdProcessed: %s", result);
            }

            @Override
            public void onSelfieScanCompleted(SelfieScanResult result) {
                super.onSelfieScanCompleted(result);

                Timber.d("onSelfieScanCompleted: %s", result);
            }

            @Override
            public void onFaceMatchCompleted(FaceMatchResult result) {
                super.onFaceMatchCompleted(result);

                Timber.d("onFaceMatchCompleted: %s", result);

                executeFaceMatchValidationCheck(result);
            }
            
            @Override
            public void onSuccess() {
                super.onSuccess();

                Timber.d("onSuccess");

                fetchUserScores(interviewId);
            }

            @Override
            public void onError(Throwable error) {
                super.onError(error);

                Timber.d("onError: %s", error);
            }

            @Override
            public void onUserCancelled() {
                super.onUserCancelled();

                Timber.d("onUserCancelled");
            }
        };

        return onboardingListener;
    }
extension MainViewController: IncdOnboardingDelegate {
    
    func onOnboardingSessionCreated(_ result: OnboardingSessionResult) {
        print("onOnboardingSessionCreated, result: \(result)")
        if result.error != nil {
            let interviewId = result.interviewId
        }
    }
    
    func onIdFrontCompleted(_ result: IdScanResult) {
        print("front IdScanResult: \(result)")
    }
    
    func onIdBackCompleted(_ result: IdScanResult) {
        print("back IdScanResult: \(result)")
    }
    
    func onIdProcessed(_ result: IdProcessResult) {
        print("idProcessResult: \(result)")
    }
    
    func onSelfieScanCompleted(_ result: SelfieScanResult) {
        print("onSelfieScanCompleted, result: \(result)")
    }
    
    func onFaceMatchCompleted(_ result: FaceMatchResult) {
        print("onFaceMatchCompleted, result: \(result)")
    }
    
    func onSuccess() {
        print("onSuccess")
        
        fetchUsetScore()
    }
    
    func onError(_ error: IncdFlowError) {
        print("onError, result: \(error)")
    }
    
    func userCancelledSession() {
        print("userCancelledSession")
    }
}
const setupListeners = ({}) => {
  // returns a callback to unregister your listener, e.g. when your screen is getting unmounted
  IncodeSdk.onSessionCreated((session) => {
    let interviewID = session.interviewId
  });

  const complete = IncodeSdk.onStepCompleted;

  return [
    complete({
      module: 'IdScanFront',
      listener: (e)  => {
        console.log('IdScanFront result: ', e.result);
      },
    }),
    complete({
      module: 'IdScanBack',
      listener: (e) => {
        console.log('IdScanBack result: ', e.result);
      },
    }),
    complete({
      module: 'ProcessId',
      listener: (e) => {
        console.log('ProcessId result: ', e.result);
      },
    }),
    complete({
      module: 'SelfieScan',
      listener: (e) => {
        console.log('SelfieScan result:', e.result);
      },
    }),
    complete({
      module: 'FaceMatch',
      listener: (e) => {
        console.log('FaceMatch result: ', e.result);
      },
    }),
  ];
};

useEffect(() => {
    const unsubscribers = setupListeners({});
    // return a function unregistering all your listeners
    return () => {
      unsubscribers.forEach((unsubscriber) => unsubscriber());
    };
  }, []);

void onSuccessCallback(){
}

void onErrorCallback(String error){
}

void onUserCancelledCallback(){
}

void onIdFrontCompletedCallback(IdScanResult result) {
  print("ID Front");
  print(result);
}

void onIdBackCompletedCallback(IdScanResult result) {
  print("ID Back");
  print(result);
}

void onIdProcessedCallback(String result) {
  print("ID Process");
  print(result);
}

void onSelfieScanCompletedCallback(SelfieScanResult result) {
  print("Selfie Scan");
  print(result);
}

void onFaceMatchCompletedCallback(FaceMatchResult result) {
  print("Face Match");
  print(result);
}
private void ProcessIdScanCallback(IdValidationResult result)
{
  if (result != null)
  {
    if (result.Result == ResultCode.Success)
    {
      Debug.WriteLine("######## Id Scan Completed!");
      Debug.WriteLine($"######## Result => {result}");

      // TODO - Manage module success completion
      // TODO - Save/store relevant information for future use in the app
      // TODO - Save current module executed succesfully
      // TODO - Apply specific business logic
      // TODO - Save flag to be used to control de general onboarding flow

      Debug.WriteLine($"######## Id Scan Front Status => {result.FrontIdStatus}.");
      Debug.WriteLine($"######## Id Scan Back Status => {result.BackIdStatus}.");

      if(result.FrontIdStatus == IdValidationResult.ValidationStatus.Ok)
      {
        Debug.WriteLine($"####### Front Id Image in Base64 => {result.FrontId}.");
        Debug.WriteLine($"####### Front Id Cropped Face Image in Base64 => {result.FrontIdCroppedFace}.");
      }

      if (result.BackIdStatus == IdValidationResult.ValidationStatus.Ok)
      {
        Debug.WriteLine($"####### Back Id Image in Base64 => {result.BackId}.");
      }

      if(result.OcrData != null)
      {
        Debug.WriteLine($"######## Id Scan OcrData => {JsonConvert.SerializeObject(result.OcrData)}.");
        Debug.WriteLine($"######## Id Scan ExtendedOcrData {result.ExtendedOcrData}.");
      }

      if (result.FrontIdStatus == IdValidationResult.ValidationStatus.Ok &&
          result.BackIdStatus == IdValidationResult.ValidationStatus.Ok)
      {
        Debug.WriteLine("######## Id Scan Successful!");
      }
      else
      {
        Debug.WriteLine($"######## Id Scan Unsuccessful! due to: FrontIdStatus ({result.FrontIdStatus}) & BackIdStatus({result.BackIdStatus})");
      }
    }
    else if (result.Result == ResultCode.Error)
    {
      Debug.WriteLine($"######## Id Scan failed with Error => {result.ErrorMessage}.");

      // TODO - Manage module error
      // TODO - Save current module executed with failure
      // TODO - Apply specific business logic
      // TODO - Save flag to be used to control de general onboarding flow
    }
  }
}

private void ProcessSelfieScanCallback(SelfieScanResult result)
{
  if (result != null)
  {
    if (result.Result == ResultCode.Success)
    {
      Debug.WriteLine("######## Selfie Scan Completed!");
      Debug.WriteLine($"######## Result => {result}");

      // TODO - Manage module success completion
      // TODO - Save/store relevant information for future use in the app
      // TODO - Save current module executed succesfully
      // TODO - Apply specific business logic
      // TODO - Save flag to be used to control de general onboarding flow

      Debug.WriteLine($"####### Selfie Scan ScanStatus => {result.ScanStatus}.");


      if (result.ScanStatus == SelfieScanResult.Status.Ok)
      {
        Debug.WriteLine("######## Selfie Scan Successful!");
        Debug.WriteLine($"####### Selfie Image in Base64 => {result.SelfieImage}.");
        Debug.WriteLine($"####### Selfie Scan FaceMatched => {(result.FaceMatched.HasValue ? result.FaceMatched.Value ? "Yes" : "No" : "Null")}.");
        Debug.WriteLine($"####### Selfie Scan SpoofAttempt => {(result.SpoofAttempt.HasValue ? result.SpoofAttempt.Value ? "Yes" : "No" : "Null")}.");
      }
      else
      {
        Debug.WriteLine($"######## Selfie Scan Unsuccessful! due to: => {result.ScanStatus}");
      }
    }
    else if (result.Result == ResultCode.Error)
    {
      Debug.WriteLine($"######## Selfie Scan failed with Error => {result.ErrorMessage}.");

      // TODO - Manage module error
      // TODO - Save current module executed with failure
      // TODO - Apply specific business logic
      // TODO - Save flag to be used to control de general onboarding flow
    }
  }
}

private void ProcessFaceMatchCallback(FaceMatchResult result) {
  if (result != null){
    if (result.Result == ResultCode.Success){
      Debug.WriteLine("######## Face Match Completed!");
      Debug.WriteLine($"######## Result => {result}");

      // TODO - Manage module success completion
      // TODO - Save/store relevant information for future use in the app
      // TODO - Save current module executed succesfully
      // TODO - Apply specific business logic
      // TODO - Save flag to be used to control de general onboarding flow

      Debug.WriteLine($"######## Face Mached: {(result.FaceMatched.HasValue ? result.FaceMatched.Value ? "Yes" : "No" : "Null")}");

      if (result.FaceMatched == true)
      {
        if(result.ExistingUser == true)
        {
          Debug.WriteLine($"######## User found in this session: {result.ExistingInterviewId}");

          if (result.NameMatched == true)
          {
            // TODO - EVERYTHING OK - This is just a new user onboarding attempt
          }
          else
          {
            // TODO - WARNING - Manage fraud attempt
          }
        }
        else
        {
          // TODO - EVERYTHING OK - This is just a completly new user
        }
      }
      else
      {
        // TODO - WARNING - Manage fraud attempt
      }
    }
    else if (result.Result == ResultCode.Error)
    {
      Debug.WriteLine($"######## Face Match failed with Error => {result.ErrorMessage}.");

      // TODO - Manage module error
      // TODO - Save current module executed with failure
      // TODO - Apply specific business logic
      // TODO - Save flag to be used to control de general onboarding flow
    }
  }
}
// Not possible to do it in Cordova for now
// Not possible to do it in Cordova for now

Step 5: Execute the Onboarding

Once the SDK is initialized, flow is configured, session is created, and callbacks are implemented, then it is ready for the user to start the flow by calling the startOnboarding function.

private void startStraightforwardOnboarding(Activity activity) {
				setIncodeCommonConfig();
        SessionConfig sessionConfig = getSimpleSession();
        FlowConfig flowConfig = getStraightforwardFlowConfig();
        IncodeWelcome.OnboardingListener onboardingListener = getStraightforwardOnboardingListener();

        IncodeWelcome.getInstance().startOnboarding(activity, sessionConfig, flowConfig, onboardingListener);
    }
func startStraightforwardOnboarding() {
  setIncodeCommonConfig()
  let sessionConfig = createSessionConfiguration()
  let flowConfig = createFlowConfiguration()
  IncdOnboardingManager.shared.presentingViewController = self
  IncdOnboardingManager.shared.startOnboarding(sessionConfig: sessionConfig, flowConfig: flowConfig, delegate: self)
}
function startStraightforwardOnboarding(){
    let sessionConfig = getSimpleSession();
    let flowConfig = getStraightforwardFlowConfig();

    IncodeSdk.startOnboarding({
      flowConfig: flowConfig,
      sessionConfig: sessionConfig,
    })
      .then((result) => {
        fetchUserScores();
      })
      .catch((e) => {
        // TODO - Manage Onboarding error
      });
  }
void startStraightforwardOnboarding(){
  var sessionConfig = getSimpleSession();
  var flowConfig = getStraightforwardFlowConfig();
  
  IncodeOnboardingSdk.startOnboarding(
    sessionConfig: sessionConfig,
    flowConfig: flowConfig,
    onSuccess: onSuccessCallback,
    onError: onErrorCallback,
    onUserCancelled: onUserCancelledCallback,
    onIdFrontCompleted: onIdFrontCompletedCallback,
    onIdBackCompleted: onIdBackCompletedCallback,
    onIdProcessed: onIdProcessedCallback,
    onSelfieScanCompleted: onSelfieScanCompletedCallback,
    onFaceMatchCompleted: onFaceMatchCompletedCallback
  );		
}
IncodeOnboarding.Current.StartOnboarding(config);
document.getElementById("element").addEventListener("click", incodeSDKInitialize);
this.incodeSDKInitialize()

Step 6: Process Results

Last but not least, when the user completes the flow, fetch the results and implement the business logic as needed to JUST redirect the user to the screen you need.

They recommended place to apply the business logic to evaluate the onboarding status and gran access to the product you offer is in the backend, please review this explanation of fetch scores in a deep to achive that.

private void fetchUserScores(String interviewId) {
  IncodeWelcome.getInstance().getUserScore(FAST, interviewId, new GetUserScoreListener() {
    @Override
    public void onUserScoreFetched(UserScoreResult result) {
      Timber.d("onUserScoreFetched: %s", result);

      validateResultWithBusinessLogic(result);
    }

    @Override
    public void onUserCancelled() {
      Timber.d("getUserScore onUserCancelled");
    }

    @Override
    public void onError(Throwable error) {
      Timber.d("getUserScore onError: %s", error);
    }
  });
}

private void validateResultWithBusinessLogic(UserScoreResult result) {
  // TODO - Apply business rules to check the scores of the session
}
func fetchUserScore() {
	IncdOnboardingManager.shared.getUserScore(userScoreFetchMode: UserScoreFetchMode.fast, interviewId: interviewId ,completion: {
  		userScore in
      if(userScore.error != nil) {
      		validateResultWithBusinessLogic()
       }
       else {
         // TODO - Manage fetch user score error
       }
    })
}
    
func validateResultWithBusinessLogic(userScore:UserScore) {
	// TODO - Apply business rules to check the scores of the session
 }
function fetchUserScores(interviewId) {
  IncodeSdk.getUserScore({ mode: 'fast' })
    .then((result) => {
    validateResultWithBusinessLogic(result);
  })
    .catch((e) => {
    // TODO - Manage getting User Score error
  });
}

function validateResultWithBusinessLogic(result){
  // TODO - Apply business rules to check the scores of the session
}
void fetchUserScores() {
  IncodeOnboardingSdk.getUserScore(
    onSuccess: (result) {
  	  validateResultWithBusinessLogic(result);
	  },
    onError: (error) {
			// TODO - Manage getting User Score error
    }
  );
}

void validateResultWithBusinessLogic(result){
}


//To fetch scores or results once onboarding modules are done.

function getUserScore(){
             cordova.exec(function(winParam) {
                 console.log("showUserScore Success: "+winParam);
                 myObj = JSON.stringify(winParam);
                 console.log("Score : "+myObj);
                 deleteLocalUserData();
                }, function(err) {
                  console.log("Error: "+ err);
                callbackError('Nothing to echo.' +err);
           }, "Cplugin", "getUserScore", ["fast"]);
        }



//To finish the session below code is mandatory after all sections or modules are completed successfully. This should be called only once when all the sections are done.

function finishOnboarding(){
       cordova.exec(function(winParam) {
           console.log("finishOnboarding Success: "+winParam);
           getUserScore();
          }, function(err) {
            console.log("Error: "+ err);
          callbackError('Nothing to echo.' +err);
     }, "Cplugin", "finishOnboarding", []);
  }




//Make sure to delete local cache data once all all the above steps are over.

function deleteLocalUserData(){
          cordova.exec(function(winParam) {
               console.log("deleteLocalUserData Success: "+winParam);
             }, function(err) {
               console.log("Error: "+ err);
        }, "Cplugin", "deleteUserLocalData", []);
     }
//To finish the session below code is mandatory after all sections or modules are completed successfully. This should be called only once when all the sections are done.

finishOnboarding(): void {
    console.log("finishOnboarding");
    cordova.exec((winParam: any) => {
      console.log("finishOnboarding Success: ", winParam);
      this.getUserScore();
    }, (err: string) => {
      console.log("Error: ", err);
      this.callbackError(err);
    }, "Cplugin", "finishOnboarding", []);
  }

  //To fetch scores or results once onboarding modules are done.
  getUserScore(): void {
    console.log("getUserScore");
    cordova.exec((winParam: any) => {
      console.log("getUserScore Success: ", winParam);
      this.deleteLocalUserData();
    }, (err: string) => {
      console.log("Error: ", err);
      this.callbackError(err);
    }, "Cplugin", "getUserScore", ["fast"]);
  }

//Make sure to delete local cache data once all all the above steps are over.

  deleteLocalUserData(): void {
    console.log("deleteUserLocalData");
    cordova.exec((winParam: any) => {
      console.log("deleteLocalUserData Success: ", winParam);
      this.navigateTo();
    }, (err: string) => {
      console.log("Error: ", err);
    }, "Cplugin", "deleteUserLocalData", []);
  }

Note: It's very important to understand that you can manage your App UI navigation or API calls in TODO section (validateResultWithBusinessLogic method), but by safety and best practices, the business rules must be applied in your backend, for example user's onboarding approve, change status, etc.

Working example project

Once you setup your environment to integrate Incode and once you reaf the explanation about the general steps to integrate the SDK into the different availble flavors, we have a working examples on each platform that can be use as a playground to test the Incode modules available