> For the complete documentation index, see [llms.txt](https://doc.ozforensics.com/oz-knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/migration-guides/migration-to-ozcapsula.md).

# Migration to OzCapsula

This guide describes the migration to the latest OzCapsula architecture: the approach where you use an encrypted container to securely transmit data between frontend and backend.

## Component version requirements <a href="#id-2.-minimal-supported-versions" id="id-2.-minimal-supported-versions"></a>

Before starting the migration, ensure that all components are updated to the minimum required versions:

* API: **6.6.1**.
* Web SDK: **1.9.10**.
* Native SDKs (iOS / Android) and Flutter: **10.0.0**.

We highly recommend using the latest versions of Oz software.

{% hint style="danger" %}
Older versions are **not compatible** with the new capture and analysis flow.
{% endhint %}

{% hint style="info" %}
For best results, migrate all components simultaneously and avoid partial upgrades.
{% endhint %}

## Authentication changes <a href="#id-3.-authentication-changes" id="id-3.-authentication-changes"></a>

All capture sessions now require a `session_token` issued by the backend.

* The token must be obtained **before** starting video capture.
* It is tied to the current session and the specific container.
* The token has a limited lifetime.

### **Migration actions**

* Implement a backend call to obtain `session_token`.
* Pass the token explicitly when creating the capture screen.

## Flow changes <a href="#id-4.-high-level-flow-changes" id="id-4.-high-level-flow-changes"></a>

### Before container (legacy flow) <a href="#before-migration-legacy-flow" id="before-migration-legacy-flow"></a>

1. You launch media capture.
2. Media is captured.
3. Media along with required data is sent to Oz API (using your backend as intermediate if needed).

### With container (new flow) <a href="#after-migration-new-flow" id="after-migration-new-flow"></a>

1. You request `session_token` from backend.
2. You put additional data like metadata (if needed) into container and launch video capture with `session_token`.
3. SDK captures media, packages it into container, and returns an encrypted file.
4. The encrypted file is sent to Oz API (using your backend as intermediate if needed).

### Migration actions

#### API

* Upgrade to v**6.6.1** or newer.
* Switch `Content-Type` of data you send to `application/octet-stream`.
* For Instant API, obtain private and public keys as described [here](/oz-knowledge/guides/developer-guide/api/oz-api/ozcapsula-data-container.md).

#### Web SDK

* Upgrade to v**1.9.10** or newer.
* Ensure backend supports `session_token`.
* In the configuration file, set `use_wasm_container` to true and `api_use_session_token` to `api` or `client` (please refer to [this article](/oz-knowledge/guides/developer-guide/sdk/oz-liveness-websdk/using-ozcapsula-data-container-in-web-sdk.md#api_use_session_token-client) for details).

{% hint style="info" %}
This mode is intended for development and testing only. For production environments, use `api_use_session_token: "client"`.
{% endhint %}

* Update initialization to pass token explicitly.
* If you use the `capture` architecture type, ensure you receive and send to your backend and then to us a **blob** object (`application/octet-stream`).

#### Mobile SDKs

* Upgrade to v**10.0.0** or newer.
* Ensure backend supports `session_token`.
* Implement new interfaces as described below.

{% hint style="info" %}
Please note: do not use "\_" (underscore) in file names for the container flow.
{% endhint %}

#### Android

{% stepper %}
{% step %}
Launching capture screen

{% tabs %}
{% tab title="OzCapsula flow" %}

```kotlin
val sessionToken: String = getSessionToken()
val captureRequest = CaptureRequest(
    listOf(
        AnalysisProfile(
            Analysis.Type.QUALITY,
            listOf(MediaRequest.ActionMedia(OzAction.Blank)),
        )
    )
)
val intent = OzLivenessSDK.createMediaCaptureScreen(captureRequest, sessionToken)
startActivityForResult(intent, REQUEST_LIVENESS_CONTAINER)
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```kotlin
val intent = OzLivenessSDK.createStartIntent(listOf(OzAction.Blank))
startActivityForResult(intent, REQUEST_LIVENESS_MEDIA)
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
Subscription to media

{% tabs %}
{% tab title="OzCapsula flow" %}

```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_LIVENESS_CONTAINER) {
        when (resultCode) {
            OzLivenessResultCode.SUCCESS -> runAnalysis(OzLivenessSDK.getContainerFromIntent(data))
            OzLivenessResultCode.USER_CLOSED_LIVENESS -> { /* user closed the screen */ }
            else -> {
                val errorMessage = OzLivenessSDK.getErrorFromIntent(data)
                /* show error */
            }
        }
    }
}
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_LIVENESS_MEDIA) {
        when (resultCode) {
            OzLivenessResultCode.SUCCESS -> runAnalysis(OzLivenessSDK.getResultFromIntent(data))
            OzLivenessResultCode.USER_CLOSED_LIVENESS -> { /* user closed the screen */ }
            else -> {
                val errorMessage = OzLivenessSDK.getErrorFromIntent(data)
                /* show error */
            }
        }
    }
}
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
Launching analysis

{% tabs %}
{% tab title="OzCapsula flow" %}

```kotlin
private fun runAnalysis(container: DataContainer?) {
    if (container == null) return

    AnalysisRequest.Builder()
        .addContainer(container)
        .build()
        .run(
            { result ->
                val isSuccess = result.analysisResults.all { it.resolution == Resolution.SUCCESS }
            },
            { /* show error */ },
            { /* update status */ },
        )
}
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```kotlin
private fun runAnalysis(media: List<OzAbstractMedia>?) {
    if (media.isNullOrEmpty()) return
    AnalysisRequest.Builder()
        .addAnalysis(Analysis(Analysis.Type.QUALITY, Analysis.Mode.SERVER_BASED, media))
        .build()
        .run(
            { result ->
                val isSuccess = result.analysisResults.all { it.resolution == Resolution.SUCCESS }
            },
            { /* show error */ },
            { /* update status */ },
        )
}
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

#### iOS

{% stepper %}
{% step %}
Launching capture screen

{% tabs %}
{% tab title="OzCapsula flow" %}

```swift
getSessionToken() { sessionToken in
            DispatchQueue.main.async {
                do {
                    let action:OZVerificationMovement = .selfie
                    let mediaRequest = MediaRequest.action(action)
                    let profile = AnalysisProfile(mediaList: [mediaRequest],
                                                  type: .quality,
                                                  params: [:] )
                    let request = CaptureRequest(analysisProfileList: [profile], cameraPosition: .front)
                    let ozLivenessVC = try OZSDK.createMediaCaptureScreen(self, request, sessionToken: sessionToken)
                    self.present(ozLivenessVC, animated: true)
                } catch let error {
                    print(error.localizedDescription)
                }
            }
        }
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```swift
do {
    let ozLivenessVC : UIViewController = try OZSDK.createVerificationVCWithDelegate(self, actions: .selfie)   
    self.present(ozLivenessVC, animated: true)
} catch let error {
    print(error.localizedDescription)
}
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
Subscription to media

{% tabs %}
{% tab title="OzCapsula flow" %}

```swift
extension ViewController: LivenessDelegate {
    func onResult(container: DataContainer) {
    }
    
    func onError(status: OZVerificationStatus?) {
    }
}
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```swift
extension ViewController: OZLivenessDelegate {
    func onOZLivenessResult(results: [OZMedia]) {
    }
  
    func onError(status: OZVerificationStatus?) {
    }
}
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
Launching analysis

{% tabs %}
{% tab title="OzCapsula flow" %}

```swift
func onResult(container: DataContainer) {
  let analysisRequest = AnalysisRequestBuilder()
  analysisRequest.addContainer(container)
  analysisRequest.run(
            statusHandler: { status in
            },
            errorHandler: { error in
            }
        ) { result in
            
        }
}
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```swift
func onResult(results: [OZMedia]) {
        let analysisRequest = AnalysisRequestBuilder()
        let analysis = Analysis(media: results,
                                type: .quality,
                                mode: .serverBased,
                                params: nil)
        analysisRequest.addAnalysis(analysis)
        analysisRequest.run { status in
        } errorHandler: { error in
        } completionHandler: { results in
            
        }
    }
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

#### Flutter

{% stepper %}
{% step %}
Launching capture screen

{% tabs %}
{% tab title="OzCapsula flow" %}

```dart
final sessionToken = await _getSessionToken();
OZSDK.createMediaCaptureScreen(
  CaptureRequest(
    analysisProfileList: [
      AnalysisProfile(
        Type.quality,
        mediaList: [ActionMedia(VerificationAction.blank)]
      ),
    ],
  ),
  sessionToken,
);
```

{% endtab %}

{% tab title="Old flow (before container)" %}

```dart
OZSDK.startLiveness(<list of actions>);
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
Subscription to media

{% tabs %}
{% tab title="OzCapsula flow" %}

```dart
@override
void initState() {
  super.initState();
  _subscription = OZSDK.livenessResult.listen((result) {
    if (result is DataContainer) {
      // OzCapsula media
    }
  }, onError: (Object error) {
    // handle error, in most cases PlatformException
  });
}

```

{% endtab %}

{% tab title="Old flow (before container)" %}

```dart
@override
void initState() {
  super.initState();
  _subscription = OZSDK.livenessResult.listen((result) {
    if (result is List) {
      final mediaList = result.toList().cast<Media>();
    }
  }, onError: (Object error) {
    // handle error, in most cases PlatformException
  });
}
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
Launching analysis

{% tabs %}
{% tab title="OzCapsula flow" %}

```dart
try {
  final Uint8List? containerBytes = await OZSDK.getContainerBytes(dataContainer);
  final List<RequestResult> analysisResult = await OZSDK.analyze(
      analysis: [],
      dataContainer: dataContainer,
    );
} catch (e) {
  _showErrorDialog(e);
}
```

{% endtab %}

{% tab title="Old flow" %}

```dart
List<Analysis> analysis = [ Analysis(Type.quality, Mode.serverBased, <media>, {}), ];
final analysisResult = await OZSDK.analyze(analysis, [], {});

```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## Migration checklist

* [x] API is updated to 6.6.1 or newer, with `session_token` supported.
  * [x] `Content-Type` is changed to `application/octet-stream`.
  * [x] For Instant API, private and public keys are obtained.
* [x] Mobile SDKs are updated to 10.0.0 or newer.
  * [x] Added the `session_token` backend call.
  * [x] New public interface is implemented: `createMediaCaptureScreen` with `CaptureRequest`.
* [x] Web SDK is updated to 1.9.10 or newer.
  * [x] `use_wasm_container` is set to `true`.
  * [x] `api_use_session_token` is set to `api` or `client`.
  * [x] If `api_use_session_token` = `client`, `session_token` is requested before session and passed in `open.options()`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/migration-guides/migration-to-ozcapsula.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
