SDK reference · Android SDK / Android Common Implementation Patterns

Configure Flows Locally and Run Step by Step

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 the CONFERENCE or RESULTS modules. 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() {
    }
});

Was this page helpful?