This Android SDK integration pattern gives you full control over the onboarding experience. Instead of passing a single flow to the SDK and waiting for it to finish, you create an onboarding session, split the flow into sections, and run each section with startOnboardingSection. Control returns to your app between sections, so you can show your own screens, make decisions, or call your back end before continuing.
Use this pattern when you need to insert your own UI between capture steps, group modules into logical sections, or branch based on intermediate results. If you prefer to define the flow in Dashboard instead, see Run Flows Configured in Dashboard. If you want full control over the flow in client code, see Configure Flows Locally and Run End to End. For more information about these patterns, see Common Integration Approaches.
This page covers session creation and section execution only. Ensure you have followed the steps to Install and Initialize the Incode Android SDK first.
Set Up This Integration Pattern
Complete the following steps in order.
Create a New Onboarding Session
Before calling any other Onboarding SDK components, create a new onboarding session with setupOnboardingSession and an OnboardingSessionListener:
val sessionConfig: SessionConfig = SessionConfig.Builder().build()
IncodeWelcome.getInstance().setupOnboardingSession(
sessionConfig,
object : OnboardingSessionListener {
override fun onOnboardingSessionCreated(
token: String?,
interviewId: String?,
region: String
) {
// Onboarding Session Created successfully
}
override fun onError(error: Throwable) {}
override fun onUserCancelled() {}
})
SessionConfig sessionConfig = new SessionConfig.Builder().build();
IncodeWelcome.getInstance().setupOnboardingSession(
sessionConfig,
new OnboardingSessionListener() {
@Override
public void onOnboardingSessionCreated(@Nullable String token, @Nullable String interviewId, @NonNull String region) {
// Onboarding Session Created successfully
}
@Override
public void onError(@NonNull Throwable error) {
}
@Override
public void onUserCancelled() {
}
});
You can optionally specify a list of OnboardingValidationModule items. This list determines which modules are used for verification and calculation of the onboarding score. If you pass null as the validationModuleList, the default values are used: id, faceRecognition, and liveness.
val validationModuleList = listOf(
OnboardingValidationModule.id,
OnboardingValidationModule.secondId,
OnboardingValidationModule.faceRecognition,
OnboardingValidationModule.liveness,
OnboardingValidationModule.faceRecognitionSecondId,
OnboardingValidationModule.governmentValidation,
OnboardingValidationModule.governmentOcrValidation,
OnboardingValidationModule.governmentFaceValidation,
OnboardingValidationModule.videoSelfie,
OnboardingValidationModule.faceMask
)
val sessionConfig: SessionConfig = SessionConfig.Builder()
...
.setValidationModuleList(validationModuleList)
.build()
List<OnboardingValidationModule> validationModuleList = new ArrayList<>();
validationModuleList.add(OnboardingValidationModule.id);
validationModuleList.add(OnboardingValidationModule.secondId);
validationModuleList.add(OnboardingValidationModule.faceRecognition);
validationModuleList.add(OnboardingValidationModule.liveness);
validationModuleList.add(OnboardingValidationModule.faceRecognitionSecondId);
validationModuleList.add(OnboardingValidationModule.governmentValidation);
validationModuleList.add(OnboardingValidationModule.governmentOcrValidation);
validationModuleList.add(OnboardingValidationModule.governmentFaceValidation);
validationModuleList.add(OnboardingValidationModule.videoSelfie);
validationModuleList.add(OnboardingValidationModule.faceMask);
SessionConfig sessionConfig = new SessionConfig.Builder()
...
.setValidationModuleList(validationModuleList)
.build();
You can also determine validation modules based on a specific Flow by passing a flow configuration ID.
Create a Callback to Receive SDK Results
Implement an OnboardingListener to receive results from each module as sections run:
val onboardingListener: OnboardingListener = object : OnboardingListener() {
override fun onOnboardingSessionCreated(token: String?, interviewId: String?, region: String) {
// Onboarding Session Created successfully
}
override fun onIntroCompleted() {
// Intro screen completed
}
override fun onAddPhoneCompleted(phoneNumberResult: PhoneNumberResult) {
// Add phone completed
}
override fun onQRScanCompleted(qrScanResult: QRScanResult) {
// QR scan completed
}
override fun onIdFrontCompleted(frontIdScanResult: IdScanResult) {
// Scanning the front of an ID completed
}
override fun onIdBackCompleted(backIdScanResult: IdScanResult) {
// Scanning the back of an ID completed
}
override fun onIdProcessed(idProcessResult: IdProcessResult) {
// ID Validation completed
}
override fun onNfcScanCompleted(nfcScanResult: NfcScanResult) {
// NFC scan completed
}
override fun onDocumentValidationCompleted(
documentType: DocumentType,
result: DocumentValidationResult
) {
// Document validation completed
}
override fun onSelfieScanCompleted(selfieScanResult: SelfieScanResult) {
// Selfie scan completed
}
override fun onFaceMatchCompleted(faceMatchResult: FaceMatchResult) {
// Face match completed
}
override fun onSignatureCollected(signatureFormResult: SignatureFormResult) {
// Signature collected
}
override fun onUserConsentCompleted() {
// User consent complete
}
override fun onVideoRecorded(videoSelfieResult: VideoSelfieResult) {
// Video selfie finished successfully
}
override fun onCaptchaCollected(captchaResult: CaptchaResult) {
// Captcha collected
}
override fun onGeolocationFetched(geolocationResult: GeolocationResult) {
// Geolocation collected
}
override fun onApproveCompleted(approveResult: ApproveResult) {
// User's onboarding approval completed
}
override fun onResultsShown(userScoreResult: UserScoreResult) {
// Results shown to the user
}
override fun onQueuePositionChanged(newQueuePosition: Int) {
// Queue position for the video call changed
}
override fun onEstimatedWaitingTime(waitingTimeInSeconds: Int) {
// Called only once with the estimated waiting time in the queue. Waiting time is in seconds.
}
override fun onConferenceEnded() {
// Called when the video conference has ended
}
override fun onSuccess() {
// User successfully finished whole onboarding flow
}
override fun onError(error: Throwable) {
// Onboarding flow was aborted due to error
}
override fun onUserCancelled() {
// User cancelled the flow
}
}
OnboardingListener onboardingListener = new IncodeWelcome.OnboardingListener() {
@Override
public void onOnboardingSessionCreated(@Nullable String token, @Nullable String interviewId, @NonNull String region) {
// Onboarding Session Created successfully
}
@Override
public void onIntroCompleted() {
// Intro screen completed
}
@Override
public void onAddPhoneCompleted(@NonNull PhoneNumberResult phoneNumberResult) {
// Add phone completed
}
@Override
public void onQRScanCompleted(@NonNull QRScanResult qrScanResult) {
// QR scan completed
}
@Override
public void onIdFrontCompleted(@NonNull IdScanResult frontIdScanResult) {
// Scanning the front of an ID completed
}
@Override
public void onIdBackCompleted(@NonNull IdScanResult backIdScanResult) {
// Scanning the back of an ID completed
}
@Override
public void onIdProcessed(@NonNull IdProcessResult idProcessResult) {
// ID Validation completed
}
@Override
public void onNfcScanCompleted(@NonNull NfcScanResult nfcScanResult) {
// NFC scan completed
}
@Override
public void onDocumentValidationCompleted(@NonNull DocumentType documentType, @NonNull DocumentValidationResult result) {
// Document validation completed
}
@Override
public void onSelfieScanCompleted(@NonNull SelfieScanResult selfieScanResult) {
// Selfie scan completed
}
@Override
public void onFaceMatchCompleted(@NonNull FaceMatchResult faceMatchResult) {
// Face match completed
}
@Override
public void onSignatureCollected(@NonNull SignatureFormResult signatureFormResult) {
// Signature collected
}
@Override
public void onUserConsentCompleted() {
// User consent complete
}
@Override
public void onVideoRecorded(@NonNull VideoSelfieResult videoSelfieResult) {
// Video selfie finished successfully
}
@Override
public void onCaptchaCollected(@NonNull CaptchaResult captchaResult) {
// Captcha collected
}
@Override
public void onGeolocationFetched(@NonNull GeolocationResult geolocationResult) {
// Geolocation collected
}
@Override
public void onApproveCompleted(@NonNull ApproveResult approveResult) {
// User's onboarding approval completed
}
@Override
public void onResultsShown(@NonNull UserScoreResult userScoreResult) {
// Results shown to the user
}
@Override
public void onQueuePositionChanged(int newQueuePosition) {
// Queue position for the video call changed
}
@Override
public void onEstimatedWaitingTime(int waitingTimeInSeconds) {
// Called only once with the estimated waiting time in the queue. Waiting time is in seconds.
}
@Override
public void onConferenceEnded() {
// Called when the video conference has ended
}
@Override
public void onSuccess() {
// User successfully finished whole onboarding flow
}
@Override
public void onError(@NonNull Throwable error) {
// Onboarding flow was aborted due to error
}
@Override
public void onUserCancelled() {
// User cancelled the flow
}
};
For the shape and fields of each result object, see Results.
The SDK has multiple exit paths. Call IncodeWelcome.deleteUserLocalData(context) after the flow ends to delete all local user data. This is a static method that takes a Context. Call deleteUserLocalData() in these callbacks:
fun onSuccess()
fun onError(error: Throwable)
fun onUserCancelled()
public void onSuccess()
public void onError(Throwable error)
public void onUserCancelled()
Split the Flow into Sections
After the session is created, split the flow into sections based on your needs:
- Call
setFlowTag(String)for each section. - Call
IncodeWelcome.getInstance().finishOnboarding()at the end of the flow, before theCONFERENCEorRESULTSmodules. This marks the end of the flow and closes the session on the server.
Create an Onboarding Section
Build a FlowConfig for the section, tag it with setFlowTag, add the modules it should run, and start it with startOnboardingSection. After all sections are complete, call finishOnboarding:
// Create section
val flowConfig: FlowConfig = FlowConfig.Builder()
.setFlowTag("section 1") // Make sure to tag your flow section
.addIntro(Intro.Builder().build())
.addPhone()
.addID()
.build()
// Start section
IncodeWelcome.getInstance().startOnboardingSection(
activityContext,
flowConfig,
onboardingListener
)
// Call when all finished
IncodeWelcome.getInstance().finishOnboarding(activityContext, object : FinishOnboardingListener {
override fun onOnboardingFinished() {
IncodeWelcome.deleteUserLocalData(activityContext) // recommended to delete local user data at this point
}
...
})
// Create section
FlowConfig flowConfig = new FlowConfig.Builder()
.setFlowTag("section 1") // Make sure to tag your flow section
.addIntro(new Intro.Builder().build())
.addPhone()
.addID()
.build();
// Start section
IncodeWelcome.getInstance().startOnboardingSection(
activityContext,
flowConfig,
onboardingListener);
// Call when all finished
IncodeWelcome.getInstance().finishOnboarding(activityContext, new FinishOnboardingListener() {
@Override
public void onOnboardingFinished() {
IncodeWelcome.deleteUserLocalData(activityContext); // recommended to delete local user data at this point
}
...
});
For the full list of modules you can add to a section and how to configure each one, see the Individual Modules catalog.
Section Flow Requirements
Receive the "Section Complete" Callback
OnboardingListener contains a callback for the section completed event, onOnboardingSectionCompleted().
Danger
Warning
Start subsequent sections only fromonOnboardingSectionCompleted(). Starting a section from a module callback such as onIdFrontCompleted()) does not work correctly.
override fun onOnboardingSectionCompleted(flowTag: String) {
...
startNextSection() // This will not work properly in module callback methods (example: `onIdFrontCompleted()`)
// Use `onOnboardingSectionCompleted()` if you need to start other sections from the `Listener`
}
fun startNextSection() {
val nextFlowConfig: FlowConfig = FlowConfig.Builder()
.setFlowTag("NEXT_SECTION_FLOW_TAG") // add modules
...
.build()
IncodeWelcome.getInstance().startOnboardingSection(
activityContext,
nextFlowConfig,
this
)
}
@Override
public void onOnboardingSectionCompleted(@NonNull String flowTag) {
...
startNextSection(); // This will not work properly in module callback methods (example: `onIdFrontCompleted()`)
// Use `onOnboardingSectionCompleted()` if you need to start other sections from the `Listener`
}
@Override
public void onIdFrontCompleted(@NonNull IdScanResult frontIdScanResult) {
...
// startNextSection(); // DON'T DO THIS HERE! Do it in `onOnboardingSectionCompleted()` instead!
}
public void startNextSection() {
FlowConfig nextFlowConfig = new FlowConfig.Builder()
.setFlowTag("NEXT_SECTION_FLOW_TAG")
// add modules
...
.build();
IncodeWelcome.getInstance().startOnboardingSection(
activityContext,
nextFlowConfig,
onboardingListener);
}
The module callbacks such as onIdFrontCompleted() deliver results as each module finishes. For the result and error objects each callback returns, see Results.
Resume an Existing Onboarding Session
To resume an existing session after the app was uninstalled and reinstalled mid-flow, call setupOnboardingSession before any other API calls. This resets the configuration that was lost during uninstall.
Create a SessionConfig instance with the existing interviewId. You can also set the validationModuleList:
val validationModuleList = listOf(
OnboardingValidationModule.id,
OnboardingValidationModule.faceRecognition,
OnboardingValidationModule.liveness,
OnboardingValidationModule.governmentValidation
)
val sessionConfig: SessionConfig = SessionConfig.Builder()
.setInterviewId(interviewId) // Set interviewId
.setValidationModuleList(validationModuleList) // Set validationModuleList (optional)
.build()
IncodeWelcome.getInstance().setupOnboardingSession(sessionConfig, object : OnboardingSessionListener {
override fun onOnboardingSessionCreated(
token: String?,
interviewId: String?,
region: String
) {
// it's safe to call individual APIs again
}
override fun onError(throwable: Throwable) {}
override fun onUserCancelled() {}
})
List<OnboardingValidationModule> validationModuleList = new ArrayList<>();
validationModuleList.add(OnboardingValidationModule.id);
validationModuleList.add(OnboardingValidationModule.faceRecognition);
validationModuleList.add(OnboardingValidationModule.liveness);
validationModuleList.add(OnboardingValidationModule.governmentValidation);
SessionConfig sessionConfig = new SessionConfig.Builder()
.setInterviewId(interviewId) // Set interviewId
.setValidationModuleList(validationModuleList) // Set validationModuleList (optional)
.build();
IncodeWelcome.getInstance().setupOnboardingSession(sessionConfig, new OnboardingSessionListener() {
@Override
public void onOnboardingSessionCreated(@Nullable String token, @Nullable String interviewId, @NonNull String region) {
// it's safe to call individual APIs again
}
@Override
public void onError(@NonNull Throwable throwable) {
}
@Override
public void onUserCancelled() {
}
});