# Capture-Only Mode

Capture-Only mode runs the Incode capture experience on-device and returns the resulting photos and videos through the `IncdOnboardingDelegate` callbacks, without sending data to Incode for processing. Use it when you want Incode's auto-capturing camera UX but do not want the SDK to upload or process the media. You are responsible for whatever you do with the captured media afterward.

***

## How It Works

The capture APIs perform local checks, auto-capture the photo when conditions are met, and return the captured media through a delegate callback.

The diagram below shows the recommended data flow when using Capture-Only mode. The SDK captures locally and hands the media to the host app. The host app sends it to the customer server, which calls the Incode API and decides whether to repeat the capture.

Each module follows the same pattern, but the completion callback, the result type, and the fields it carries change. Read the diagram below once to understand the pattern, then view the diagram for the module you are integrating: [ID Capture](#id-capture), [Selfie](#selfie), or [Video Selfie](#video-selfie). Each module-specific diagram includes the callback signature and the result fields your app receives.

```mermaid
sequenceDiagram
    autonumber
    participant App as Host App
    participant SDK as Incode SDK
    participant Server as Customer Server

    App->>SDK: initIncdOnboarding(...) then sdkMode = .captureOnly
    Note over App,SDK: Keep the shared manager alive so the<br/>SDK is not cleared from memory

    loop For each capture section in the flow
        App->>SDK: startOnboardingSection(flowConfig:sectionTag:delegate:)
        SDK->>SDK: local checks, auto capture
        SDK-->>App: module completion callback with result
        SDK-->>App: onOnboardingSectionCompleted(flowTag)
        Note over App,SDK: Run one section at a time - start the next<br/>only after onOnboardingSectionCompleted
        App->>Server: upload captured media
        Server->>Server: call Incode API, analyze response
        alt capture not acceptable
            Server-->>App: request re-capture
            App->>SDK: startOnboardingSection(...) again
        end
    end

    App->>SDK: deleteLocalUserData()
```

Starting a second section while one is still running fails with `IncdFlowError.sectionAlreadyRunning`. Wait for `onOnboardingSectionCompleted(_:)` before starting the next section.

### ID Capture

The diagram below shows the recommended data flow when using Capture-Only mode for the [ID Capture](https://developer.incode.com/docs/module-id-scan) module. The result is an in-memory `IdScanResult`; the image is exposed as a `UIImage` and a base64 string, not as a file on disk.

```mermaid
sequenceDiagram
    autonumber
    participant App as Host App
    participant SDK as Incode SDK
    participant Server as Customer Server

    App->>SDK: startOnboardingSection (ID scan, front)
    SDK-->>App: onIdFrontCompleted(IdScanResult)
    Note right of App: image (UIImage?)<br/>base64Image (String?)<br/>chosenIdType<br/>metadata (String?)
    SDK-->>App: onOnboardingSectionCompleted(flowTag)
    App->>Server: upload front ID image
    Server-->>App: OK or repeat front scan

    App->>SDK: startOnboardingSection (ID scan, back)
    SDK-->>App: onIdBackCompleted(IdScanResult)
    Note right of App: image (UIImage?)<br/>base64Image (String?)<br/>chosenIdType<br/>metadata (String?)
    SDK-->>App: onOnboardingSectionCompleted(flowTag)
    App->>Server: upload back ID image
    Server-->>App: OK or repeat back scan
```

### Selfie

The diagram below shows the recommended data flow when using Capture-Only mode for the [Selfie](https://developer.incode.com/docs/module-selfie) module.

```mermaid
sequenceDiagram
    autonumber
    participant App as Host App
    participant SDK as Incode SDK
    participant Server as Customer Server

    App->>SDK: startOnboardingSection (selfie scan)
    SDK-->>App: onSelfieScanCompleted(SelfieScanResult)
    Note right of App: image (UIImage?)<br/>selfieBase64 (String?)<br/>selfieEncryptedBase64 (String?)<br/>metadata (String?)<br/>videoFileURL (URL?, video liveness only)
    SDK-->>App: onOnboardingSectionCompleted(flowTag)
    App->>Server: upload selfie
    Server-->>App: OK or repeat selfie scan
```

### Video Selfie

The diagram below shows the recommended data flow when using Capture-Only mode for the [Video Selfie](https://developer.incode.com/docs/module-video-selfie) module. `VideoSelfieResult` exposes the captured stills as `UIImage` and the media as `Data`.

```mermaid
sequenceDiagram
    autonumber
    participant App as Host App
    participant SDK as Incode SDK
    participant Server as Customer Server

    App->>SDK: startOnboardingSection (video selfie)
    SDK-->>App: onVideoSelfieCompleted(VideoSelfieResult)
    Note right of App: selfie (UIImage?)<br/>idFront / idBack (UIImage?)<br/>document (UIImage?)<br/>videoData (Data?)<br/>audioData (Data?)<br/>voiceConsentSelfie (UIImage?)
    SDK-->>App: onOnboardingSectionCompleted(flowTag)
    App->>Server: upload video selfie assets
    Server-->>App: OK or repeat video selfie
```

***

## Supported Modules

Capture-Only mode supports the following modules. Each is added to a flow through the matching `IncdOnboardingFlowConfiguration.addXxx(...)` builder.

| Module                                                                                   | Builder                                          | Description                                                                                                                                                                                                               |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Intro](https://developer.incode.com/docs/module-intro)                    | `addIntro(checks:)`                              | Displays an introduction screen at the start of a flow.                                                                                                                                                                   |
| [Full Name](https://developer.incode.com/docs/module-full-name)            | `addFullName()`                                  | Collects the user's name and sends it to the server.                                                                                                                                                                      |
| [Phone](https://developer.incode.com/docs/module-phone)                    | `addPhone(otpVerification:defaultRegionPrefix:)` | Collects a user's phone number and can confirm ownership by sending a one-time password (OTP) via SMS.                                                                                                                    |
| [Email](https://developer.incode.com/docs/module-email)                    | `addEmail(otpVerification:)`                     | Collects a user's email address and can confirm ownership by sending a one-time password (OTP) to that address.                                                                                                           |
| [ID Capture](https://developer.incode.com/docs/module-id-scan)             | `addIdScan(...)`                                 | Captures the front and back of a government-issued ID, with auto-capture and quality checks, and produces clean images for processing.                                                                                    |
| [NFC](https://developer.incode.com/docs/module-nfc-scan)                   | `addNfcScan(...)`                                | Reads the secure [NFC](https://developer.incode.com/docs/glossary#nfc-scan) chip embedded in ICAO 9303-compliant travel documents (`-nfc` variant).                                                         |
| [Document Capture](https://developer.incode.com/docs/module-document-scan) | `addDocumentScan(...)`                           | Captures a supplementary document, such as a proof of address document, medical document, or bank statement.                                                                                                              |
| [Selfie](https://developer.incode.com/docs/module-selfie)                  | `addSelfieScan(...)`                             | Captures the user's face with the device camera and runs configurable liveness, face-recognition, and image-quality checks.                                                                                               |
| [Geolocation](https://developer.incode.com/docs/module-geolocation)        | `addGeolocation(isSkippable:)`                   | Requests location permission, then captures the precise physical location of the user's device, using its GPS sensor to record coordinates and location fields such as country, state, and city.                          |
| [Signature](https://developer.incode.com/docs/module-signature)            | `addSignature(...)`                              | Captures a signature the user hand-draws on screen, optionally presenting documents to sign.                                                                                                                              |
| [Video Selfie](https://developer.incode.com/docs/module-video-selfie)      | `addVideoSelfie(videoSelfieConfiguration:)`      | Records a short video of the user performing a guided series of actions, including capturing their ID and selfie, to confirm physical presence and run liveness and face match checks. It can also capture voice consent. |

***

## Set Up Capture-Only Mode

Complete the following steps in order.

### Initialize the SDK for Capture-Only

Initialize the SDK with `initIncdOnboarding(...)`, then set `sdkMode` to `.captureOnly` on the shared manager before starting a capture section.

```swift
IncdOnboardingManager.shared.initIncdOnboarding(
    url: "<YOUR_API_URL>",
    apiKey: "<YOUR_API_KEY>"
) { success, error in
    guard success == true else { return }
    IncdOnboardingManager.shared.sdkMode = .captureOnly
}
```

- Capture calls only work after the SDK has been initialized. `IncdOnboardingManager.shared` is a singleton; keep it alive so the SDK is not cleared from memory.
- In the **Public** distribution variant, setting `.captureOnly` logs a warning and the SDK reverts to `.standard`.

### Perform Capture SDK Calls

Build the module you want to capture into an `IncdOnboardingFlowConfiguration`, then start an onboarding section. Results arrive on the `IncdOnboardingDelegate` callbacks. The example below starts an ID scan section; the selfie scan section is started only after `onOnboardingSectionCompleted` reports the ID section done. Run one section at a time; starting a second section while one is still running fails with `IncdFlowError.sectionAlreadyRunning`.

```swift
// ID scan
IncdOnboardingManager.shared.sdkMode = .captureOnly
IncdOnboardingManager.shared.delegate = self

let idFlow = IncdOnboardingFlowConfiguration()
idFlow.addIdScan(scanStep: .both)

IncdOnboardingManager.shared.startOnboardingSection(
    flowConfig: idFlow,
    sectionTag: "id-scan",
    delegate: self
)
```

```swift
extension MyCaptureController: IncdOnboardingDelegate {
    // ID scan results
    func onIdFrontCompleted(_ result: IdScanResult) {
        // result.image, result.base64Image, result.chosenIdType, result.metadata
    }
    func onIdBackCompleted(_ result: IdScanResult) {
        // result.image, result.base64Image, result.chosenIdType, result.metadata
    }

    // Selfie scan result
    func onSelfieScanCompleted(_ result: SelfieScanResult) {
        // result.image, result.selfieBase64, result.metadata
    }

    // Section lifecycle — start the next section only after this fires
    func onOnboardingSectionCompleted(_ flowTag: String) {
        // Upload the captured media, then start the next section.
        guard flowTag == "id-scan" else { return }

        let selfieFlow = IncdOnboardingFlowConfiguration()
        selfieFlow.addSelfieScan()

        IncdOnboardingManager.shared.startOnboardingSection(
            flowConfig: selfieFlow,
            sectionTag: "selfie-scan",
            delegate: self
        )
    }

    func onSuccess() {}
    func onError(_ error: IncdFlowError) {}
    func userCancelledSession() {}
}
```

### Forward Metadata for Deepsight

If your organization uses Deepsight, forward the `metadata` field from `IdScanResult` and `SelfieScanResult` to the corresponding `omni/add/*` API request. Deepsight's downstream checks depend on this field. If you omit it, those checks run without the data they need.

### Clean Up Local User Data

After the flow exits, call `IncdOnboardingManager.shared.deleteLocalUserData()` to delete all local user data generated during the flow and clear the current onboarding session data.

<Callout icon="❗️" theme="error">
  ### Always call deleteLocalUserData()

  Call `deleteLocalUserData()` to remove the local user data the flow generated. Call it from your success, error, and cancellation callbacks.
</Callout>

Call `deleteLocalUserData()` from the following delegate callbacks:

```swift
func onSuccess() {
    IncdOnboardingManager.shared.deleteLocalUserData()
}

func onError(_ error: IncdFlowError) {
    IncdOnboardingManager.shared.deleteLocalUserData()
}

func userCancelledSession() {
    IncdOnboardingManager.shared.deleteLocalUserData()
}
```

***

## Supported API Configurations

### FlowConfig

`IncdOnboardingFlowConfiguration` assembles the modules, in order, for the section you start. The builder below lists every module supported in Capture-Only mode. The `…` placeholders stand for the values you supply.

```swift
let flow = IncdOnboardingFlowConfiguration()   // or .init(waitForTutorials:)
flow.addIntro(checks: [])
flow.addFullName()
flow.addPhone(otpVerification: true, defaultRegionPrefix: 1)
flow.addEmail(otpVerification: true)
flow.addIdScan(scanStep: .both)
flow.addNfcScan(…)
flow.addDocumentScan(…)
flow.addSelfieScan()
flow.addGeolocation(isSkippable: false)
flow.addSignature(…)
flow.addVideoSelfie(videoSelfieConfiguration: …)
```

Each builder's full options, result type, and callback are documented on its corresponding [module page](https://developer.incode.com/docs/ios-individual-modules).

### Shared Settings

iOS does not expose a separate shared-configuration object. Settings shared across modules—SDK mode, presentation, delegate, diagnostics, and other flow toggles—are set on `IncdOnboardingManager.shared`, and theming and localization are configured separately. See [Configure Flows Locally and Run End to End](https://developer.incode.com/docs/ios-configure-locally-end-to-end#presentation-and-ux-configuration) for the manager properties and [Customization](https://developer.incode.com/docs/ios-customization) for appearance. The same settings apply in Capture-Only mode.

***

## Customizable Strings

Every user-facing string the SDK shows can be overridden, including in Capture-Only flows. For the complete list of string keys and override methods, see [Customization](https://developer.incode.com/docs/ios-customization).

***

For help, see the [API Reference](https://developer.incode.com/docs/ios-api-reference) or contact Incode support.

<br />
