boltZharp Docs
play_circleApp Telemetry

Session Replay

Record what a user saw and did in the browser, and play it back from the error, trace or session it belongs to.

Replay is part of @zharp/browser and works in web pages only — the Flutter, Android and iOS SDKs don't record. It is off until you turn it on. Recording uses rrweb: it captures the page's structure and its changes, not video.

Turn it on

The setup page's snippet records one session in ten from start to end, and every session that hits an error:

import { Zharp } from "@zharp/browser";

Zharp.init({
  key: "zpk_…",
  service: "checkout-web",
  replay: {
    sampleRate: 0.1,         // record 10% of sessions from start to end
    onErrorSampleRate: 1.0,  // and every session that hits an error
  },
});

Sampling

OptionDefaultWhat it does
sampleRate0Share of sessions recorded from start to end, 0 to 1.
onErrorSampleRate0Share of the remaining sessions that keep the last minute in memory and upload it, with the rest of the session, only when an error happens.
replay: {
  sampleRate: 0.1,         // record 10% of sessions from start to end
  onErrorSampleRate: 1.0,  // of the rest, keep the last minute in memory and upload it
                           // — and the rest of the session — only if an error happens
}
  • The decision is made once per session, when it starts.
  • A session that won't record never downloads the recorder. One waiting for an error uploads nothing unless an error happens — onErrorSampleRate: 1.0 costs nothing for the sessions that go fine.
  • With the script tag, the recorder is a separate file, zharp-replay.min.js, loaded from next to zharp.min.js; recorderUrl points elsewhere. Bundled builds load it as a chunk of their own.

Privacy

warning
Every input is masked by default

What people type into inputs, text areas and selects is masked. Turning that off with maskAllInputs: false sends passwords and card numbers unless blockSelector covers them.

OptionDefaultWhat it does
maskAllInputstrueMask what is typed into every input, text area and select.
maskAllTextfalseMask all text on the page, not just inputs.
maskTextSelectornoneCSS selector for elements whose text is masked.
blockSelectornoneCSS selector for elements left out of the recording entirely, such as a payment iframe.
beforeSendnoneCalled with each batch of events before it is sent; return false to drop it.
replay: {
  onErrorSampleRate: 1.0,
  blockSelector: ".payment-form, [data-private]",  // never recorded: space kept, contents left out
  maskTextSelector: ".account-balance",            // text replaced by placeholder characters
  maskAllText: false,                              // true masks every text node on the page
  maskAllInputs: true,                             // the default
}

For anything really sensitive, prefer blockSelector: masked text is still recorded as placeholder characters of the right length, while a blocked element is never captured at all.

replay: {
  onErrorSampleRate: 1.0,
  beforeSend: (events) => !location.pathname.startsWith("/admin"),  // false drops the batch
}

Watching a replay

  • On the Sessions tab, recorded sessions can be opened and played; the others say Not recorded. With errors narrows the list to the sessions worth watching.
  • On an issue, Watch replay opens the recording of the session it happened in.
  • Each recording lists the traces that happened during it, so you can go from a replay to the request that failed and back. The session's page also lists its requests, logs and errors in order.

Retention

Recordings are kept for 7 days, or for your plan's log retention if that is shorter. Deleting the app deletes its recordings. The Session replay item on the setup checklist ticks once the first recording arrives.