---
title: "Capture-Only Mode"
url: "https://developer.incode.com/sdk-reference/ios-capture-only-sdk/"
section: "sdk-reference"
group: "iOS SDK / iOS Getting Started / Capture-Only Mode"
version: "v1.1"
status: "live"
---
# Capture-Only Mode

Capture-Only mode presents Incode's capture UI and returns the captured media to your app without uploading that media to Incode for server-side processing. Use this mode when your app needs to control when and how captures are sent to your own back end.

The SDK still performs the local checks needed to guide capture, such as framing and image-quality checks. Server-side validation, OCR, face matching, and other processing do not run as part of the Capture-Only section.

:::info
Capture-Only mode is not available in the **Public** distribution variant. If you set `sdkMode` to `.captureOnly` in that variant, the SDK logs a warning and uses `.standard` mode instead.
:::

***

## Supported Modules

Capture-Only mode supports these capture modules:

| Module | Builder | Result callback |
| --- | --- | --- |
| [ID Capture](/sdk-reference/module-id-scan/) | `addIdScan(...)` | `onIdFrontCompleted(_:)` and `onIdBackCompleted(_:)` |
| [Selfie](/sdk-reference/module-selfie/) | `addSelfieScan(...)` | `onSelfieScanCompleted(_:)` |
| [Document Capture](/sdk-reference/module-document-scan/) | `addDocumentScan(...)` | `onDocumentScanCompleted(_:)` |
| [Video Selfie](/sdk-reference/module-video-selfie/) | `addVideoSelfie(videoSelfieConfiguration:)` | `onVideoSelfieCompleted(_:)` |

Do not add processing or non-capture modules to a Capture-Only flow. Note that:

- `addIdScan(scanStep: .both)` normally runs [Process ID](/sdk-reference/module-process-id/) automatically after ID Capture. In this mode, it does not; it adds ID Capture only.
- The [Process ID](/sdk-reference/module-process-id/) and [NFC](/sdk-reference/module-nfc-scan/) modules are not supported when `sdkMode` is `.captureOnly`.
- You can add the [Intro](/sdk-reference/module-intro/) module, but it shows a local screen only; it returns no capture result.
- Capture-Only mode does not support the [Full Name](/sdk-reference/module-full-name/), [Phone](/sdk-reference/module-phone/), [Email](/sdk-reference/module-email/), [Geolocation](/sdk-reference/module-geolocation/), or [Signature](/sdk-reference/module-signature/) modules.

Set `sdkMode` before creating an `IncdOnboardingFlowConfiguration`. Some builder behavior depends on the active SDK mode.

***

## How It Works

Use the Sections API to run one capture section at a time. A module-specific callback returns the capture result. After the section closes, `onOnboardingSectionCompleted(_:)` reports its tag and the next section can start.

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

    App->>SDK: Initialize and set sdkMode = .captureOnly
    App->>SDK: startOnboardingSection(...)
    SDK->>SDK: Local checks and capture
    SDK-->>App: Module result callback
    SDK-->>App: onOnboardingSectionCompleted(sectionTag)
    App->>Server: Send captured media and metadata
    Server-->>App: Accept or request another capture
    App->>SDK: Start the next section when needed
    App->>SDK: finishFlow(...)
    SDK-->>App: finishFlow completion
    Note over App,SDK: Diagnostics delivery is asynchronous and<br/>is not ordered with the completion
    SDK-->>App: onDiagnosticsDataReceived(data), when enabled and available
    App->>SDK: deleteLocalUserData()
```

Only one section can run at a time. Starting another section before the active one completes reports `IncdFlowError.sectionAlreadyRunning(activeSectionTag)` to the delegate supplied for the rejected request. The active section continues running.

`onOnboardingSectionCompleted(_:)` is the safe point to start another section. Do not start the next section from a module result callback.

***

## Set Up Capture-Only Mode

### 1. Initialize the SDK

Set the required presenting view controller, initialize the SDK, and then enable Capture-Only mode. The SDK manager is a singleton; the object that must remain available for presentation is your view controller because `presentingViewController` is a weak reference.

```swift
let manager = IncdOnboardingManager.shared
manager.presentingViewController = self

