Skip to content

WebSocket ingest

Use the GET /recordings/{recordingId}/ingest/ws route when you want to stream rrweb payloads over a single WebSocket connection instead of issuing multiple HTTP POST requests.

Making a connection

  • URL template: wss://api.rrwebcloud.com/recordings/{recordingId}/ingest/ws
  • Query parameters:
    • contentType (application/x-ndjson | application/ndjson | application/json) — defaults to application/x-ndjson.
    • contentEncoding (gzip | br | zstd) — optional, defaults to identity.
    • debug — optional; when set to true the server echoes the decoded payload back over the WebSocket along with the upstream response metadata.
  • Authentication is handled server-side using your configured token/allowed origins, so clients do not need to add headers beyond the usual WebSocket handshake (see Authentication).

⚠️ Browsers do not allow custom request headers during the WebSocket handshake, so contentType, contentEncoding, and debug must be supplied as query parameters.

Client lifecycle

  1. Open the socket with the desired query parameters.
  2. Stream NDJSON/JSON frames:
    • Send text frames for uncompressed payloads.
    • Send binary frames if the payload is pre-compressed.
  3. The server automatically flushes upstream whenever either
    • about 256 KB of buffered data has been received, or
    • the connection has been idle for roughly 1 second.
  4. Call ws.close() when the batch is complete to force a final flush and close.

The server sends back:

  • Zero or more debug frames (plain text NDJSON) when debug=true.
  • One or more summary frames containing JSON. Intermediate summaries use "final": false while the last summary will use "final": true.
json
{
  "type": "upstream-result",
  "ok": true,
  "status": 204,
  "statusText": "",
  "headers": {
    "content-type": "application/json"
  },
  "final": true
}

If forwarding fails, the server emits:

json
{
  "type": "error",
  "status": 502,
  "message": "Unable to forward recording data."
}

Connections close with code 1000 for success and 1011 for failures so that clients can react without parsing the summary payload.

Example

ts
const recordingId = window.crypto.randomUUID();
const ws = new WebSocket(
  `wss://api.rrwebcloud.com/recordings/${recordingId}/ingest/ws?contentType=application%2Fx-ndjson`,
);

ws.addEventListener("open", () => {
  ws.send(JSON.stringify({ type: "snapshot", data: {} }) + "\n");
  ws.send(JSON.stringify({ type: "incremental", data: {} }) + "\n");
  ws.close(); // flush to rrweb cloud
});

ws.addEventListener("message", (event) => {
  try {
    const payload = JSON.parse(event.data);
    if (payload.type === "upstream-result") {
      console.log("rrweb cloud accepted payload", payload.status);
    }
  } catch {
    console.log("Debug payload", event.data);
  }
});