> For the complete documentation index, see [llms.txt](https://docs.sudoplatform.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sudoplatform.com/guides/privacy-interaction/subscriptions.md).

# Subscriptions

Rather than polling for updates, you can subscribe to events and react as scanning progresses, data holders are discovered, and analysis results are updated.

### Virtual Presence Subscriptions <a href="#virtual-presence-subscriptions" id="virtual-presence-subscriptions"></a>

Subscribe to virtual presence state changes (e.g., transitions to `SCANNING`, `NEEDS_REAUTH`, or `INACTIVE`). Provide a unique subscription id to safely manage the subscription lifecycle.&#x20;

#### Subscribe <a href="#subscribe" id="subscribe"></a>

{% tabs %}
{% tab title="Typescript" %}

```typescript
await privacyInteractionClient.subscribeToVirtualPresence(
  'my-subscription-id',
  {
    virtualPresenceUpdated(virtualPresence) {
      console.log('Virtual presence updated:', virtualPresence.id, virtualPresence.state)
    },
    connectionStatusChanged(state) {
      console.log('Connection state:', state)
      if (state === ConnectionState.Disconnected) {
        // Handle disconnection: consider re-subscribing
      }
    },
  },
)
```

{% endtab %}
{% endtabs %}

The subscriber receives a full `VirtualPresence` object on each update.

#### Unsubscribe <a href="#unsubscribe" id="unsubscribe"></a>

{% tabs %}
{% tab title="Typescript" %}

```typescript
privacyInteractionClient.unsubscribeFromVirtualPresence('my-subscription-id')
```

{% endtab %}
{% endtabs %}

### Data Holder Subscriptions <a href="#data-holder-subscriptions" id="data-holder-subscriptions"></a>

Subscribe to data holder updates. Provide a unique subscription id to safely manage the subscription lifecycle.&#x20;

Notifications are emitted as data holders are discovered or updated during scanning. Updates are delivered in batches.&#x20;

#### Subscribe <a href="#subscribe-1" id="subscribe-1"></a>

{% tabs %}
{% tab title="Typescript" %}

```typescript
await privacyInteractionClient.subscribeToDataHolders(
  'my-subscription-id',
  {
    dataHoldersUpdated(dataHolders) {
      console.log(`${dataHolders.length} data holders updated`)
      for (const dh of dataHolders) {
        console.log(dh.name, dh.protectionState)
      }
    },
    connectionStatusChanged(state) {
      console.log('Connection state:', state)
    },
  },
)
```

{% endtab %}
{% endtabs %}

The subscriber receives an array of `DataHolder` objects on each update batch.

#### Unsubscribe <a href="#unsubscribe-1" id="unsubscribe-1"></a>

{% tabs %}
{% tab title="Typescript" %}

```typescript
privacyInteractionClient.unsubscribeFromDataHolders('my-subscription-id')
```

{% endtab %}
{% endtabs %}

### Analysis Result Subscriptions <a href="#analysis-result-subscriptions" id="analysis-result-subscriptions"></a>

Subscribe to analysis result changes. Provide a unique subscription id to safely manage the subscription lifecycle.&#x20;

Notifications are emitted when a new analysis result is created, or when an analysis result changes status (e.g., from `PENDING` to `COMPLETE`) or is updated with new data.

#### Subscribe <a href="#subscribe-2" id="subscribe-2"></a>

{% tabs %}
{% tab title="Typescript" %}

```typescript
await privacyInteractionClient.subscribeToAnalysisResult(
  'my-subscription-id',
  {
    analysisResultUpdated(update) {
      console.log('Analysis result updated:', update.id, update.status)
      if (update.status === 'COMPLETE') {
        // Fetch the full analysis result to get the data payload
        const full = await privacyInteractionClient.getAnalysisResult(update.id)
      }
    },
    connectionStatusChanged(state) {
      console.log('Connection state:', state)
    },
  },
)
```

{% endtab %}
{% endtabs %}

The subscriber receives an `AnalysisResultUpdate` object; a lightweight notification containing the analysis result's metadata and status but not the full `data` payload. To retrieve the complete analysis data, call `getAnalysisResult` with the update's `id`.

#### Unsubscribe <a href="#unsubscribe-2" id="unsubscribe-2"></a>

{% tabs %}
{% tab title="Typescript" %}

```typescript
privacyInteractionClient.unsubscribeFromAnalysisResult('my-subscription-id')
```

{% endtab %}
{% endtabs %}

### Connection State <a href="#connection-state" id="connection-state"></a>

All subscription interfaces include a `connectionStatusChanged` callback that notifies you when the subscription connection state changes. Your subscriber will not receive resource update events until the connection state is `CONNECTED`, and will stop receiving them when it transitions to `DISCONNECTED`.

| State          | Description                                                   |
| -------------- | ------------------------------------------------------------- |
| `CONNECTED`    | The subscription is actively connected and receiving updates. |
| `DISCONNECTED` | The subscription is not connected.                            |

### Subscription Best Practices <a href="#subscription-best-practices" id="subscription-best-practices"></a>

* **Use unique subscription IDs.** If you call a subscribe method with the same ID twice, the second call will replace the first subscription.
* **Handle disconnections.** Monitor the `connectionStatusChanged` callback and re-subscribe if the connection is lost unexpectedly.
* **Unsubscribe when done.** Always unsubscribe when your component unmounts or the user navigates away to avoid memory leaks and unnecessary network activity.
* **Keep callbacks lightweight.** Subscription callbacks are invoked on the main thread. Perform heavy processing asynchronously to avoid blocking the UI.
* **Fetch full data on update.** For analysis results, the subscription notification is lightweight. Fetch the full resource via `getAnalysisResult` when you need the complete data payload.
