> ## Documentation Index
> Fetch the complete documentation index at: https://developer.suki.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile SDK Interoperability

> Share one clinical note across Mobile SDK, Web SDK, and Ambient APIs with emrEncounterId, conflict handling, and note read APIs

<div className="quick-summary-wrapper">
  <div className="quick-summary-header">
    <span className="quick-summary-icon" aria-hidden="true" />

    <span className="quick-summary-title">Quick summary</span>
  </div>

  <div className="quick-summary-content">
    Ambient interoperability lets clinicians continue the same clinical note between the Mobile SDK, Web SDK, and Ambient APIs. Each product uses its own fields for the shared encounter values. In the Mobile SDK, use `SukiAmbientConstant.kEmrEncounterId` and, for re-ambient workflows, `SukiAmbientConstant.kSessionId`.
  </div>

  <div className="quick-summary-footer">
    <span className="quick-summary-footer-icon" aria-hidden="true" />

    <span className="quick-summary-footer-text">Last updated:</span>
    <span className="quick-summary-footer-date">August 2026</span>
  </div>
</div>

<Warning>
  * Interoperability applies only to **Ambient workflows**. It is not supported for Dictation or Form filling.
  * Only versions **`v2.7.0`** and later support interoperability for the Mobile SDK.
</Warning>

Ambient interoperability lets you use multiple Suki products that support ambient workflows with the same clinical note for a patient encounter.

Before ambient interoperability, each ambient session belonged to the Suki product where it was created. For example, if you started a session using the Mobile SDK, you could only continue or retrieve that session from the Mobile SDK.

With <Tooltip tip="Interoperability is the ability of different systems, devices, or software applications to communicate, exchange data, and use that information seamlessly." cta="View in Glossary" href="/Glossary/i">interoperability</Tooltip>, you can create and access ambient sessions across Ambient APIs, Mobile SDK, and Web SDK for the same patient encounter. The EMR encounter identifier makes the note interoperable. For re-ambient workflows, pass `SukiAmbientConstant.kSessionId` (maps to Ambient API `encounter_id`) and reuse it for every session on that note.

In the Mobile SDK, you can:

* Start an interoperable ambient session by passing `SukiAmbientConstant.kEmrEncounterId`.
* Continue in the Mobile SDK a clinical note that started in the Web SDK or Ambient APIs.
* Continue in the Web SDK or Ambient APIs a clinical note that started in the Mobile SDK.
* Clear a blocking session on another Suki product, then retry create.
* React when another Suki product cancels or ends the live session.
* List and read shared notes for an EMR encounter.

## How Ambient interoperability works in the Mobile SDK

When you call `createSession`, pass `SukiAmbientConstant.kEmrEncounterId` to identify the patient's EMR or EHR encounter and make the note interoperable. For re-ambient workflows, pass `SukiAmbientConstant.kSessionId` (maps to Ambient API `encounter_id`) and reuse the same value for every session on that note.

The table below shows how Mobile SDK identifiers map to the corresponding Ambient API fields.

| Mobile SDK                                                      | Ambient APIs                                 | Available                                           | Description                                                                                                                                                                     |
| --------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`SukiAmbientConstant.kEmrEncounterId`** (`"emrEncounterId"`)  | **`emr_encounter_id`**                       | Passed to `createSession`                           | Identifies the patient encounter in your EMR or EHR. Required to enable ambient interoperability.                                                                               |
| **`SukiAmbientConstant.kSessionId`** (`"sessionId"`)            | **`encounter_id`**                           | Passed to `createSession` for re-ambient workflows  | Groups re-ambient sessions for the same note. Reuse the same value for every session on that note.                                                                              |
| **`sessionId`** (create response)                               | **`ambient_session_id`**                     | Returned on every successful `createSession`        | Identifies the current ambient session. Use this value for recording controls and session-level content APIs. This is not the `encounter_id` you may pass as `kSessionId`.      |
| **`compositionId`** (create response)                           | **`composition_id`** (used as **`note_id`**) | Returned for online sessions only                   | Identifies the shared clinical note. Store this value for note-level read APIs. Offline `createSession` calls do not return `compositionId`.                                    |
| **`blockingSessionId`** (returned with `remoteSessionConflict`) | **`ambient_session_id`**                     | Returned only when a remote session conflict occurs | Identifies the active ambient session running in another Suki product. Pass this value to `cancelRemoteAmbientSession` or `endSessionRemotely` before retrying `createSession`. |

