The following callbacks are available:

On SDK state changes

Use withHandlers(onStateChanged=onSDKStateChangedHandler) or .withStateChangedHandler(stateChangedHandler) in the SDK builder to get notified about changes in the flow of the verification process.

The callback takes two parameters:

  • newState is the current SDK State.
  • prevState is the previous state value.

The following example lists all the possible states:

val onSDKStateChangedHandler: (SNSSDKState, SNSSDKState) -> Unit = { newState, prevState ->
    Timber.d("onSDKStateChangedHandler: $prevState -> $newState")

    when (newState) {
        is SNSSDKState.Ready -> Timber.d("SDK is ready")
        is SNSSDKState.Failed -> {
            when (newState) {
                is SNSSDKState.Failed.ApplicantNotFound -> Timber.e(newState.message)
                is SNSSDKState.Failed.ApplicantMisconfigured -> Timber.e(newState.message)
                is SNSSDKState.Failed.InitialLoadingFailed -> Timber.e(newState.exception,"Initial loading error")
                is SNSSDKState.Failed.InvalidParameters -> Timber.e(newState.message)
                is SNSSDKState.Failed.NetworkError -> Timber.e(newState.exception,newState.message)
                is SNSSDKState.Failed.Unauthorized -> Timber.e(newState.exception,"Invalid token or a token can't be refreshed by the SDK. Please, check your token expiration handler")
                is SNSSDKState.Failed.Unknown -> Timber.e(newState.exception, "Unknown error")
        is SNSSDKState.Initial -> Timber.d("No verification steps are passed yet")
        is SNSSDKState.Incomplete -> Timber.d("Some but not all verification steps are passed over")
        is SNSSDKState.Pending -> Timber.d("Verification is in pending state")
        is SNSSDKState.FinallyRejected -> Timber.d("Applicant has been finally rejected")
        is SNSSDKState.TemporarilyDeclined -> Timber.d("Applicant has been declined temporarily")
        is SNSSDKState.Approved -> Timber.d("Applicant has been approved")

val snsSdkBuilder = SNSMobileSDK.Builder(this).withHandlers(onStateChanged = onSDKStateChangedHandler)
SNSStateChangedHandler stateChangedHandler = (previousState, currentState) -> {

    Timber.d("The SDK state was changed: " + previousState + " -> " + currentState);

    if (currentState instanceof SNSSDKState.Ready) {
        Timber.d("SDK is ready");
    } else if (currentState instanceof SNSSDKState.Failed.Unauthorized) {
        Timber.e(((SNSSDKState.Failed.Unauthorized) currentState).getException(), "Invalid token or a token can't be refreshed by the SDK. Please, check your token expiration handler");
    } else if (currentState instanceof SNSSDKState.Failed.Unknown) {
        Timber.e(((SNSSDKState.Failed.Unknown) currentState).getException(), "Unknown error");
    } else if (currentState instanceof SNSSDKState.Initial) {
        Timber.d("No verification steps are passed yet");
    } else if (currentState instanceof SNSSDKState.Incomplete) {
        Timber.d("Some but not all verification steps are passed over");
    } else if (currentState instanceof SNSSDKState.Pending) {
        Timber.d("Verification is in pending state");
    } else if (currentState instanceof SNSSDKState.FinallyRejected) {
        Timber.d("Applicant has been finally rejected");
    } else if (currentState instanceof SNSSDKState.TemporarilyDeclined) {
        Timber.d("Applicant has been declined temporarily");
    } else if (currentState instanceof SNSSDKState.Approved) {
        Timber.d("Applicant has been approved");

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity()).withStateChangedHandler(stateChangedHandler).build();

On SDK errors

Use withHandlers(onError=onSDKErrorHandler) in the SDK builder to know about errors that occur within the SDK.

Refer to the example below to see how.

val onSDKErrorHandler: (SNSException) -> Unit = { exception ->
    Timber.d("The SDK throws an exception. Exception: $exception")

    when (exception) {
        is SNSException.Api -> Timber.d("Api exception. ${exception.description}")
        is SNSException.Network -> Timber.d(exception, "Network exception.")
        is SNSException.Unknown -> Timber.d(exception, "Unknown exception.")

val snsSdkBuilder = SNSMobileSDK.Builder(this).withHandlers(onError = onSDKErrorHandler)

SNSErrorHandler errorHandler = e -> {
    Timber.d("The SDK throws an exception. Exception: %s", e);

    if (e instanceof SNSException.Api) {
        Timber.d("Api exception. %s", ((SNSException.Api) e).getDescription());
    } else if (e instanceof SNSException.Network) {
        Timber.d(e, "Network exception.");
    } else if (e instanceof SNSException.Unknown) {
        Timber.d(e, "Unknown exception.");


SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity()).withErrorHandler(errorHandler).build();

On SDK completion

An optional callback to get notified when the SDK is closed.

val onSDKCompletedHandler: (SNSCompletionResult, SNSSDKState) -> Unit = { (result, state) ->
    when (result) {
        is SNSCompletionResult.SuccessTermination -> Timber.d("The SDK finished successfully")
        is SNSCompletionResult.AbnormalTermination -> Timber.e(result.exception, "The SDK got closed because of errors")

val snsSdkBuilder = SNSMobileSDK.Builder(this).withHandlers(onCompleted = onSDKCompletedHandler)
SNSCompleteHandler completeHandler = (result, state) -> {
    Timber.d("The SDK is finished. Result: " + result + " , State: " + state);
    Toast.makeText(applicationContext, "The SDK is finished. Result: $result, State: $state", Toast.LENGTH_SHORT).show();

    if (result instanceof SNSCompletionResult.SuccessTermination) {
    } else if (result instanceof SNSCompletionResult.AbnormalTermination) {
        Timber.d(((SNSCompletionResult.AbnormalTermination) result).getException());

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity()).withCompleteHandler(completeHandler).build();

The callback takes two parameters:

  • result:
    • SNSCompletionResult.SuccessTermination — a successful completion of the work of the SDK, including dismiss() call.
    • SNSCompletionResult.AbnormalTermination — an error occurred. Look at the exception object to get more information.
  • state: The state at which the SDK was closed. For possible states, refer to the following On SDK state changes

On action result

An optional handler for getting liveness result and controlling action scenario (for Face Auth action only).

The handler takes two parameters:

  • actionId : String — Action ID.
  • answer : String — Liveness module answer. Possible values: "GREEN", "YELLOW", "RED", "ERROR" or null.

The handler must return SNSActionResult. Currently the following values are supported:

  • SNSActionResult.Continue — continue default action scenario (show result screen etc.).
  • SNSActionResult.Cancel — cancel default action scenario (close sdk without result screen).
val onActionResultHandler: SNSActionResultHandler = object : SNSActionResultHandler {
   override fun onActionResult(actionId: String, actionType: String, answer: String?, allowContinuing: Boolean): SNSActionResult {
       Timber.d("Face Auth action result: actionId: $actionId answer: $answer")
       // use default scenario
       return SNSActionResult.Continue

val snsSdkBuilder = SNSMobileSDK.Builder(this).withActionResultHandler(onActionResult)
SNSActionResultHandler actionResultHandler = (actionId, actionType, answer, allowContinuing) -> {
    Timber.d("Action Result: actionId: " + actionId + ", answer: " + answer);
    return SNSActionResult.Continue;

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity()).withActionResultHandler(actionResultHandler).build();


Providing events callback allows you to be aware of the events happening along the processing.

Events are passed into the callback as instances of a class inherited from the base SNSEvent class, this way each event has its eventType and some parameters packed into payload dictionary. So, depending on your needs, you can get event parameters either by examining the payload directly or by casting the given event instance to a specific SNSEvent* class according to its type.

val onEventHandler: SNSEventHandler = object : SNSEventHandler {
    override fun onEvent(event: SNSEvent) {
        when (event) {
            is SNSEvent.SNSEventStepInitiated -> {
                Timber.d("onEvent: step initiated")
            is SNSEvent.SNSEventStepCompleted -> {
                Timber.d("onEvent: step completed")

val snsSdkBuilder = SNSMobileSDK.Builder(this).withEventHandler(onEventHandler)
SNSEventHandler eventHandler = snsEvent -> {
    if (snsEvent instanceof SNSEvent.SNSEventStepInitiated) {
        Timber.d("onEvent: step initiated");
    } else if (snsEvent instanceof SNSEvent.SNSEventStepCompleted) {
        Timber.d("onEvent: step completed");

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity()).withEventHandler(eventHandler).build();

Icon handler

By providing an icon handler you are able to provide drawable for specific positions on Sumsub SDK screens.

We currently only support instructions icons and document icons (for the document selector screen).

You will be provided with an icon key and the themed context. The handler should return a drawable or null if the drawable is not needed.

The SDK uses the following keys:

  • default/do_idCard — an icon for the identity card instruction, the DO block.
  • default/do_idCard_backSide — an icon for the back side of the identity card instruction, the DO block.
  • default/do_passport — an icon for the password instruction, the DO block.
  • default/dont_idCard — an icon for the identity card instruction, the DON'T block.
  • default/dont_idCard_backSide — an icon for the back side of the identity card instruction, the DON'T block.
  • default/dont_passport — an icon for the password instruction, the DON'T block.
  • default/facescan — an icon for the Liveness instruction.
  • IdentityType/<DOCUMENT TYPE> — an icon for the document's item on the Document Selector screen, where <DOCUMENT TYPE> is PASSPORT, DRIVERS, RESIDENCE_PERMIT or another document.
  • Flag/<COUNTRY_CODE> — an icon for a country flag, where <COUTRY_CODE> is ISO 3166-1 alpha-2 country code.

You can provide your own icons or overwrite existing ones by extending the SNSDefaultIconHandler. If no handler has been provided the SNSDefaultIconHandler is used.

val iconHandler = object : SNSIconHandler {
    override fun onResolveIcon(context: Context, key: String): Drawable? {
        val iconRes = when {
            key == "default/do_idCard" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_do
            key == "default/do_passport" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_do_passport
            key == "default/dont_idCard" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_dont
            key == "default/dont_passport" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_dont_passport
            key == "default/facescan" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_liveness
            key == "default/do_idCard_backSide" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_do_back
            key == "default/dont_idCard_backSide" -> com.sumsub.sns.core.R.drawable.sns_ic_intro_dont_back
            key == "IdentityType/PASSPORT" -> com.sumsub.sns.core.R.drawable.sns_ic_iddoc_passport
            key == "IdentityType/DRIVERS" -> com.sumsub.sns.core.R.drawable.sns_ic_iddoc_driving_license
            key == "IdentityType/RESIDENCE_PERMIT" -> com.sumsub.sns.core.R.drawable.sns_ic_iddoc_residence_permit
            key.startsWith("IdentityType/") -> com.sumsub.sns.core.R.drawable.sns_ic_iddoc_id_card
            else -> -1

        return iconRes.takeIf { it != -1 }?.let { ResourcesCompat.getDrawable(context.resources, it, context.theme) }

val snsSdkBuilder = SNSMobileSDK.Builder(this).withIconHandler(iconHandler)