manager.initIncdOnboarding { success, _ in
    guard success == true else {
        // Handle IncdInitError.
        return
    }

    manager.sdkMode = .captureOnly
}
```

All `initIncdOnboarding(...)` configuration parameters are optional. Pass your normal `url`, `apiKey`, logging, or experiment settings when your integration requires them. Console logging is disabled by default; set `loggingEnabled: true` only when you need SDK console logs.

### 2. Build and Start a Capture Section

Create a separate `IncdOnboardingFlowConfiguration` for each section and pass your `IncdOnboardingDelegate` to `startOnboardingSection(flowConfig:sectionTag:delegate:)`.

#### ID Capture

```swift
let flow = IncdOnboardingFlowConfiguration()
flow.addIdScan(scanStep: .both)

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

For `.both`, the same section captures the front and then the back of an ID. It calls `onIdFrontCompleted(_:)`, then `onIdBackCompleted(_:)`, and finally calls `onOnboardingSectionCompleted("id-capture")`. A passport has no back capture.

Key `IdScanResult` capture fields are:

- `image: UIImage?`
- `base64Image: String?`
- `encryptedBase64Image: String?`
- `chosenIdType: IdType?`
- `idCategory: IDCategory`
- `metadata: String?`, populated for Capture-Only captures
- `error: IncdIdScanError?`

See [ID Capture](/sdk-reference/module-id-scan/) for all configuration options and result fields.

#### Selfie

```swift
let flow = IncdOnboardingFlowConfiguration()
flow.addSelfieScan()

IncdOnboardingManager.shared.startOnboardingSection(
    flowConfig: flow,
    sectionTag: "selfie",
    delegate: self
)
```

Key `SelfieScanResult` capture fields are:

- `image: UIImage?`
- `selfieBase64: String?`
- `selfieEncryptedBase64: String?`
- `metadata: String?`
- `frameWithDepth: String?`, when that capture option is enabled and supported
- `videoFileURL: URL?` and `presignedVideoFileURL: URL?`, when video capture is enabled
- `error: SelfieScanError?`

Read URL-backed results before deleting local user data. See [Selfie](/sdk-reference/module-selfie/) for all configuration options and result fields.

#### Document Capture

```swift
let flow = IncdOnboardingFlowConfiguration()
flow.addDocumentScan(
    documentType: .addressStatement,
    documentSources: [.camera, .photoUpload, .fileUpload]
)

IncdOnboardingManager.shared.startOnboardingSection(
    flowConfig: flow,
    sectionTag: "document-capture",
    delegate: self
)
```

Key `DocumentScanResult` capture fields are:

- `documentImage: UIImage?`
- `data: Data?`, containing image or PDF bytes when available
- `mimeType: String?`
- `documentType: DocumentType`
- `error: DocumentScanError?`

Server-generated OCR result fields are not produced by a Capture-Only section. See [Document Capture](/sdk-reference/module-document-scan/) for all configuration options and result fields.

#### Video Selfie

```swift
let configuration = VideoSelfieConfiguration()
configuration.tutorials(enabled: true)
configuration.selfieScan(enabled: true)
configuration.idScan(enabled: true, validateId: false)

let flow = IncdOnboardingFlowConfiguration()
flow.addVideoSelfie(videoSelfieConfiguration: configuration)

IncdOnboardingManager.shared.startOnboardingSection(
    flowConfig: flow,
    sectionTag: "video-selfie",
    delegate: self
)
```

`VideoSelfieResult` can contain:

- `selfie: UIImage?`
- `idFront: UIImage?`, `idBack: UIImage?`, and `passport: UIImage?`
- `document: UIImage?`
- `videoData: Data?` and `audioData: Data?`
- `voiceConsentSelfie: UIImage?`
- `error: VideoSelfieError?`

