REST API (on premise)
On-Premise REST API Overview
The on-premise REST API functions similarly to the cloud version, with some differences detailed below.
Enabling the API
By default, the API is disabled. It will only be enabled when you add a REST_KEY environment variable.
Authentication
To authenticate your requests to the REST API, add an Authorization header set to the value you provided in the REST_KEY environment variable.
The Visitor Object
The Visitor object may differ slightly in the on-premise version, depending on your configuration. Most attributes will be present, and all Visitors will have the same properties.
Session IDs
The watch link and viewer token endpoints return a session_id like onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4. Your instance generates it, so it needs no connection to our servers. Keep it: it's what you pass back to request the Session's recording.
Link and Token Expiry
Watch links and viewer tokens last 60 seconds by default. Set an X-Token-Expiry-Seconds header on the request to choose a different lifetime, as a whole number of seconds between 1 and 86400 (24 hours). Anything else is rejected with a 400, so a value like 1h fails rather than quietly becoming one second.
Use a longer value only when the link or token will not be used straight away, and prefer the shortest value your flow allows. Note that the clock starts when the link is generated, not when it is opened: a Session started from a link that has been sitting around for most of a day has correspondingly less time left before the viewer is disconnected.
Earlier versions of the server took a TOKEN_EXPIRY_SECONDS environment variable instead. It has been removed in favour of the header, which lets you vary the lifetime per request without a redeploy. Instances that still set it start normally and ignore it.
Endpoints
On-premise, the REST API is mounted on the /api path of your base endpoint. For example, if your BASE_ENDPOINT is https://support.acmetech.com/cobrowsing, one of the endpoints will be https://support.acmetech.com/cobrowsing/api/visitors/list/latest.
Listing the Latest Visitors
Use this endpoint to list the latest online Visitors. This only works with MongoDB.
GET /v1.4/visitors/list/latest
| Parameter | Type | Description |
|---|---|---|
max_results | number, optional | The max number of results to return. |
Response
// HTTP 200
{
"status": "ok",
"visitors": [
{
"short_id": "1234",
// ...
}
]
}
| Field | Type | Description |
|---|---|---|
visitors | Visitor[] | List of Visitors matching the query. |
Searching for a Visitor
Use this endpoint to search for Visitors.
GET /v1.4/visitors/list/search
| Parameter | Type | Description |
|---|---|---|
q | string, optional | The search query in this format. If left empty, the latest Visitors will be returned. |
max_results | number, optional | The max number of results to return. |
Response
// HTTP 200
{
"status": "ok",
"visitors": [
{
"short_id": "1234",
// ...
}
]
}
| Field | Type | Description |
|---|---|---|
visitors | Visitor[] | List of Visitors matching the query. |
Retrieving a Visitor
Use this endpoint to retrieve a specific Visitor.
GET /v1.4/visitors/{visitor_id}
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
includeHistory | boolean, optional | Set to true to include the Visitor's pageview history (MongoDB only). |
Response
// HTTP 200
{
"status": "ok",
"visitor": {
"short_id": "1234",
"history": [
// Pageviews, if includeHistory was set to true
],
// ...
}
}
| Field | Type | Description |
|---|---|---|
visitor | Visitor | The Visitor. |
visitor.history | Pageview[], optional | The Visitor's latest pageviews. Only present with includeHistory. |
Errors
| Code | Description |
|---|---|
404 | The Visitor does not exist. |
Generating a Link to Start Screen Sharing with a Visitor
To start a Session with one of your Visitors, first obtain an authorization token from our server, then make a request to your on-premise REST API.
// POST /v1.4/visitors/{visitor_id}/watch_url
{
"licenseToken": "token1234",
"branding": {},
"permissions": {},
"viewer": {
"id": "123",
"name": "Joe Smith"
}
}
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
licenseToken | string | The token obtained from here. Reach out to our team if this is incompatible with your flow for alternative options. |
viewer.id | string | The ID of the person viewing the screen (required). |
viewer.name | string, optional | The name of the person viewing the screen. |
agent.id | string, optional | The ID of the Agent responsible for the Session. When set, the Agent (rather than the viewer) is the identity used for licensing and usage tracking, and the licenseToken must have been generated for this Agent. Use it when the person viewing the screen is not one of your Agents — for example when the Visitor is viewing their own Session — so each viewer is not counted as a separate Agent. |
agent.name | string, optional | The name of the Agent responsible for the Session. |
branding.naked | "page_only", optional | Set to "page_only" to remove everything on the page except the shared screen, including the viewer's UI. Ideal when embedding the page inside an iframe. |
branding.show_end_button | boolean, optional | Whether an "End session" button should be shown on the viewer page. |
branding.on_end_url | string | null, optional | If set, the viewer will be redirected to this URL after the Session ends. Defaults to null. |
branding.retry_url | string | null, optional | If set, the visitor can click a "Retry now" link that redirects to this URL if there are problems. Defaults to null. |
permissions.allow_click | boolean, optional | Whether the viewer should have access to remote click (defaults to true). |
permissions.allow_confetti | boolean, optional | Whether the viewer should be able to throw confetti (defaults to true). |
permissions.allow_console | boolean, optional | Whether the viewer should have access to remote console capabilities (defaults to false). |
permissions.allow_draw | boolean, optional | Whether the viewer should have access to drawing (defaults to true). |
permissions.allow_redirect | boolean, optional | Whether the viewer should have access to remote redirect & reload features (defaults to true). Called allow_agent_redirect before v1.4. |
permissions.allow_request_control | boolean, optional | Whether the viewer should be able to request remote control when the Visitor's SDK requires a control request (defaults to true). |
permissions.allow_request_full_tab | boolean, optional | Whether the viewer should be able to request full browser tab sharing (defaults to false). |
permissions.allow_request_mode | boolean, optional | Whether the viewer should be able to request a different sharing mode, such as full screen sharing (defaults to true). |
permissions.allow_request_visitor_screen | boolean, optional | Whether the viewer should be able to ask the Visitor to share their full screen (defaults to true). |
permissions.allow_scroll | boolean, optional | Whether the viewer should have access to remote scroll (defaults to true). |
permissions.allow_show_viewer_screen | boolean, optional | Whether the viewer should be able to share their screen with the Visitor (defaults to true). Called allow_show_agent_screen before v1.4. |
permissions.allow_type | boolean, optional | Whether the viewer should have access to remote type (defaults to true). |
permissions.allow_video | boolean, optional | Whether the viewer should have access to video calls (defaults to true). Audio calls are not available on premise. |
permissions.hide_private_details | boolean, optional | Whether the Visitor's private information (such as IP address) should be hidden from the viewer (defaults to false). |
permissions.keeps_session_alive | boolean, optional | Whether this viewer keeps the Session alive. Set to false for silent observers, so the Session ends when the last keep-alive viewer leaves (defaults to true). |
session_properties.controller | Array<"viewer" | "visitor">, optional | Which sides of the Session can start video calls and request a different sharing mode. Defaults to ["viewer"]. |
session_properties.record | boolean, optional | Whether this Session should be recorded. Defaults to true whenever session recording is set up on your server; set it to false to skip recording this Session. |
Upgrading from v1.3?
agent object was replaced by the viewer object, and viewer.id is now required. In v1.4, agent is instead an optional, separate object identifying the Agent the Session is licensed to (see above). The permissions.allow_agent_redirect and permissions.allow_show_agent_screen options were renamed to permissions.allow_redirect and permissions.allow_show_viewer_screen. Requests to the /v1.3/ endpoints keep working with the old names.Response
// HTTP 200
{
"status": "ok",
"watch_url": "https://support.acmetech.com/cobrowsing/screen?id=jfoiajsdoif&token=osdjfoijo",
"session_id": "onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4"
}
| Field | Type | Description |
|---|---|---|
watch_url | string | The Secure watch URL. It stays valid for 60 seconds unless you set X-Token-Expiry-Seconds, so open it right away. |
session_id | string | The id of the Session this link starts. Use it to request the Session's recording. |
Errors
| Code | Description |
|---|---|
400 | A required parameter is missing or invalid. |
402 | This viewer would put you over your subscription limits. |
404 | The Visitor does not exist. |
409 | The Visitor's browser is not supported. |
Generating a Viewer Token to Embed Co-Browsing
Use this endpoint to generate a viewer token instead of a watch link. The token lets you embed the co-browsing screen directly in your own application with the viewer SDK, instead of opening the hosted watch page. The Visitor's browser is asked to prepare for the Session right away, so only request a token when the viewer is about to connect.
// POST /v1.4/visitors/{visitor_id}/viewer_token
{
"licenseToken": "token1234",
"permissions": {},
"viewer": {
"id": "123",
"name": "Joe Smith"
}
}
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
licenseToken | string | The token obtained from here. Reach out to our team if this is incompatible with your flow for alternative options. |
The endpoint also accepts the same viewer, agent, permissions and session_properties parameters as the watch link endpoint above.
Response
// HTTP 200
{
"status": "ok",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"endpoint": "https://support.acmetech.com/cobrowsing",
"region": "onpremise",
"short_id": "1234",
"session_id": "onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4",
"expiration": "2026-01-11T18:01:49.700Z"
}
| Field | Type | Description |
|---|---|---|
token | string | The token to pass to the viewer SDK. |
endpoint | string | The server the viewer should connect to (your on-premise instance). |
region | string | Always onpremise. |
short_id | string | The short id of the Visitor. |
session_id | string | The id of the Session this token starts. Use it to request the Session's recording. |
expiration | Datetime (ISO 8601) | Time at which the token is no longer valid. It's 60 seconds after issue unless you set X-Token-Expiry-Seconds. Use it right away. |
Errors
| Code | Description |
|---|---|
400 | A required parameter is missing or invalid. |
402 | This viewer would put you over your subscription limits. |
404 | The Visitor does not exist. |
409 | The Visitor's browser is not supported. |
Requesting a Session Recording
Renders an ended Session to MP4 on your own instance. See Session Recording for the storage and Chromium setup it needs.
This Endpoint Authenticates Differently
REST_KEY. Send an Authorization: Bearer header holding a short-lived HS256 JWT signed with your SECRET_KEY, with the payload {"scope": "cobrowsing-data-call", "apiKeys": "*"}.// POST /v1.4/recordings/{visitor_id}
{
"session_id": "onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4"
}
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
session_id | string | The session_id returned when you generated the watch link or viewer token. |
webhook_url | string, optional | An http or https URL the outcome is POSTed to when the render finishes, so you don't have to poll for it. |
clear_error | boolean, optional | Discards a failure remembered from an earlier attempt and starts a fresh render. See Failed Renders. |
Rendering is asynchronous. The first request starts it, and further requests report progress until the video is ready. Poll about once a minute rather than tightly, as rendering a long Session takes a while.
Response
// HTTP 200, still rendering
{
"status": "ok",
"video_status": "recording"
}
// HTTP 200, finished
{
"status": "ok",
"video_status": "ready",
"video_url": "https://support.acmetech.com/cobrowsing/recordings/APIKEY/1234/6f1b....mp4"
}
| Field | Type | Description |
|---|---|---|
video_status | string | recording while the render is in progress, ready once the video is available. |
video_url | string | Present when video_status is ready. Points at your own storage. Held for 6 hours after the render completes. |
Failed Renders
A render that fails is remembered for 6 hours, so a poll doesn't silently start the whole thing over. Within that window every request for the Session reports the same failure, in the standard REST error shape:
// HTTP 422
{
"success": false,
"status": "error",
"error": "recording_failed",
"error_explained": "Recording this session failed. Retry it with clear_error."
}
| Field | Type | Description |
|---|---|---|
error | string | Why the render failed, from the closed set below. This is the field to branch on. |
error_explained | string | A human-readable description. Meant for logs and support tickets, not for your code: the wording can change between releases. |
success | boolean | Always false. Kept for older clients; status carries the same information. |
The response that first reports a failure carries the status code that fits it (404 when the Session has no events, 501 when your instance can't render it at all). The polls after it come back as 422 with the same error, which is why a 404 can turn into a 422 for the same Session. Branch on error, not on the status code.
To try again, send the same request with clear_error. The remembered failure is discarded and a new render starts, so the response is the usual video_status: "recording":
// POST /v1.4/recordings/{visitor_id}
{
"session_id": "onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4",
"clear_error": true
}
Retrying only helps for the codes marked retriable. The rest fail the same way until something changes about the Session or your instance:
| error | Meaning | Retriable |
|---|---|---|
recording_failed | The render failed for a reason we can't attribute. The underlying error is in your instance's logs. | Yes, though often the same way twice |
browser_crashed | The Chromium container dropped the replay part-way through. | Yes |
recording_timed_out | The replay or the encode didn't finish in time. | Yes |
recording_lost | The instance running the render went away, and it couldn't be picked back up within two hours. | Yes |
upload_failed | The video rendered, but couldn't be written to your storage. Check the bucket, credentials or mounted volume. | After fixing storage |
no_events_found | No events were stored for this Session, so there is nothing to render. | No |
no_browser_configured | The Session needs a browser and CHROMIUM_ENDPOINT is unset. | After setting CHROMIUM_ENDPOINT |
not_implemented | MongoDB is disabled on this instance, so no Session events were stored to replay. | After enabling MONGO_URI |
visitor_not_found | The Visitor does not exist. Sent as user_not_found to v1.0 and v1.2 clients. | No |
A 503 with "error": "server_busy" is not a failed render: every render slot is in use and nothing has been given up on. Retry the same request in a few seconds, without clear_error.
Being Told When It's Done
Pass webhook_url and the outcome is POSTed there as JSON when the render settles, instead of you having to poll for it. Both outcomes name the Session and Visitor they are about. A finished render carries the same fields as the successful response; a failed one carries only status and error, not the success and error_explained a REST error body has:
// POST to your webhook_url, once the render finishes
{
"status": "ok",
"video_status": "ready",
"video_url": "https://support.acmetech.com/cobrowsing/recordings/APIKEY/1234/6f1b....mp4",
"session_id": "onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4",
"short_id": "1234"
}
// or, when it failed
{
"status": "error",
"error": "browser_crashed",
"session_id": "onprem_s_3f2c8a1e-9d47-4b02-8f61-05c7ae13d9b4",
"short_id": "1234"
}
Any 2xx counts as delivered. Anything else is retried up to five times with a growing delay, then given up on: your instance has no durable job queue, so a receiver that stays down for the length of the retries loses the notification. Polling remains the reliable channel, and the webhook_url is remembered across a re-triggered render.
Errors
| Code | Description |
|---|---|
400 | session_id is missing, or webhook_url is not a valid http/https URL. |
404 | The Visitor does not exist, or no events were recorded for this Session. |
422 | An earlier attempt at this Session failed. See Failed Renders. |
501 | MongoDB is disabled on your instance, or the Session needs a browser and CHROMIUM_ENDPOINT is unset. |
503 | Every render slot is in use. Retry in a few seconds. |
Preparing a Session
Use this endpoint to ask the Visitor's browser to prepare for a Session ahead of time, so the Session starts faster. This is done automatically when you generate a watch link.
POST /v1.4/visitors/{visitor_id}/session/prepare
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
Response
// HTTP 200
{
"status": "ok"
}
Errors
| Code | Description |
|---|---|
404 | The Visitor does not exist. |
Stopping an Active Session
Use this endpoint to stop the Visitor's active Session.
DELETE /v1.4/visitors/{visitor_id}/session
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
Response
// HTTP 200
{
"status": "ok",
"visitor": {
"short_id": "1234",
// ...
}
}
| Field | Type | Description |
|---|---|---|
visitor | Visitor | The Visitor. |
Errors
| Code | Description |
|---|---|
404 | The Visitor does not exist. |
Deleting a Visitor
Use this endpoint to delete a Visitor entirely, including their pageview history and Session recordings. You can only delete Visitors who are not currently online or in a Session.
DELETE /v1.4/visitors/{visitor_id}
| Parameter | Type | Description |
|---|---|---|
{visitor_id} | string | The short id of the Visitor. |
Response
// HTTP 200
{
"status": "ok"
}
Errors
| Code | Description |
|---|---|
404 | The Visitor does not exist. |
409 | The Visitor is online or in a Session. |
Deleting All Visitors
Use this endpoint to delete all Visitors, along with their pageview history and Session recordings.
DELETE /v1.4/visitors
Response
// HTTP 200
{
"status": "ok"
}