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.

Step 1: Initialize the Incode SDK

Fast way to initialize the SDK

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.

fun incodeSDKInitialize(app: Application) {
    try {
        IncodeWelcome.Builder(app, Constants.API_URL, Constants.API_KEY)
            .setLoggingEnabled(true)
            .build()

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

private fun setIncodeCommonConfig() {
    val commonConfig: CommonConfig = CommonConfig.Builder()
        .setShowExitConfirmation(true)
        .setShowCloseButton(true)
        .build()

    IncodeWelcome.getInstance().setCommonConfig(commonConfig)
}
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 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,
        testMode: false,
        onError: (String error) {
          print('Incode SDK init failed: $error');
        },
        onSuccess: () {
          print('Incode SDK init success:');
        },
    );
  }

Initialize the SDK with session created on the backend

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

fun incodeSDKInitialize(app: Application?) {
    try {
        IncodeWelcome.Builder(app!!, Constants.API_URL + "/0/")
            .setLoggingEnabled(true)
            .build()

        incodeSDKInitialized = true
    } catch (exception: java.lang.Exception) {
        incodeSDKInitialized = false
    }
}

private fun setIncodeCommonConfig() {
    val commonConfig: CommonConfig = CommonConfig.Builder()
        .setShowExitConfirmation(true)
        .setShowCloseButton(true)
        .build()

    IncodeWelcome.getInstance().setCommonConfig(commonConfig)
}
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 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();
      },
    );
}

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 to create a session

private fun getSimpleWorkflowSession(): SessionConfig {
    val sessionConfig: SessionConfig = SessionConfig.Builder()
        .setConfigurationId(Constants.WORKFLOW_ID)
        .build()

    return sessionConfig
}
private SessionConfig getSimpleWorkflowSession() {
    SessionConfig sessionConfig = new SessionConfig.Builder()
        .setConfigurationId(Constants.WORKFLOW_ID)
        .build();

    return sessionConfig;
}
func getSimpleWorkflowSession() -> IncdOnboardingSessionConfiguration {
  return IncdOnboardingSessionConfiguration(regionCode: "ALL",
                                            configurationId: "PASTE_HERE_WORKFLOW_ID")
}
function getSimpleWorkflowSession() {
    let sessionConfig: {
      region: 'ALL',
      configurationId: WORKFLOW_ID,
    };

    return sessionConfig;
  }
OnboardingSessionConfiguration getSimpleWorkflowSession() {
    IncodeOnboardingSdk.showCloseButton(allowUserToCancel: true); 
    OnboardingSessionConfiguration sessionConfig = OnboardingSessionConfiguration(configurationId: WORKFLOW_ID);
    return sessionConfig;
  }

Setup a session created on the 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 fun getSimpleWorkflowSession(): SessionConfig? {
    val sessionConfig: SessionConfig = SessionConfig.Builder()
        .setExternalToken(SESSION_TOKEN_FROM_BACKEND)
        .build()

    return sessionConfig
}
private SessionConfig getSimpleWorkflowSession() {
    SessionConfig sessionConfig = new SessionConfig.Builder()
        .setExternalToken(SESSION_TOKEN_FROM_BACKEND)
        .build();

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

    return sessionConfig;
  }
OnboardingSessionConfiguration getSimpleWorkflowSession() {
    IncodeOnboardingSdk.showCloseButton(allowUserToCancel: true); 
    OnboardingSessionConfiguration sessionConfig = OnboardingSessionConfiguration(token: SESSION_TOKEN_FROM_BACKEND);
    return sessionConfig;
  }

Step 3: Execute the Workflow