The SDK delivers `onVideoSelfieCompleted(_:)` when the result contains recorded video or audio. A fatal Video Selfie failure is also reported through `onError(_:)`. See [Video Selfie](/sdk-reference/module-video-selfie/) for all configuration options and result fields.

***

## Receive Results

Implement the callbacks for the modules you use. Copy or upload any result data your app needs before local cleanup.

```swift
extension MyCaptureViewController: IncdOnboardingDelegate {
    func onIdFrontCompleted(_ result: IdScanResult) {
        // Store or upload the front image and metadata.
    }

    func onIdBackCompleted(_ result: IdScanResult) {
        // Store or upload the back image and metadata.
    }

    func onSelfieScanCompleted(_ result: SelfieScanResult) {
        // Store or upload the selfie, metadata, and optional video data.
    }

    func onDocumentScanCompleted(_ result: DocumentScanResult) {
        // Store or upload result.data and result.mimeType.
    }

    func onVideoSelfieCompleted(_ result: VideoSelfieResult) {
        // Store or upload the captured Video Selfie assets.
    }

    func onOnboardingSectionCompleted(_ flowTag: String) {
        // The section is closed. Start the next section when your app is ready.
    }

    func onSuccess() {
        // Successful Sections API cleanup is reported by finishFlow's completion.
    }

    func onError(_ error: IncdFlowError) {
        if case .sectionAlreadyRunning = error {
            // The original section is still active. Do not delete its local data.
            return
        }

        // Persist any capture data you need, then remove SDK-local user data.
        IncdOnboardingManager.shared.deleteLocalUserData()
    }

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

### Forward Capture Metadata

When you submit ID or Selfie captures from your back end, include the corresponding `metadata` value from `IdScanResult` or `SelfieScanResult`. Forward it unchanged with the capture it describes. This metadata is part of the server-side capture-processing contract.

***

## Finish the Capture-Only Session

After the final section completes successfully and your app has retained the capture data it needs, call `finishFlow` once:

```swift
IncdOnboardingManager.shared.finishFlow { success, error in
    guard success else {
        // Handle IncdError.
        return
    }

    IncdOnboardingManager.shared.deleteLocalUserData()
}
```

In Capture-Only mode, `finishFlow` performs local flow cleanup and does not call the back end. For a successful Sections API integration, its completion is the success signal; it does not call `onSuccess()`.

### Forward Diagnostics Data

When diagnostics are enabled, the SDK returns a prepared diagnostics payload through `onDiagnosticsDataReceived(_:)` instead of uploading it. The callback can arrive asynchronously after `finishFlow` completes. Terminal SDK errors and user cancellation also finalize diagnostics automatically.

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

Use the diagnostics upload contract provided for your Incode server integration. If `sendDiagnosticsData` is `false`, the SDK does not produce this callback.

### Delete Local User Data

`deleteLocalUserData()` clears the current onboarding state and removes SDK-managed local capture files. Call it only after your app has copied or uploaded every capture it needs.

Use these terminal cleanup points:

- The `finishFlow` completion after a successful Capture-Only Sections flow.
- `onError(_:)` for a terminal flow error, excluding `.sectionAlreadyRunning`.
- `userCancelledSession()` after cancellation.

:::danger
Do not call `deleteLocalUserData()` while a section is active or before reading URL-backed capture results. Deleting local files is permanent.
:::

***

## Customization

Capture-Only mode uses the same theme, localization, tutorial, retake, and capture-quality options as the corresponding modules in other SDK modes. See [Customization](/sdk-reference/ios-customization/) and each supported [module page](/sdk-reference/ios-individual-modules/) for the available settings.

For related integration guidance, see [SDK Modes](/sdk-reference/ios-sdk-modes/), [Integration Approaches](/sdk-reference/ios-flow-configuration/), and [API Reference](/sdk-reference/ios-api-reference/).