<Note>
  Keep these identifiers separate:

  * **Create response `sessionId`**: Maps to Ambient API `ambient_session_id`. Use it for recording and session-level APIs. It is not the `encounter_id` you pass as `kSessionId`.
  * **`kSessionId` / `encounter_id`**: Required for re-ambient workflows. Reuse the same value for every session on the note and as `encounterId` for `encounterContent`.
  * **`compositionId`**: The note id. Use it for note-level read APIs. It is not `encounter_id`.
</Note>

Ambient interoperability **shares the clinical note**, not the audio recording. Each ambient session adds content to the same note. The audio for each session stays tied to that session's `sessionId`.

<Warning>
  If you omit `SukiAmbientConstant.kEmrEncounterId` on `createSession`, the Mobile SDK still creates an ambient session, but the clinical note is not shared with other Suki products.
</Warning>

## Common integration patterns

Below are some common integration patterns for ambient interoperability that you can use as a reference for your implementation while building your ambient workflows using the Mobile SDK.

<CardGroup cols={2}>
  <Card title="Capture on Mobile, Review on Web" icon="mobile">
    Start the clinical note in the Mobile SDK, then open it in the Web SDK for review, edit, and submission. Use the shared encounter value through each product's corresponding field.
  </Card>

  <Card title="Capture on Web, Continue on Mobile" icon="arrow-right-arrow-left">
    Start the clinical note in the Web SDK, then continue it in the Mobile SDK. Use the shared encounter value through each product's corresponding field.
  </Card>

  <Card title="Capture on Mobile, Continue with APIs" icon="code">
    Start the clinical note in the Mobile SDK, then continue or retrieve it through Ambient APIs. Use the shared encounter values through each product's corresponding fields.
  </Card>

  <Card title="Capture with APIs, Continue on Mobile" icon="mobile-screen">
    Start the clinical note through Ambient APIs, then continue it in the Mobile SDK. Use the shared encounter values through each product's corresponding fields.
  </Card>

  <Card title="Resolve a Cross-Device Conflict" icon="triangle-exclamation">
    On `remoteSessionConflict`, cancel or end the blocking session with `blockingSessionId`, then retry create.
  </Card>

  <Card title="Keep the UI in Sync on Peer End" icon="bell">
    Handle `sessionTerminatedByPeer` to clear recording UI when another product ends the live session.
  </Card>

  <Card title="List Notes for the Visit" icon="list">
    Call `listEncounterNotes` with the EMR encounter id, then call `getNoteContent(noteId:)` (or context / structured data) with `compositionId` as `noteId`.
  </Card>
</CardGroup>

## How to enable interoperability in the Mobile SDK

Pass a stable EMR encounter UUID to make the note interoperable. For re-ambient workflows, pass `SukiAmbientConstant.kSessionId` (Ambient API `encounter_id`) and reuse the same value for every session on that note.

<Steps>
  <Step title="Pass the EMR Encounter ID">
    Set **`SukiAmbientConstant.kEmrEncounterId`** to the **`emr_encounter_id`** returned by the Ambient API.
  </Step>

  <Step title="Pass encounter_id">
    Set **`SukiAmbientConstant.kSessionId`** to the Ambient API **`encounter_id`** on the first session in a re-ambient workflow. Store and reuse the same value for every session on that note and for `encounterContent`. Do not use **`compositionId`** as this value.
  </Step>

  <Step title="Store the Create Response">
    On success, store **`sessionId`** and **`compositionId`** when present. Use **`sessionId`** for recording controls and session-level content APIs. Use **`compositionId`** as **`noteId`** for note-level read APIs.
  </Step>

  <Step title="Continue Across Products">
    Use the shared encounter values through the target product's corresponding fields. In the Mobile SDK, continue using `SukiAmbientConstant.kEmrEncounterId` and `SukiAmbientConstant.kSessionId`. For more information, see [Use ambient across modalities](/documentation/how-to/ambient-clinical-notes/use-ambient-across-modalities).
  </Step>
</Steps>

### Code example for creating an interoperable session

Use the following code example to create an interoperable ambient session in the Mobile SDK.

```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
let sessionInfo: [String: AnyHashable] = [
    SukiAmbientConstant.kEmrEncounterId: "6ec3920f-b0b1-499d-a4e9-889bf788e5ab",
    SukiAmbientConstant.kSessionId: existingEncounterId,
]

SukiAmbientCoreManager.shared.createSession(with: sessionInfo, onCompletion: { result in
    switch result {
    case .success(let sessionResponse):
        let sessionId = sessionResponse.sessionId
        let compositionId = sessionResponse.compositionId // optional; online only
        // Store sessionId for recording and session content.
        // Store compositionId for note-level reads.
    case .failure(let error):
        // Handle remoteSessionConflict, SukiStatus, or other create errors.
        print(error)
    }
})
```

## How to handle a remote session conflict

