# Selfie Scan

The Selfie Scan module captures the user's face with their device's camera and runs configurable liveness, face recognition, and image quality checks. It can enroll new users so their face can be matched against an ID photo or used for later authentication. It can also log in returning users using [1:1](https://developer.incode.com/docs/glossary#11-face-authentication) or [1:N](https://developer.incode.com/docs/glossary#1n-face-authentication) face authentication.

For an overview of this module and how it works, see [Face Capture](https://developer.incode.com/docs/face-capture).

How you use this module depends on your [integration pattern](https://developer.incode.com/docs/android-common-implementation-patterns#integration-patterns). In Patterns 1 and 2, you add the module to a `FlowConfig` in code as shown on this page. In Pattern 3, you define your modules and configuration in Dashboard as a Flow or Workflow and reference it by ID with `startFlow()` or `startWorkflow()` as shown on [Run Flows Configured in Dashboard](https://developer.incode.com/docs/android-run-flows-configured-online).

## Add Selfie Scan

Add the module with one of the `addSelfieScan` overloads. Most onboarding flows require Selfie Scan, and some modules depend on ordering: for example, [Face Match](https://developer.incode.com/docs/android-face-match) needs Selfie Scan and an ID or QR Scan added first. `FlowConfig.Builder.build()` throws `ModuleConfigurationException` if a required module or ordering rule is broken.

```kotlin
// Default configuration
flowConfigBuilder.addSelfieScan()

// Custom configuration
flowConfigBuilder.addSelfieScan(selfieScan)
```
```java
// Default configuration
flowConfigBuilder.addSelfieScan();

// Custom configuration
flowConfigBuilder.addSelfieScan(selfieScan);
```

Selfie Scan ships both a v1 (legacy View) and a v2 (Jetpack Compose) capture screen; the active one depends on your Incode configuration. Contact your Incode representative to enable v2. In v2, tablets are landscape-locked with an optimized layout and phones are portrait-locked.

### Example

The example below builds a `SelfieScan` module with tutorials shown, adds it to the flow, and listens for the result through `onSelfieScanCompleted()`.

```kotlin
val selfieScan = SelfieScan.Builder()
    .setShowTutorials(true)
    .build()

val flowConfig = FlowConfig.Builder()
    .addSelfieScan(selfieScan)
    .build()

val onboardingListener = object : IncodeWelcome.OnboardingListener() {
    override fun onSelfieScanCompleted(selfieScanResult: SelfieScanResult) {
        // Selfie scan completed
    }

    override fun onError(error: Throwable) {
        // Onboarding flow was aborted due to error
    }
}

IncodeWelcome.getInstance().startOnboarding(
    activityContext,
    sessionConfig,
    flowConfig,
    onboardingListener
)
```
```java
SelfieScan selfieScan = new SelfieScan.Builder()
    .setShowTutorials(true)
    .build();

FlowConfig flowConfig = new FlowConfig.Builder()
    .addSelfieScan(selfieScan)
    .build();

IncodeWelcome.OnboardingListener onboardingListener = new IncodeWelcome.OnboardingListener() {
    @Override
    public void onSelfieScanCompleted(@NonNull SelfieScanResult selfieScanResult) {
        // Selfie scan completed
    }

    @Override
    public void onError(@NonNull Throwable error) {
        // Onboarding flow was aborted due to error
    }
};

IncodeWelcome.getInstance().startOnboarding(
    activityContext,
    sessionConfig,
    flowConfig,
    onboardingListener
);
```

## Configuration Options

Configure the module with `SelfieScan.Builder`. The scan mode and where liveness and recognition run also depend on your Incode Flow or Workflow configuration, not just these options.

| Setting                                   | Description                                                                                                                                                                                                                                                                                                                                                     |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setMode`                                 | Sets the scan mode: `Mode.ENROLL`, `Mode.LOGIN`, or `Mode.MASK_CHECK`. Default is `Mode.ENROLL`.                                                                                                                                                                                                                                                                |
| `setFaceAuthMode`                         | Runs face detection and liveness on `LOCAL` or the `SERVER`. Default is `FaceAuthMode.SERVER`.                                                                                                                                                                                                                                                                  |
| `setShowTutorials`                        | Shows a tutorial before the scan. Default is `true`.                                                                                                                                                                                                                                                                                                            |
| `setMaskCheckEnabled`                     | Enables local face-mask detection during capture.                                                                                                                                                                                                                                                                                                               |
| `setCameraFacing`                         | Selects `CameraFacing.FRONT` or `CameraFacing.BACK`. Default is `CameraFacing.FRONT`. Has no effect in some SDK versions; check `SelfieScan.Builder` in [API Reference](https://developer.incode.com/docs/android-api-reference) for current behavior.                                                                                            |
| `setAutoCaptureTimeout`                   | Sets the selfie auto-capture timeout, in seconds.                                                                                                                                                                                                                                                                                                               |
| `setOnDeviceFaceResultsSubmissionEnabled` | Runs liveness detection and age estimation on-device, submitting only the score to the backend. Requires [end-to-end encryption](https://developer.incode.com/docs/android-e2ee), the liveness-detection and age-estimation model dependencies, and the V2 UI; incompatible with frame streaming, screen recording, and video liveness recording. |

In `CAPTURE_ONLY` mode, captured media stays on the device. See [Capture-Only Mode](https://developer.incode.com/docs/android-capture-only-sdk) for the capture-only data flow.

For the complete option set, see `SelfieScan.Builder` in [API Reference](https://developer.incode.com/docs/android-api-reference).

## Result

Selfie Scan delivers a `SelfieScanResult` to the `onSelfieScanCompleted(selfieScanResult)` callback on `OnboardingListener`. Key fields include:

- `isSpoofAttempt`: `false` if user is a real person; `true` if there was a spoof attempt. Populated only when a server-side face evaluation runs and `status` is `STATUS_OK` or `STATUS_SPOOF_DETECTED`; otherwise, `null`, including in Capture-Only, on-device, and offline modes.

- `hasFaceMask`: `true` if a face mask is detected, `false` if not, `null` if the check wasn't performed.

- `croppedSelfieImgPath`: The file path to the cropped selfie image.

- `fullFrameSelfieImgPath`: The file path to the full-frame selfie image.

- `status`: The selfie scan status. One of the following:
  - `STATUS_OK` (0): All preconditions for checking liveness are met.
  - `STATUS_LENSES_DETECTED` (1): The user is wearing lenses.
  - `STATUS_TOO_DARK` (2): The photo is too dark for reliable results.
  - `STATUS_TOO_BLURRY` (3): The photo is too blurry for reliable results.
  - `STATUS_FACE_NOT_FOUND` (4): No face meeting the constraints was found.
  - `STATUS_FACE_TOO_SMALL` (5): The face dimensions are insufficient.
  - `STATUS_INSUFFICIENT_PHOTO_QUALITY` (6): Photo quality too low to check liveness. This is often because of bad lighting.
  - `STATUS_GENERIC_ERROR` (7): A generic error occurred while producing the result.
  - `STATUS_NO_RESULT` (9): No status has been produced yet.
  - `STATUS_MASK_DETECTED` (10): The user is wearing a mask.
  - `STATUS_CLOSED_EYES_DETECTED` (11): The user has their eyes closed.
  - `STATUS_HEAD_COVER_DETECTED` (12): A head covering such as a hat is detected.
  - `STATUS_SPOOF_DETECTED` (13): A spoof attempt is detected.

- `selfieBase64`: The Base64-encoded cropped selfie image. Redacted from `toString()`.

- `fullFrameSelfieEncryptedBase64`: The encrypted Base64-encoded full-frame selfie image. Redacted from `toString()`.

- `fullFrameSelfieBase64`: The Base64-encoded full-frame selfie image. Redacted from `toString()`.

- `metadata`: Proprietary capture metadata required by the SDK. Populated only when `SdkMode` is `CAPTURE_ONLY`; otherwise, `null`.

- `videoFilePath`: The path to the original, unencrypted video-liveness recording. Populated only when `setVideoLivenessRecordingEnabled(true)` and `SdkMode` is `CAPTURE_ONLY`; otherwise, `null`. For transmission, use `presignedVideoFilePath`.

- `presignedVideoFilePath`: The path to the video-liveness file prepared for back-end transmission. It contains base64 text and is not cryptographically encrypted. Populated only when `setVideoLivenessRecordingEnabled(true)` and `SdkMode` is `CAPTURE_ONLY`; otherwise, `null`.

- `allAttemptsExhausted`: `true` if no further capture or upload retries are available for this selfie scan.

- `isBlocklisted`: `true` if the recognized user is on the blocklist and login was denied (`faceLoginResult.success` is `false`). Set only during server-side face authentication in `LOGIN` mode; always `false` in LOCAL mode. This result is available for SDK versions 5.50.0 and newer.

- `faceRecognitionConfidence`: The similarity score between stored face and captured selfie. This result was deprecated in SDK version 5.2.0.

- `isFaceMatched`: If the stored face and selfie match. This result was deprecated in SDK version 5.35.0; use `faceLoginResult.success` instead.

- `spoofConfidence`: The likelihood of a spoofing attempt. This result was deprecated in SDK version 5.2.0; use `isSpoofAttempt` instead.

- `maskConfidence`: The likelihood the person is wearing a mask. This result was deprecated in SDK version 5.18.0; use `hasFaceMask` instead.

- `selfieEncryptedBase64`: The encrypted Base64 cropped selfie. This result was deprecated in SDK version 5.12.0; use `selfieBase64` / `fullFrameSelfie*`.

- `idealCaptureEnvironmentTestResult`: The result of the ideal-capture-environment test. This result was deprecated in SDK version 5.39.0; use `metadata` instead.

- `faceLoginResult`: The result of the Face Login operation populated in `SelfieScan.Mode.LOGIN`. One of the following:
  - `success`: `true` if the face login succeeded; otherwise, `false`.
  - `customerUUID`: The customer UUID of the recognized user; `null` when unavailable.
  - `interviewId`: The interview ID for the login session; `null` when unavailable.
  - `interviewToken`: The interview token for the login session; `null` when unavailable.
  - `token`: The session token associated with the login; `null` when unavailable.
  - `transactionId`: The transaction ID for the login operation; `null` when unavailable.

- Fields inherited from `BaseResult`:
  - `resultCode`: The `ResultCode` for this result.
  - `error`: When `resultCode` is `ERROR`, the `Throwable` that caused it; otherwise, `null`.
  - `deviceStats`: The `DeviceStats` snapshot of the device state when the result was produced. In `CAPTURE_ONLY` mode this info is also available in `metadata`.
  - `motionStatus`: Device motion assessment during capture. One of the following:
    - `UNCLEAR`: Motion could not be determined; the default.
    - `PASS`: Device motion was within acceptable limits.
    - `FAIL`: Excessive device motion was detected.

If the scan cannot complete, the flow is aborted and the error surfaces via `OnboardingListener.onError(Throwable)`.

For all fields, see `SelfieScanResult` in [API Reference](https://developer.incode.com/docs/android-api-reference).
