Connect RUM
This capability is only available to BYOC deployments. Check out our pricing page for more information about subscription plans and the available deployment modes.
groundcover’s Real User Monitoring (RUM) SDK captures front-end performance, user interactions, errors, logs, distributed traces, and session replay from your web application — with privacy masking on by default.
Start capturing RUM data by installing the browser SDK in your web app.
This guide walks you through installing and initializing the SDK, the full configuration reference, identifying users, sending custom events and logs, capturing exceptions, session management, source maps, and session replay.
Install the SDK
npm install @groundcover/browser
# or
yarn add @groundcover/browserInitialize the SDK
A single init() call installs every instrumentation (page loads, DOM interactions, network requests, errors, console logs, navigation, and performance) and starts sending data. Session replay is the one exception — it must be started explicitly with startReplayRecording().
import groundcover from '@groundcover/browser';
groundcover.init({
apiKey: 'your-ingestion-key',
dsn: 'your-dsn',
cluster: 'your-cluster',
appId: 'your-app-id',
environment: 'production',
});From here you can enrich it:
Configuration
init() takes your connection and identity fields at the top level, plus an options object for behavioral configuration.
Connection & identity
apiKey
✅
A dedicated Ingestion Key of type RUM (Settings → Access → Ingestion Keys).
dsn
✅
Your public groundcover endpoint, in the format https://example.platform.grcv.io, where example.platform.grcv.io is your ingress.site installation value.
cluster
✅
Identifier for your cluster; helps filter RUM data by cluster.
appId
✅
Application identifier; reported as service.name.
environment
—
Deployment environment (e.g. production, staging) used for filtering.
namespace, releaseId, user, sessionId
—
Optional identity/grouping fields. releaseId associates uploaded source maps with a release; user matches identifyUser; sessionId enables shared sessions.
Behavioral options
All behavioral configuration lives under options, grouped by concern — sampling (sessionSampleRate, eventSampleRate), enabled instrumentations (enabledEvents), excludedUrls, the beforeSend / enrichEvent hooks, and the privacy, tracing, transport, and replay groups. You can update it at runtime with groundcover.updateConfig(...).
For the complete, always-current configuration reference — every option, type, and default — see the @groundcover/browser package on npm. Data masking is on by default; see Privacy and data masking below.
Privacy and data masking
Masking is on by default (privacy.level: 'mask-sensitive'). A single level is the master switch; finer toggles and hooks refine it.
level
Replay inputs
Replay text
DOM events
Network / logs / errors
mask-sensitive (default)
sensitive masked
[data-private] + maskSelectors masked
sensitive masked
redacted
mask-all
masked
masked (*)
all masked
redacted
allow
—
—
—
off (auth headers are still stripped)
Under mask-sensitive, an input/element is masked when it is a type="password", sits under a [data-private] ancestor or a maskSelectors match, or has an id/name/class/aria-label/placeholder matching a built-in sensitive-key pattern or your sensitiveKeys. Non-sensitive inputs and static page text stay visible; use [data-private] / maskSelectors to mask static content.
Built-in sensitive patterns (always treated as sensitive, case-insensitive; your sensitiveKeys merge on top):
Key substrings — matched inside a body/query key or a DOM element attribute (
id/name/class/aria-label/placeholder):token,secret,passwd,password,api_key,access_key,write_key,auth,bearer,credential,cvv,ssn,credit_card,card_number(the_in the last five is optional —apikey/api-keyalso match).Request/response headers — always stripped regardless of
level:authorization,cookie,set-cookie, plus any header name containingtoken,key,secret,passwd,password,auth,bearer, orcredential.Query / form param names — matched as a whole key (not inside JSON bodies), for OAuth-style callbacks:
code,state,session_state,id_token,access_token,refresh_token,token.
To turn masking off, set privacy: { level: 'allow' }. Note this disables masking only — auth/request headers are still stripped regardless of level (see the header list above).
Identify Users
Link RUM data to specific users. An omitted key leaves the current identity untouched; pass null via updateConfig to clear it (e.g. on logout).
Send Custom Events
Instrument key user interactions or business events:
Custom event payloads are not auto-redacted (they’re deliberately provided). Scrub sensitive fields yourself, or via enrichEvent.
Capture Exceptions
Manually track caught errors with optional context:
Send Logs
groundcover.logger provides one method per level — log, info, warn, error, debug, trace. The second argument is an attributes object; nested objects are flattened to dotted keys.
The SDK also auto-captures console.* calls; when any argument is a plain object, its keys are promoted to structured log attributes. Reserved keys (message, level, location) are always set by the SDK and can’t be overridden.
Session Management
Read or override the current session id:
Micro-frontend session synchronization
Pass a shared sessionId so multiple frontends report under one session:
Session lifecycle
sessionMaxDuration sets a target maximum wall-clock session length (default 4 hours; must be between 1 minute and 8 hours).
It is enforced lazily, on activity — not by a background timer, so it is not a hard upper bound. Once the cap has elapsed, the next user/business event (click, navigation, log, custom, network, exception, …) flushes pending events under the current session id, mints a fresh id, and resumes replay recording if it had been active.
Because rotation is activity-gated, a session that goes idle keeps its id past the cap until the next qualifying event. Sessions are also bounded by a 30-minute inactivity gap, enforced the same lazy way. The flush is best-effort and the rotation always proceeds regardless of delivery success. Invalid values fall back to the default with a console.warn.
Manual navigation
When navigation isn’t auto-tracked (e.g. a custom router), you can bracket navigation spans manually:
Source Maps
Source maps map minified/bundled code back to your original source files. With them, stack traces in RUM (e.g. in session details and exceptions) show your real file names, line numbers, and function names instead of bundle names and minified positions.
Pre-condition: source maps were enabled in your CI operation.
To enable source maps in your account, upload them from your CI job via the API:
Required form fields:
app_id- your application identifier (alphanumeric, dots, hyphens, underscores), as provided in the RUM init call.release_id- the release/version being deployed (same character restrictions), as provided in the RUM init call.file- the source map file.
Required headers:
Authorization- groundcover api key - here is how to generate one.X-Backend-Id- the relevant BYOC backend, displayed in the api keys page.
Here is an example of source maps uploading using curl:
Response (201):
Files will be stored in the selected provider at the path: sourcemaps/<app_id>/<release_id>/<filename>.
Every new RUM trace that holds a stack trace will be automatically converted based on the source map.
Session Replay
Session replay records your users’ behavior with rrweb so you can replay the actions that led to specific RUM events.
Replay recording does not start automatically — even when replay is in enabledEvents. You must start it explicitly (for example, after obtaining user consent), and can stop it before a sensitive section of your app:
Recording is stored on your BYOC server and is deleted along with the RUM session, in accordance with your retention policy.
Masking replay content
Replay masking is driven by your privacy config — masking is on by default. To mask specific static content, add the data-private attribute or a maskSelectors match:
rrweb mask options are fixed at record() time. Changing privacy config at runtime via updateConfig automatically restarts the active recording so the new masking applies.
Viewing sessions
In the summary page, you will see an indication next to sessions with a recording.

