Skip to main content

Co-browse: live screen assistance

Co-browse is the real-time counterpart to session replay: instead of playing back what a visitor did, an agent joins the visitor’s live page as it happens. When a customer is stuck on your checkout, sign-up, or account settings, live assistance resolves it in the conversation instead of over a back-and-forth of screenshots. This page covers the order of operations from widget install to first session — the consent model, the privacy policy you set before going live, the agent join/end lifecycle, and guided-control packets. For the full request/response schema, see the Co-browse API reference.

When to co-browse

Reach for co-browse when the session’s value depends on what the visitor sees right now — a checkout field that won’t validate, a multi-step form abandoned midway, an agent who can’t reproduce what the visitor describes. It runs inside a conversation the visitor already has open in the chat widget, so the agent joins the page without asking for a share link. Two consent postures, chosen by the visitor when they start the session:
  • Observe (control: false) — the agent watches the live page only. The page DOM is mirrored; nothing the agent types or clicks reaches the visitor.
  • Guided control (control: true) — in addition to observing, the agent can highlight elements, scroll, and fill form fields on the visitor’s page. The consent ceiling is set by the visitor; an agent can request control on join, but an observe-only session can never be escalated to control. The visitor can re-confirm or revoke the grant at any time by re-POSTing their start call.
Choose guided control for form coaching (“here’s where the account number goes”), observe-only for diagnosing what the visitor sees (“your billing page shows an empty name field; here’s where to set it”). Both postures transmit only the DOM of the page the visitor is on — not a pixel stream — so masking applies at capture time, before anything is published.

Prerequisites

Before a session can start, you need the native chat widget installed and the operator’s role set. Co-browse is not available on SMS, WhatsApp, voice, or email threads — only on web-chat conversations.
  • Widget installed. Install the native chat widget on the pages your visitors use — the ./widget export of @devotel-orbit/web, or the hosted orbit-chat.js bootstrap. See the widget install guide for the embed steps; a page without the widget script can never carry a session.
  • Operator role. The agent joining from the inbox needs the conversations:write scope plus an owner, admin, or developer role on their API key or dashboard session. Agents with only conversations:read can see the “Co-browse available” affordance but can’t join.

End-to-end flow

A session moves through four steps, visitor-side then agent-side:
  1. Visitor starts. The visitor starts co-browsing from the widget — usually when they answer the agent’s question “can I see what you’re looking at?” The widget calls POST /widget/conversations/{id}/cobrowse with the visitor’s session token; the session is stamped on the conversation’s metadata and fans out to the inbox in real time. The body is optional; control defaults to false.
  2. Inbox shows the session. The dashboard Inbox polls the session state on open web-chat conversations. When the visitor has initiated, the conversation panel shows a “Co-browse available” affordance; once an agent joins, it flips to “Co-browse live” with a per-session consent indicator.
  3. Agent joins. Call POST /api/v1/cobrowse/{conversationId}/join with the visitor’s conversation id. The endpoint mints a data-channel join token and a scoped room_name bound to , flips the session to active, and stamps the agent and start time. Pass control: true in the body to request guided control; the response’s control field is the effective grant after the visitor’s consent cap.
  4. Agent’s client connects. Use the returned token and ws_url to attach an agent-side viewer to the room so the agent’s browser can render the page mirror. The token expires after expires_in (30 minutes); re-mint by re-calling join, which is idempotent on an already-active session.
The sequence as calls:
From here the agent joins the room over the media data channel with a viewer client; the visitor’s browser publishes the serialized DOM and every capture packet respects the privacy policy below.

Guided control

Once the visitor granted control: true, an agent that joined with control: true in their request can send highlight, scroll, and form_fill packets on the session. Each one travels as a broadcast into the data room; the visitor’s browser rerenders the hint — a pulsing box around a selector, a scroll, or the value you wrote into a form field. The packet is checked against the privacy policy before it leaves the platform — a selector or input type inside any blocked region or always-blocked hard floor is rejected with 403 COBROWSE_CONTROL_BLOCKED, and form_fill attempts are audit-logged. A session where the visitor joined observe-only rejects control with 403 COBROWSE_CONTROL_NOT_GRANTED. The sequence as calls:
A successful POST .../control returns 204 No Content — the packet was broadcast into the room. Keep control packets small: each request travels as a dispatch payload, and a packet that exceeds the 1 MB dispatch ceiling returns 502 COBROWSE_CONTROL_FAILED.

