Replay

Statusstable
Version1.2.0(changelog)

Replay captures a recording of a user's browser or mobile session and streams it to Sentry in segments. Each segment consists of two envelope items sent together: a replay_event (metadata about the replay) and a replay_recording (the actual recording data). Sentry stitches segments into a continuous replay for debugging.

Related specs:


Replay operates in one of two modes:

  • Session mode (sessionSampleRate) — Recording starts immediately at SDK initialization and streams continuously to Sentry.
  • Buffer mode (onErrorSampleRate) — Recording starts at SDK initialization but is buffered in-memory (ring buffer). Capture is only triggered by an error or manual flush.

A replay is composed of segments, each identified by a segment_id (starting at 0). Each segment is sent as a pair of envelope items: a replay_event containing metadata and a replay_recording containing the actual recording data.


Stablesince 1.1.0

When an SDK records Session Replay in session mode (sessionSampleRate is specified), the recording SHOULD start when the SDK is initialized and MUST be continuously streamed to the Sentry servers. SDKs SHOULD send a replay envelope every 5 seconds.

The maximum duration of the recording MUST NOT exceed 60 minutes (or 15 minutes without activity on Web). (since 1.2.0) After the hard limit has been reached, the SDK MUST stop recording, clear the current replay_id, and remove it from the Scope so all subsequent events are not associated with it.

For SDKs that support disk cache, the recording SHOULD pause when there is no internet connection or the SDK is being rate-limited. This prevents overflowing the disk cache, which can result in losing more critical envelopes. When internet connection is restored or the rate limit is lifted, the recording SHOULD resume.

Stablesince 1.1.0

When an SDK records Session Replay in buffer mode (onErrorSampleRate is specified), the recording SHOULD start when the SDK is initialized and MUST be buffered in-memory (and to disk if the SDK supports disk cache) in a ring buffer for up to 30 seconds back.

Capturing of the recording MAY be triggered when one of the following conditions is met:

  • A crash or error event occurs and is captured by the SDK.
  • The flush API has been called manually on the replay (for SDKs that support manual API).

After the initial (buffered) segment has been captured, the SDK SHOULD continue recording in session mode. The replay_type field of subsequent segments MUST still be set to buffer to reflect the original replay type.

If the crash or error event has been dropped in beforeSend, the replay MUST NOT be captured.


Stablesince 1.0.0

A replay_event Item MUST always be sent together with a replay_recording Item in the same envelope.

FieldTypeRequiredSinceDescription
typeStringREQUIRED1.0.0MUST be "replay_event".
replay_idStringREQUIRED1.0.0A unique ID for the replay. Follows the same requirements as event_id: hexadecimal UUID v4, exactly 32 characters, lowercase, no dashes.
replay_typeStringREQUIRED1.0.0One of "session" or "buffer". Describes the mode of the replay.
segment_idNumberREQUIRED1.0.0The segment identifier, starting at 0.
replay_start_timestampNumberREQUIRED (first segment)1.0.0UNIX timestamp of the start of the replay (in seconds). Only required on the first segment.
urlsList[String]OPTIONAL1.0.0List of URLs in order of visitation.
trace_idsList[String]OPTIONAL1.0.0List of trace IDs that occurred during the replay.
error_idsList[String]DEPRECATED1.0.0Deprecated. Do not use.

The following attributes are a subset of the optional attributes of an Event.

FieldTypeRequiredSinceDescription
timestampNumberREQUIRED1.0.0UNIX timestamp of the current time (in seconds).
event_idStringREQUIRED1.0.0MUST be the same as replay_id.
platformStringOPTIONAL1.0.0Platform of the SDK.
environmentStringOPTIONAL1.0.0The environment name.
releaseStringOPTIONAL1.0.0The release version.
distStringOPTIONAL1.0.0The distribution identifier.
user.idStringOPTIONAL1.0.0User identifier.
user.usernameStringOPTIONAL1.0.0Username.
user.emailStringOPTIONAL1.0.0User email.
user.ip_addressStringOPTIONAL1.0.0User IP address.
sdk.nameStringOPTIONAL1.0.0SDK name.
sdk.versionStringOPTIONAL1.0.0SDK version.
request.urlStringOPTIONAL1.0.0Current request URL.
request.headers.User-AgentStringOPTIONAL1.0.0User agent string.

Stablesince 1.0.0

The replay_recording item consists of two sub-items delimited by a newline:

  1. Metadata — a JSON object. Currently only segment_id is required:
Copied
{ "segment_id": 0 }
  1. Recording payload — the replay recording instruction set. This payload SHOULD be gzipped, but uncompressed payloads are also accepted. See Replay Recording Events for the recording format.

Stablesince 1.0.0

SDKs MUST accept a replaysSessionSampleRate configuration option (number, 0.0–1.0, default 0). This controls the fraction of sessions recorded in session mode. 1.0 records all sessions, 0 records none.

Naming SHOULD follow the SDK's language conventions:

  • replaysSessionSampleRate (JavaScript)
  • replays_session_sample_rate (Python)

Stablesince 1.0.0

SDKs MUST accept a replaysOnErrorSampleRate configuration option (number, 0.0–1.0, default 0). This controls the fraction of sessions buffered for error replay. 1.0 captures all sessions with an error, 0 captures none.

Naming SHOULD follow the SDK's language conventions:

  • replaysOnErrorSampleRate (JavaScript)
  • replays_on_error_sample_rate (Python)

Stablesince 1.0.0