Within the drawer, open the Session Replay tab to see the recording.

API reference
All methods are available on the default export and on window.groundcover.
init(config)
Initialize the SDK and install instrumentation.
identifyUser(user)
Attach user identity to subsequent events.
sendCustomEvent({ event, attributes })
Emit a custom business event.
captureException(error, metadata?)
Capture a handled error with optional context.
logger.{log,info,warn,error,debug,trace}(message, attributes?)
Structured logging.
updateConfig({ options?, user?, … })
Update config at runtime (merges nested groups one level deep).
startNavigation(metadata) / endNavigation(metadata)
Manual navigation spans (when navigation isn’t auto-tracked).
getSessionId() / setSessionId(id?)
Read / override the current session id.
startReplayRecording() / stopReplayRecording()
Manually control session replay.
Migrating to 1.0.0
1.0.0 restructures options by concern (a clean break from 0.x) and removes the deprecated masking flags. Update your config as follows:
0.x
1.0.0
environment (duplicated in options)
top-level environment only
userIdentifier
user
options.sessionReplay.blockedSelectors
options.replay.blockedSelectors
options.tracePropagationUrls
options.tracing.propagationUrls
options.tracePropagationHeaders
options.tracing.propagationHeaders
options.tracePropagationTraceIdHeaderName
options.tracing.traceIdHeaderName
options.tracePropagationSpanIdHeaderName
options.tracing.spanIdHeaderName
options.traceOrigin
options.tracing.origin
options.batchSize / options.batchTimeout
options.transport.batchSize / options.transport.batchTimeout
options.enableCompression
options.transport.compression
options.enableMasking: true (removed)
set options.privacy.level: 'mask-all'
options.enableMasking: false (removed)
set options.privacy.level: 'allow'
options.maskFields (removed)
set options.privacy.maskSelectors / options.privacy.sensitiveKeys
The removed masking flags are ignored, not auto-mapped — passing enableMasking / maskFields logs a console.warn and has no effect. You must set the corresponding privacy option yourself. Masking remains on by default (mask-sensitive); if you previously relied on masking being off, set privacy: { level: 'allow' } explicitly.
Last updated