Set the capture-time privacy policy first

Co-browse mirrors the visitor’s live DOM. The privacy policy decides what the capture client masks in the visitor’s page before it publishes a DOM packet — masking happens in the visitor’s browser, and masked content never leaves the page. It also doubles as the server-side guard on the control endpoint: a selector or input type the policy blocks can never be captured or remotely controlled. The default posture is fail-closed — with nothing configured, every input is masked and tel, email, and cc input types are blocked. Password fields and a small set of built-in payment/card selectors are always blocked and cannot be relaxed by this endpoint. Set the policy before your first live session, and cover your checkout, payment, and account-settings pages explicitly:
The same toggle set is backed by the Settings → Channels → Native chat dashboard panel, so operators can adjust masking without an API client. The change takes effect on the next co-browse session bootstrap for both the visitor and the joining agent — it doesn’t retroactively change a session already in progress. Because off-screen DOM is never transmitted, co-browse isn’t a screen share: the capture client only sends the DOM tree the visitor’s page exposes, minus whatever your policy masks, over a scoped data channel. Choosing selectors to block — and documenting that decision with your DPO — is the tenant-owned control that keeps co-browse aligned with your compliance posture. Password fields, X-Frame-Options-protected iframes, and your own block_selectors are all excluded at capture time.

Ending a session

Either side can end the session; both stop the data room:
  • Agent ends. POST /api/v1/cobrowse/{conversationId}/end stamps status: ended, ended_at, and ended_by: "agent". The inbox panel’s End session button calls this endpoint. Ending an already-ended session is an idempotent no-op.
  • Visitor ends. The widget calls POST /widget/conversations/{id}/cobrowse/end with the visitor token; ended_by becomes "visitor". Either a visitor closing the widget or a system cleanup stamps ended_by: "system".
Best practices for session hygiene — keep these so a session can’t linger live when nobody is using it:
  1. End sessions on resolution, not just on conversation close. An ended session can never be joined again; the visitor re-initiates from the widget so consent is re-confirmed.
  2. Re-initiation re-confirms consent — don’t reuse an ended room. The re-join endpoint is idempotent on a live session and never revives an ended one.
  3. The visitor can revoke at any time. If an agent requests control on a session the visitor opened observe-only, the 403 rejection is the consent model working, not an error.
  4. Sessions on closed/archived threads can’t be joined — the affordance hides once the thread is terminal.
  5. Tokens expire after 30 minutes. Re-mint via join rather than holding a long-lived token; the process re-stamps the agent without advancing started_at.

Failure ladder

When a session won’t start, stalls, or refuses control, work the ladder in the Troubleshooting: co-browse session stuck page. The common classes:
  • COBROWSE_NOT_ACTIVE on join — the visitor hasn’t started co-browsing, or the previous session ended. Ask the visitor to re-initiate from the widget.
  • A frame blocked by the customer site’s X-Frame-Options or CSP frame-ancestors — the embedded viewer can’t render the page.
  • A session that joins and then freezes — an edge point-of-presence dropped the data-channel socket; reconnect with the client SDK and re-mint the token.
  • A control or custom-tool packet refused with 502 — the dispatch payload exceeded the 1 MB ceiling; trim the selector and value.
The troubleshooting page walks each class through a remedy ladder and reads session errors from the conversation activity feed when the HTTP verdict alone doesn’t pin the stage.

Minimal HTML that starts a session

The hosted widget bootstrap is an orbit-chat custom element on your page; the visitor opens chat, and starts co-browsing from the widget UI — no integration code is required on your end. The element below is the least the page needs:
The visitor’s start call is made by the widget with its own session token — the agent-side verbs in this guide and the Co-browse API reference use your API key. Keep the visitor-side start in the widget, and use the API key surface only for join, end, control, and the privacy policy.

Endpoint summary

See also