A `createSession` request fails with `SukiAmbientCoreError.remoteSessionConflict(blockingSessionId:)` when another Suki product already has an active ambient session for the same provider and EMR encounter.

To create a new session:

1. Read the `blockingSessionId` from the error. This value identifies the active ambient session and maps to `ambient_session_id`.
2. Clear the blocking session by calling one of the following APIs with the `blockingSessionId`:.
   * `cancelRemoteAmbientSession(ambientSessionId:onCompletion:)` to cancel the remote session and discard it.
   * `endSessionRemotely(sessionId:onCompletion:)` to end the remote session so any audio already captured can be processed.
3. After the remote API call succeeds, retry `createSession` using the same `SukiAmbientConstant.kEmrEncounterId`.

The SDK does not require a delay between the successful remote operation and retrying `createSession`.

<Note>
  `remoteSessionConflict` only applies when create runs online. If the first ambient session is fully offline, another Suki product can still start a session for the same EMR encounter, and you may not receive a conflict error.
</Note>

### Code example for handling a remote session conflict

Use the following code example to handle a remote session conflict in the Mobile SDK.

```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
SukiAmbientCoreManager.shared.createSession(with: sessionInfo, onCompletion: { result in
    switch result {
    case .success(let sessionResponse):
        // Proceed with recording using sessionResponse.sessionId
        break
    case .failure(let error):
        if let coreError = error as? SukiAmbientCoreError,
           case .remoteSessionConflict(let blockingSessionId) = coreError {
            // CancelSession for the other modality's active session:
            SukiAmbientCoreManager.shared.cancelRemoteAmbientSession(
                ambientSessionId: blockingSessionId,
                onCompletion: { cancelResult in
                    // After success, retry createSession with the same kEmrEncounterId.
                }
            )
            // Or EndSession:
            // SukiAmbientCoreManager.shared.endSessionRemotely(
            //     sessionId: blockingSessionId,
            //     onCompletion: { endResult in /* then retry create */ }
            // )
            return
        }
        print(error)
    }
})
```

<Warning>
  * `remoteSessionConflict` is a server-side interoperability conflict. It can occur when you pass an EMR encounter id and another Suki product already holds the active ambient session.
  * `sessionInProgress` is a local conflict only. An ambient session is already active on the current device. End or cancel that session locally before calling `createSession` again. An EMR encounter id is not required for this case.
  * Use `cancelRemoteAmbientSession` when you want to discard the blocking session. Use `endSessionRemotely` when you want the blocking session to end and process any audio already captured.
</Warning>

## What happens when a peer terminates the live session

If another Suki product cancels or ends the ambient session that is currently live in your app, the SDK tears down the session locally before it notifies your app. The SDK:

* Stops the microphone.
* Deletes local audio for the session.
* Closes the stream.
* Clears the local session state.

Then it delivers `SessionEvent.sessionTerminatedByPeer(reason:)` with `.cancelled` or `.ended`.

Update your UI only. Do not call local `end()`, `cancel()`, or `clear()`. Teardown is already complete. Do not call `cancelRemoteAmbientSession` or `endSessionRemotely` for this direction.

### Code example for handling a peer end event

Use the following code example to handle a peer end event in the Mobile SDK.

```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
case .sessionTerminatedByPeer(let reason):
    switch reason {
    case .cancelled, .ended, .unknown:
        // Update UI only. Local mic, stream, and session are already cleared.
        break
    }
```

For more information, see [Session events and delegates](/mobile-sdk/ambient-guides/events-and-delegates).

## How to read shared notes

To list or retrieve clinical notes shared across Suki products, use the note read APIs. Depending on the API, pass one of the following:

* An EMR encounter id.
* A `compositionId` as the `noteId`.
* The Ambient API `encounter_id` (the same value you pass as `kSessionId`) as `encounterId` for `encounterContent`.

All of these APIs are **asynchronous**, and their completion handlers are invoked on the **main queue**. Errors are returned as generic or gRPC errors, not `SukiStatus`. These APIs use the same token provider configured for the ambient session APIs.

| Method                                    | Pass                                        | Returns                                                |
| ----------------------------------------- | ------------------------------------------- | ------------------------------------------------------ |
| **`listEncounterNotes(emrEncounterId:)`** | EMR encounter id                            | `[EncounterCompositionSummary]` (`compositionId` only) |
| **`getNoteContext(noteId:)`**             | `compositionId` as `noteId`                 | `NoteLevelContext`                                     |
| **`getNoteContent(noteId:)`**             | `compositionId` as `noteId`                 | `[Suggestion]`                                         |
| **`getNoteStructuredData(noteId:)`**      | `compositionId` as `noteId`                 | `StructuredDataResponseModel`                          |
| **`encounterContent(encounterId:)`**      | Ambient API `encounter_id` as `encounterId` | `[Suggestion]`                                         |

