> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Session Replay API

> List, fetch, and purge async session-replay recordings of a customer's web session, and manage the capture-time privacy masking policy.

# Session Replay API

Session replay is the asynchronous twin of live co-browse. The native chat widget records the visitor's web session (a serialized DOM snapshot plus incremental rrweb mutation events) and stores it against the conversation. A support agent plays it back later — in the inbox, with an rrweb player — to see exactly what the customer hit, after the fact rather than in real time.

This page documents the operator half: listing recordings, fetching one for playback, purging one for a privacy request, and setting the capture-time masking policy. The visitor capture endpoints (how the widget uploads events) are internal to the widget runtime and not part of this surface.

**Base path:** `/api/v1/session-replay`

**Authentication:** API key (`X-API-Key`) or session JWT.

**Scopes & roles:**

* Reads (`GET`) require the `conversations:read` scope.
* Writes — `DELETE` a recording and `PUT` the privacy policy — require the `conversations:write` scope and an `owner`, `admin`, or `developer` role.

| Method   | Path                                      | Purpose                                               |
| -------- | ----------------------------------------- | ----------------------------------------------------- |
| `GET`    | `/api/v1/session-replay`                  | List recent recordings (summaries, no event payloads) |
| `GET`    | `/api/v1/session-replay/{conversationId}` | Fetch the full recording for playback                 |
| `DELETE` | `/api/v1/session-replay/{conversationId}` | Purge a recording (privacy / right-to-erasure)        |
| `GET`    | `/api/v1/session-replay/privacy`          | Get the capture-time privacy masking policy           |
| `PUT`    | `/api/v1/session-replay/privacy`          | Update the capture-time privacy masking policy        |

A recording is keyed by its `conversationId` — the conversation the session was captured against. Recordings are tenant-scoped: you only ever see the recordings captured for your own account.

## List recordings

Returns the most recent recordings across your conversations as lightweight summaries — the event payloads are omitted so the list stays cheap. The inbox "Session replays" panel uses this to let an agent pick one to play back. The newest 50 recordings are returned, newest first.

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/session-replay \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

```json theme={null}
{
  "data": {
    "recordings": [
      {
        "conversation_id": "conv_abc123",
        "recording_id": "rec_9f2a",
        "status": "ended",
        "visitor_id": "vis_77c1",
        "start_url": "https://shop.example.com/checkout",
        "started_at": "2026-06-18T14:02:11.000Z",
        "ended_at": "2026-06-18T14:07:53.000Z",
        "event_count": 1840,
        "size_bytes": 612344,
        "truncated": false,
        "offloaded": false,
        "total_event_count": 1840,
        "total_size_bytes": 612344
      }
    ]
  },
  "meta": {
    "request_id": "req_...",
    "timestamp": "2026-06-18T14:09:00.000Z"
  }
}
```

`status` is one of `recording` (still capturing), `ended` (the visitor finished or navigated away), or `capped` (an abuse backstop stopped capture). `offloaded` is `true` when a long session spilled past the inline head into durable storage; `total_event_count` and `total_size_bytes` describe the full recording across that storage, while `event_count` and `size_bytes` describe only the inline head. `truncated` is `true` only when the abuse backstop was hit.

## Fetch a recording for playback

Returns the full rrweb event stream for one conversation. For a long recording that was offloaded, the events held in durable storage are reassembled with the inline head so the stream plays end to end. Feed `session_replay.events` to an rrweb player to replay the session.

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/session-replay/conv_abc123 \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

```json theme={null}
{
  "data": {
    "session_replay": {
      "recording_id": "rec_9f2a",
      "status": "ended",
      "visitor_id": "vis_77c1",
      "start_url": "https://shop.example.com/checkout",
      "started_at": "2026-06-18T14:02:11.000Z",
      "ended_at": "2026-06-18T14:07:53.000Z",
      "event_count": 1840,
      "size_bytes": 612344,
      "truncated": false,
      "offloaded": false,
      "total_event_count": 1840,
      "total_size_bytes": 612344,
      "events": [
        { "type": 4, "data": { "href": "https://shop.example.com/checkout" }, "timestamp": 1781792531000 }
      ]
    }
  },
  "meta": { "request_id": "req_...", "timestamp": "2026-06-18T14:09:30.000Z" }
}
```

`session_replay` is `null` when the conversation exists but carries no recording. Requesting a conversation that doesn't exist in your account returns `404 NOT_FOUND`. A malformed `conversationId` returns `422 VALIDATION_ERROR`.

Each event in `events` is a raw rrweb event — `type` (the rrweb event type), `data` (the opaque rrweb payload, passed straight through to the player), and `timestamp` (epoch milliseconds). Orbit never interprets the payload; it only stores and replays it.

## Delete a recording

Removes the recording from the conversation. The conversation and its messages are untouched — only the replay is erased, including any events held in durable storage. The call is idempotent, so a privacy or right-to-erasure workflow can retry it safely. Returns `404 NOT_FOUND` if the conversation isn't in your account.

```bash theme={null}
curl -X DELETE https://api.orbit.devotel.io/api/v1/session-replay/conv_abc123 \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

```json theme={null}
{
  "data": { "deleted": true },
  "meta": { "request_id": "req_...", "timestamp": "2026-06-18T14:10:00.000Z" }
}
```

## Privacy masking policy

Session replay records the visitor's DOM, which can include data the visitor typed. The masking policy decides what the capture client redacts **in the visitor's browser, before any events are uploaded** — so masked content never leaves the page. The policy is account-wide and reaches new capture sessions on the next widget session bootstrap; it does not touch existing recordings or the delete path.

The default posture is fail-closed: with no policy configured, every input value is masked and built-in PII heuristics are on. You must explicitly relax masking.

| Field                 | Type      | Default | Meaning                                                                                                                             |
| --------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `mask_all_inputs`     | boolean   | `true`  | Mask every `<input>`, `<textarea>`, and `<select>` value before upload.                                                             |
| `mask_text_selectors` | string\[] | `[]`    | CSS selectors whose visible text is replaced with asterisks — for rendered free-text PII that isn't an input.                       |
| `block_selectors`     | string\[] | `[]`    | CSS selectors whose subtree is never serialized at all — a hard cut for highly sensitive regions like payment iframes or ID images. |
| `mask_pii_default`    | boolean   | `true`  | Apply the recorder's built-in heuristic masking for elements marked sensitive (the `.rr-mask` class or `data-rr-mask` attribute).   |

Each selector entry is a single CSS selector, trimmed and non-empty (max 200 characters); up to 200 entries are allowed in each selector list.

### Get the policy

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/session-replay/privacy \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

```json theme={null}
{
  "data": {
    "privacy": {
      "mask_all_inputs": true,
      "mask_text_selectors": [],
      "block_selectors": [],
      "mask_pii_default": true
    }
  },
  "meta": { "request_id": "req_...", "timestamp": "2026-06-18T14:11:00.000Z" }
}
```

### Update the policy

```bash theme={null}
curl -X PUT https://api.orbit.devotel.io/api/v1/session-replay/privacy \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "mask_all_inputs": true,
    "mask_text_selectors": [".account-number"],
    "block_selectors": ["#payment-iframe"],
    "mask_pii_default": true
  }'
```

The response echoes the stored policy. Every field is optional and defaults to the fail-closed value, so a partial body only relaxes what you name. An invalid body returns `422 VALIDATION_ERROR`.

## See also

* [Conversations API](/api-reference/conversations) — the threads recordings attach to
* [Inbox API](/api-reference/inbox) — where agents play recordings back
* [Error codes](/reference/error-codes)