private fun startWorkflow() {
    setIncodeCommonConfig()
    val sessionConfig: SessionConfig = getSimpleWorkflowSession()
    val onboardingListener: OnboardingListener = getWorkflowOnboardingListener()
    IncodeWelcome.getInstance().startWorkflow(activity, sessionConfig, onboardingListener)
}
private void startWorkflow() {
    setIncodeCommonConfig();
    SessionConfig sessionConfig = getSimpleWorkflowSession();
    IncodeWelcome.OnboardingListener onboardingListener = getWorkflowOnboardingListener();
    IncodeWelcome.getInstance().startWorkflow(activity, sessionConfig, onboardingListener);
}
func startWorkflow() {
  setIncodeCommonConfig()
  let sessionConfig = getSimpleWorkflowSession();
  IncdOnboardingManager.shared.presentingViewController = self
  IncdOnboardingManager.shared.startWorkflow(sessionConfig: sessionConfig, delegate: self)
}
function startWorkflow(){
    let sessionConfig = getSimpleWorkflowSession();

    IncodeSdk.startWorkflow({
      sessionConfig: sessionConfig,
    })
      .then((result) => {
        fetchUserScores();
      })
      .catch((e) => {
        // TODO - Manage Onboarding error
      });
  }
void startWorkflow(){
    var sessionConfig = getSimpleWorkflowSession();
    
    IncodeOnboardingSdk.startWorkflow(
      sessionConfig: sessionConfig, 
      onSuccess: onSuccessCallback,
      onError: onErrorCallback,
      onUserCancelled: onUserCancelledCallback,
      onIdFrontCompleted: onIdFrontCompletedCallback,
      onIdBackCompleted: onIdBackCompletedCallback,
      onIdProcessed: onIdProcessedCallback,
      onSelfieScanCompleted: onSelfieScanCompletedCallback,
      onFaceMatchCompleted: onFaceMatchCompletedCallback,
      );
  }

Step 4: 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 achieve that.

private fun fetchUserScores(interviewId: String) {
    IncodeWelcome.getInstance().getUserScore(FAST, interviewId, object : GetUserScoreListener {
        override fun onUserScoreFetched(result: UserScoreResult) {
            Timber.d("onUserScoreFetched: %s", result)

            validateResultWithBusinessLogic(result)
        }

        override fun onUserCancelled() {
            Timber.d("getUserScore onUserCancelled")
        }

        override fun onError(error: Throwable) {
            Timber.d("getUserScore onError: %s", error)
        }
    })
}

private fun validateResultWithBusinessLogic(result: UserScoreResult) {
    // TODO - Apply business rules to check the scores of the session
}
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){
}

Advanced: 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 fun getSimpleWorkflowOnboardingListener(): OnboardingListener {
    val onboardingListener: OnboardingListener = object : OnboardingListener() {
        override fun onOnboardingSessionCreated(
            token: String,
            interviewId: String,
            region: String
        ) {
            super.onOnboardingSessionCreated(token, interviewId, region)

            (activity as MainActivity).interviewId = interviewId
        }

        override fun onIdFrontCompleted(result: IdScanResult) {
            super.onIdFrontCompleted(result)

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

        override fun onIdBackCompleted(result: IdScanResult) {
            super.onIdBackCompleted(result)

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

        override fun onIdProcessed(result: IdProcessResult) {
            super.onIdProcessed(result)

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

        override fun onSelfieScanCompleted(result: SelfieScanResult) {
            super.onSelfieScanCompleted(result)

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

        override fun onFaceMatchCompleted(result: FaceMatchResult) {
            super.onFaceMatchCompleted(result)

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

            executeFaceMatchValidationCheck(result)
        }

        override fun onSuccess() {
            super.onSuccess()

            Timber.d("onSuccess")

            fetchUserScores(interviewId)
        }

        override fun onError(error: Throwable) {
            super.onError(error)

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

        override fun onUserCancelled() {
            super.onUserCancelled()

            Timber.d("onUserCancelled")
        }
    }

    return onboardingListener
}
private IncodeWelcome.OnboardingListener getSimpleWorkflowOnboardingListener() {
    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);
}

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's modules available