SDKs MUST provide a replay integration that initializes the recording subsystem:

Copied
replayIntegration(options?) -> Integration

SDKs MAY accept privacy and configuration options (text masking, media blocking, network capture settings).

Naming SHOULD follow the SDK's language conventions:

  • replayIntegration() (JavaScript)
  • SessionReplayIntegration() (Python)
  • SentryReplay / SessionReplay (mobile SDKs)

Stablesince 1.0.0

SDKs SHOULD expose a function to retrieve the active replay instance:

Copied
getReplay() -> replayInstance | null

Returns the replay integration instance if replay is initialized, or null if not. The returned instance provides the runtime control methods documented below.

Stablesince 1.0.0

The replay instance MUST expose a method to retrieve the current replay identifier:

Copied
replayInstance.getReplayId() -> replayId | null

Returns the current replay_id (string) if a replay is active, or null if no replay is recording or buffering. This is useful for checking whether a replay is active before calling other runtime methods.

Stablesince 1.1.0

The replay instance SHOULD expose a method to manually start recording in session mode:

Copied
replayInstance.start() -> void

Starts recording in session mode regardless of sample rates. If a session is already running, this SHOULD be a no-op.

Stablesince 1.1.0

The replay instance SHOULD expose a method to manually start recording in buffer mode:

Copied
replayInstance.startBuffering() -> void

Starts recording in buffer mode regardless of sample rates. If a session is already running, this SHOULD be a no-op.

Stablesince 1.1.0

The replay instance SHOULD expose a method to stop recording:

Copied
replayInstance.stop() -> Promise<void>

Flushes any pending recording data, stops the replay, and ends the session.

Stablesince 1.1.0

The replay instance SHOULD expose a method to flush pending recording data:

Copied
replayInstance.flush() -> Promise<void>

In session mode, this uploads pending recording data to Sentry. In buffer mode, this uploads the buffered data and continues recording (same as when an error triggers capture). Calling flush() while replay is stopped MAY start a new session recording.


Copied
import * as Sentry from "@sentry/browser";

Sentry.init({
  dsn: "https://[email protected]/0",
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  integrations: [Sentry.replayIntegration()],
});

// Manual control
const replay = Sentry.getReplay();
replay.start(); // start session-mode recording
replay.flush(); // flush buffered data
await replay.stop(); // stop and end session

Copied
{
  "type": "replay_event",
  "replay_id": "36b75d9fa11f45459412a96c41bdf691",
  "replay_start_timestamp": 1710861499.287,
  "replay_type": "session",
  "segment_id": 0,
  "trace_ids": ["905aef2282af5fe2ab2c93aa7a340521"],
  "urls": [
    "https://sentry.io/issues/",
    "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true"
  ],

  "request": {
    "url": "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true",
    "headers": {
      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    }
  },
  "timestamp": 1710861507.579,
  "event_id": "36b75d9fa11f45459412a96c41bdf691",
  "environment": "prod",
  "release": "frontend@f00",
  "sdk": {
    "integrations": [
      "BrowserTracing",
      "BrowserProfiling",
      "Replay",
      "ReplayCanvas"
    ],
    "name": "sentry.javascript.react",
    "version": "7.105.0"
  },
  "tags": {
    "sentry_version": "24.4.0.dev0"
  },
  "user": {
    "ip_address": "127.0.0.1",
    "email": "[email protected]",
    "id": 1,
    "name": "Admin"
  },
  "contexts": { "organization": { "id": "0", "slug": "sentry" } },
  "platform": "javascript"
}

Copied
{"segment_id": 0}
\x00\x00\x00\x14ftypqt  \x00\x00\x00\x00qt  \x00\x00\x00\x08wide\x03\xbdd\x11mdat

Copied
{"event_id":"36b75d9fa11f45459412a96c41bdf691","sent_at":"2024-03-19T15:18:27.581Z","sdk":{"name":"sentry.javascript.react","version":"7.105.0"}}
{"type":"replay_event"}
{
  "type": "replay_event",
  "replay_id": "36b75d9fa11f45459412a96c41bdf691",
  "replay_start_timestamp": 1710861499.287,
  "replay_type": "session",
  "segment_id": 0,
  "trace_ids": ["905aef2282af5fe2ab2c93aa7a340521"],
  "urls": [
    "https://sentry.io/issues/",
    "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true"
  ],

  "request": {
    "url": "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true",
    "headers": {
      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    }
  },
  "timestamp": 1710861507.579,
  "event_id": "36b75d9fa11f45459412a96c41bdf691",
  "environment": "prod",
  "release": "frontend@f00",
  "sdk": {
    "integrations": [
      "BrowserTracing",
      "BrowserProfiling",
      "Replay",
      "ReplayCanvas"
    ],
    "name": "sentry.javascript.react",
    "version": "7.105.0"
  },
  "tags": {
    "sentry_version": "24.4.0.dev0"
  },
  "user": {
    "ip_address": "127.0.0.1",
    "email": "[email protected]",
    "id": 1,
    "name": "Admin"
  },
  "contexts": { "organization": { "id": "0", "slug": "sentry" } },
  "platform": "javascript"
}
{"type":"replay_recording","length":141666}
{"segment_id":0}
/* gzipped JSON payload */

VersionDateSummary
1.2.02025-07-30Added session mode hard limit behavior (stop recording, clear replay_id, remove from scope)
1.1.02025-07-29Added SDK behavior for session mode and buffer mode
1.0.02024-08-15Initial spec — replay_event and replay_recording wire format
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").