> ## 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.

# Co-browse API

> Read, join, and end a live co-browse session so a support agent can view — and, with the visitor's consent, guide — a customer's browser session in real time.

# Co-browse API

Co-browse is the real-time twin of [session replay](/api-reference/session-replay). When a customer is stuck on your site, they start a co-browse session from the native chat widget; a support agent then joins from the inbox to watch the customer's live page and, when the customer grants control, highlight elements, scroll, and assist with form fields. The page DOM is mirrored over a data channel — it is not a screen share, so off-screen content the agent was never shown is never transmitted, and the visitor can revoke at any time.

This page documents the operator half: reading the session state, joining the live session, and ending it. The visitor starts and ends co-browse from the widget runtime — those calls are authenticated with the visitor's widget token and are not part of this API key surface.

**Base path:** `/api/v1/cobrowse`

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

**Scopes & roles:**

* Reads (`GET`) require the `conversations:read` scope.
* Writes — joining and ending a session — require the `conversations:write` scope and an `owner`, `admin`, or `developer` role.

| Method | Path                                     | Purpose                                       |
| ------ | ---------------------------------------- | --------------------------------------------- |
| `GET`  | `/api/v1/cobrowse/{conversationId}`      | Read the current co-browse session state      |
| `POST` | `/api/v1/cobrowse/{conversationId}/join` | Join the live session and mint an agent token |
| `POST` | `/api/v1/cobrowse/{conversationId}/end`  | End the session from the agent side           |

A session is keyed by its `conversationId` — the conversation it belongs to. Sessions are tenant-scoped: you only ever see sessions on your own account's conversations.

## The session object

Every endpoint returns the current session under `cobrowse`, or `null` when the conversation has never started one.

| Field           | Type           | Meaning                                                                                                                                                  |
| --------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session_id`    | string         | Identifier for this co-browse session.                                                                                                                   |
| `status`        | string         | `requested` (the visitor started it, no agent yet), `active` (an agent has joined), or `ended`.                                                          |
| `room_name`     | string         | The data-channel room both parties join. Bound to the account, conversation, and session, so a token can never be replayed against another conversation. |
| `visitor_id`    | string \| null | The widget visitor that initiated the session.                                                                                                           |
| `control`       | boolean        | The visitor's standing consent for agent-side guidance. When `false`, an agent can only observe.                                                         |
| `requested_at`  | string         | ISO 8601 timestamp the visitor initiated the session.                                                                                                    |
| `started_at`    | string \| null | ISO 8601 timestamp the agent first joined, or `null` before then.                                                                                        |
| `ended_at`      | string \| null | ISO 8601 timestamp the session ended, or `null` while live.                                                                                              |
| `ended_by`      | string \| null | `visitor`, `agent`, or `system` — who ended it.                                                                                                          |
| `agent_user_id` | string \| null | The operator currently on the session, if any.                                                                                                           |

## Read the session state

Returns the current co-browse session stamped on the conversation, or `null` when no session has been started. The inbox detail panel polls this to decide whether to show the "Co-browse available" or "Co-browse live" affordance.

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

```json theme={null}
{
  "data": {
    "cobrowse": {
      "session_id": "cobrowse_9f2a",
      "status": "requested",
      "room_name": "cobrowse:org_123:conv_abc123:cobrowse_9f2a",
      "visitor_id": "vis_77c1",
      "control": false,
      "requested_at": "2026-06-18T14:02:11.000Z",
      "started_at": null,
      "ended_at": null,
      "ended_by": null,
      "agent_user_id": null
    }
  },
  "meta": {
    "request_id": "req_...",
    "timestamp": "2026-06-18T14:02:30.000Z"
  }
}
```

`cobrowse` is `null` when the conversation exists but carries no session. Requesting a conversation that isn't in your account returns `404 NOT_FOUND`. A malformed `conversationId` returns `422 VALIDATION_ERROR`.

## Join the session

Mints a data-channel join token for the agent so they can view — and, when the visitor granted control, guide — the customer's live browser session. The call flips the session to `active` and stamps the agent and start time.

Pass `control: true` to request guided control. The effective grant is the AND of your request and the visitor's stored consent: if the visitor joined observe-only, the session can never be escalated to guided control, regardless of this flag. The grant is enforced when the token is minted, not just in the UI.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cobrowse/conv_abc123/join \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "control": true }'
```

The body is optional; `control` defaults to `false` (observe-only).

```json theme={null}
{
  "data": {
    "token": "ey...",
    "ws_url": "wss://media.orbit.devotel.io",
    "room_name": "cobrowse:org_123:conv_abc123:cobrowse_9f2a",
    "session_id": "cobrowse_9f2a",
    "control": true,
    "role": "agent",
    "expires_in": 1800,
    "cobrowse": {
      "session_id": "cobrowse_9f2a",
      "status": "active",
      "room_name": "cobrowse:org_123:conv_abc123:cobrowse_9f2a",
      "visitor_id": "vis_77c1",
      "control": true,
      "requested_at": "2026-06-18T14:02:11.000Z",
      "started_at": "2026-06-18T14:03:05.000Z",
      "ended_at": null,
      "ended_by": null,
      "agent_user_id": "user_88ad"
    }
  },
  "meta": { "request_id": "req_...", "timestamp": "2026-06-18T14:03:05.000Z" }
}
```

Use `token` and `ws_url` to connect to the room with the orbit-media client SDK. The token expires after `expires_in` seconds (30 minutes); the SDK re-mints on reconnect by calling this endpoint again, which is idempotent on an already-active session — it re-stamps the agent without resetting `started_at`. `control` in the response is the *effective* grant after the visitor's consent ceiling is applied.

| Status | Code                   | When                                                                                                                                           |
| ------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `404`  | `NOT_FOUND`            | The conversation isn't in your account.                                                                                                        |
| `404`  | `COBROWSE_NOT_ACTIVE`  | No joinable session — the visitor hasn't started co-browsing, or the previous session ended. Ask the visitor to start it from the chat widget. |
| `422`  | `VALIDATION_ERROR`     | Malformed `conversationId` or join body.                                                                                                       |
| `503`  | `COBROWSE_UNAVAILABLE` | The co-browse media service is not configured on this deployment.                                                                              |

## End the session

Ends the co-browse session from the agent side: `status` becomes `ended`, with `ended_at` and `ended_by: "agent"` stamped. The call is idempotent — ending an already-ended session is a no-op and returns the same record. The visitor can independently end from the widget; either side ending stops the data room. To start co-browsing again, the visitor must re-initiate from the widget so consent is re-confirmed.

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

```json theme={null}
{
  "data": {
    "cobrowse": {
      "session_id": "cobrowse_9f2a",
      "status": "ended",
      "room_name": "cobrowse:org_123:conv_abc123:cobrowse_9f2a",
      "visitor_id": "vis_77c1",
      "control": true,
      "requested_at": "2026-06-18T14:02:11.000Z",
      "started_at": "2026-06-18T14:03:05.000Z",
      "ended_at": "2026-06-18T14:11:40.000Z",
      "ended_by": "agent",
      "agent_user_id": "user_88ad"
    }
  },
  "meta": { "request_id": "req_...", "timestamp": "2026-06-18T14:11:40.000Z" }
}
```

Returns `404 NOT_FOUND` when the conversation — or its session — isn't in your account.

## See also

* [Session Replay API](/api-reference/session-replay) — the asynchronous twin, for replaying a session after the fact
* [Conversations API](/api-reference/conversations) — the threads sessions attach to
* [Inbox API](/api-reference/inbox) — where agents start and end co-browse
* [Error codes](/api-reference/error-codes)
