---
title: "Capture-Only Mode"
url: "https://developer.incode.com/sdk-reference/ios-capture-only-sdk/"
section: "sdk-reference"
group: "iOS SDK / iOS Getting Started"
version: "v1.1"
status: "live"
---
# 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: finishFlow(...)
    SDK-->>App: onDiagnosticsDataReceived(data), when available
    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](/sdk-reference/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](/sdk-reference/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](/sdk-reference/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](/sdk-reference/module-intro/)                    | `addIntro(checks:)`                              | Displays an introduction screen at the start of a flow.                                                                                                                                                                   |
| [Full Name](/sdk-reference/module-full-name/)            | `addFullName()`                                  | Collects the user's name and sends it to the server.                                                                                                                                                                      |
| [Phone](/sdk-reference/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](/sdk-reference/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](/sdk-reference/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](/sdk-reference/module-nfc-scan/)                   | `addNfcScan(...)`                                | Reads the secure [NFC](/get-started-with-incode/glossary/#nfc-scan) chip embedded in ICAO 9303-compliant travel documents (`-nfc` variant).                                                         |
| [Document Capture](/sdk-reference/module-document-scan/) | `addDocumentScan(...)`                           | Captures a supplementary document, such as a proof of address document, medical document, or bank statement.                                                                                                              |
| [Selfie](/sdk-reference/module-selfie/)                  | `addSelfieScan(...)`                             | Captures the user's face with the device camera and runs configurable liveness, face-recognition, and image-quality checks.                                                                                               |
| [Geolocation](/sdk-reference/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](/sdk-reference/module-signature/)            | `addSignature(...)`                              | Captures a signature the user hand-draws on screen, optionally presenting documents to sign.                                                                                                                              |
| [Video Selfie](/sdk-reference/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
}
```

Console logs are disabled by default. Pass `true` for `loggingEnabled` to enable SDK logs.

- 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) {
        switch flowTag {
        case "id-scan":
            // Upload the ID media, then start the next section.
            let selfieFlow = IncdOnboardingFlowConfiguration()
            selfieFlow.addSelfieScan()

            IncdOnboardingManager.shared.startOnboardingSection(
                flowConfig: selfieFlow,
                sectionTag: "selfie-scan",
                delegate: self
            )
        case "selfie-scan":
            // Upload the selfie, then finish the local capture-only flow.
            IncdOnboardingManager.shared.finishFlow { success, error in
                // Local session cleanup is complete.
                IncdOnboardingManager.shared.deleteLocalUserData()
            }
        default:
            break
        }
    }

        func onDiagnosticsDataReceived(_ data: Data) {
        // Forward the prepared diagnostics payload without modifying it.
    }

    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.

### Finish the Capture-Only Session

After all capture-only sections complete successfully, we strongly recommend calling `finishFlow` once to end the local session:

```swift
IncdOnboardingManager.shared.finishFlow { success, error in
    // Local capture-only session cleanup is complete.
}
```

In Capture-Only mode, `finishFlow` does not call the back end. It completes local session cleanup and triggers `onDiagnosticsDataReceived` when diagnostics are available.

### Forward Diagnostics Data

In Capture-Only mode, the SDK does not send diagnostics to Incode. Instead, it hands them to your app, so you can upload them yourself. This step is optional, but recommended. Call `finishFlow` after the final section completes successfully to trigger this callback. Terminal SDK errors and user cancellation finalize diagnostics automatically.

Implement the following delegate method to receive the data:

```swift
func onDiagnosticsDataReceived(_ data: Data) {
    // Upload `data` without modifying it.
}
```

Forward the data as the request body of a `POST` to `omni/add/diagnostics-data/`, using the same Incode API base URL and session authentication as the onboarding session. Set `Content-Type` to `application/json`.

### 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.

:::danger
Always call `deleteLocalUserData()` to remove the local user data the flow generated. Call it from your success, error, and cancellation callbacks.
:::

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](/sdk-reference/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](/sdk-reference/ios-configure-locally-end-to-end/#presentation-and-ux-configuration) for the manager properties and [Customization](/sdk-reference/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](/sdk-reference/ios-customization/).

***

For help, see the [API Reference](/sdk-reference/ios-api-reference/) or contact Incode support.

<br />