# 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.4.2.
* **Web SDK:** 1.9.2.
* **Native SDKs (iOS / Android):** 8.22.

{% 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.4.2** or higher.
* Switch `Content-Type` of data you send to `application/octet-stream`.
* For Instant API, obtain private and public keys as described [here](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/ozcapsula-data-container).

#### Web SDK

* Upgrade to v**1.9.2** or higher.
* 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](https://doc.ozforensics.com/oz-knowledge/guides/sdk/oz-liveness-websdk/using-ozcapsula-data-container-in-web-sdk#api_use_session_token-client) for details).
* 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**8.22** or higher.
* 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="Container 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" %}

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

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

{% step %}
Delegate

{% tabs %}
{% tab title="Container 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" %}

```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="Container 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" %}

```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="Container 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" %}

```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 %}
Delegate

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

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

{% endtab %}

{% tab title="Old flow" %}

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

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

{% step %}
Launching Analysis

{% tabs %}
{% tab title="Container 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" %}

```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 %}

## Migration Checklist

* [x] API is updated to 6.4.2 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 8.22 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.2 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: 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:

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

The question should be specific, self-contained, and written in natural language.
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.