<Note>
  For `encounterContent`, pass the Ambient API `encounter_id` (the same value you pass as `kSessionId`). Do not pass the EMR encounter id.
</Note>

### Code example for reading shared notes

Use the following code example to read shared notes in the Mobile SDK.

```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
SukiAmbientCoreManager.shared.listEncounterNotes(emrEncounterId: emrEncounterId) { result in
    switch result {
    case .success(let summaries):
        for summary in summaries {
            let noteId = summary.compositionId
            SukiAmbientCoreManager.shared.getNoteContext(noteId: noteId) { contextResult in
                switch contextResult {
                case .success(let context):
                    print(context)
                case .failure(let error):
                    print(error)
                }
            }
        }
    case .failure(let error):
        print(error)
    }
}

// Pass Ambient API encounter_id (kSessionId) as encounterId:
SukiAmbientCoreManager.shared.encounterContent(encounterId: encounterId) { result in
    switch result {
    case .success(let suggestions):
        print(suggestions)
    case .failure(let error):
        print(error)
    }
}
```

`status(for:)`, `content(for:)`, `transcript(for:)`, and `getStructuredData(for:)` still take `sessionId` and return status or content for a single ambient session. Use the note read APIs above when you list or retrieve clinical notes shared across Suki products.

For full method details and signatures, see [Session status and content retrieval](/mobile-sdk/ambient-guides/session-status-and-content-retrieval#shared-notes-across-products).

## Create errors that do not fall back to offline

These create failures return **`SukiStatus`** and do **not** create an offline session:

| Status                    | Typical code | Example situation                                                     |
| ------------------------- | ------------ | --------------------------------------------------------------------- |
| **`FAILED_PRECONDITION`** | `9`          | Re-ambient on a submitted note (exact message comes from the backend) |
| **`INVALID_ARGUMENT`**    | `3`          | Bad encounter UUID (exact message comes from the backend)             |

Other create failures can still follow the existing offline workflow. For more information, see [Offline mode](/mobile-sdk/ambient-guides/offline-mode).

## Available cookbooks

<div className="hp-io-method-grid tut-hub-card-grid" data-cookbook-related-grid>
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/cookbooks/pass-emr-encounter-id">
    <div className="tut-hub-card-media" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <div className="tut-hub-card-badges">
        <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
        <span className="hp-wn-badge cookbook-hub-badge-surface cookbook-hub-badge-surface--api">API</span>
      </div>

      <h3 className="hp-io-method-card-title">Share One Note Across Products</h3>

      <p className="hp-io-method-card-desc cookbook-hub-card-desc">
        Share one note with emr\_encounter\_id.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="5 min">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">5 min</span>
        </div>
      </div>
    </div>
  </a>

  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/cookbooks/seed-patient-context-for-web-sdk">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <div className="tut-hub-card-badges">
        <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
        <span className="hp-wn-badge cookbook-hub-badge-surface cookbook-hub-badge-surface--sdk">Web SDK</span>
      </div>

      <h3 className="hp-io-method-card-title">Seed Patient Context for the Web SDK</h3>

      <p className="hp-io-method-card-desc cookbook-hub-card-desc">
        Seed patient context for Web SDK.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="5 min">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">5 min</span>
        </div>
      </div>
    </div>
  </a>
</div>

## Next steps

<Icon icon="file-lines" iconType="solid" /> Refer to [Create ambient session](/mobile-sdk/ambient-guides/create-session) for session info parameters and create response fields.

<Icon icon="file-lines" iconType="solid" /> Refer to [Ambient interoperability](/documentation/concepts/ambient-clinical-notes/ambient-interoperability) for the platform overview across ambient products.

<Icon icon="file-lines" iconType="solid" /> Refer to [Interoperable identifiers](/documentation/concepts/ambient-clinical-notes/interoperable-identifiers) for `emr_encounter_id`, `encounter_id`, and `composition_id`.

<Icon icon="file-lines" iconType="solid" /> Refer to [Use interoperable ambient notes](/documentation/how-to/ambient-clinical-notes/use-ambient-across-modalities) for the end-to-end cross-modality workflow.

<Icon icon="file-lines" iconType="solid" /> Refer to [Web SDK interoperability](/web-sdk/guides/ambient-interoperability) when the shared note opens in the headed Web SDK.

<Icon icon="file-lines" iconType="solid" /> Refer to [Seed patient context for Web SDK](/documentation/cookbooks/seed-patient-context-for-web-sdk) if a Mobile or API session will open later in the headed Web SDK.
