Documentation

Browsonic SDK Documentation

Real-time error and event capture for JavaScript applications. The @browsonic/sdk package is the canonical surface — framework adapters (React, Vue, Svelte, Angular, Next.js, Astro, Remix) wrap it without changing the wire format.

Last updated:

Introduction

Browsonic SDK is a lightweight, zero-dependency JavaScript library that automatically captures frontend errors and anomalies in real time. It is published on the public npm registry as @browsonic/sdk under the Apache 2.0 licence.

What it captures

Console errors
console.error, warn, info
Unhandled exceptions
window.onerror events
Promise rejections
Unhandled promise rejections
Network failures
HTTP 4xx / 5xx responses

Key features

FeatureDescription
Zero dependenciesNo external libraries required.
Automatic collectionIntercepts console, errors, and fetch automatically.
Smart batchingGroups events and flushes at a configurable interval.
DeduplicationPrevents duplicates via fingerprinting.
Offline supportPersists the queue to localStorage when offline.
Privacy firstAutomatic redaction of sensitive keys.
Circuit breakerAuto-disables on repeated internal errors.

Installation

Install with your preferred package manager.

npm

Terminal window
npm install @browsonic/sdk

pnpm

Terminal window
pnpm add @browsonic/sdk

yarn

Terminal window
yarn add @browsonic/sdk

Quick Start

Get up and running in under two minutes.

Basic setup

import { Browsonic } from '@browsonic/sdk';
const sdk = new Browsonic();
sdk.init({
apiEndpoint: 'https://api.browsonic.com',
appKey: 'your-app-key',
apiKey: 'pk_live_…', // mint in dashboard (required when trackPageViews is on, which is the default)
});
// That's it. Errors are now captured automatically.

React integration

src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Browsonic } from '@browsonic/sdk';
import App from './App';
const sdk = new Browsonic();
sdk.init({
apiEndpoint: import.meta.env.VITE_BROWSONIC_API,
appKey: import.meta.env.VITE_BROWSONIC_APP_KEY,
apiKey: import.meta.env.VITE_BROWSONIC_API_KEY,
environment: import.meta.env.MODE,
clientVersion: import.meta.env.VITE_GIT_SHA,
debug: import.meta.env.DEV,
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

Framework-specific adapters (@browsonic/react, @browsonic/vue, @browsonic/svelte, @browsonic/angular, @browsonic/nextjs, @browsonic/astro, @browsonic/remix) add router instrumentation and navigation breadcrumbs on top of the core SDK. See the integration guide for framework-specific wiring.

Configuration

Every option accepted by init(), grouped by purpose.

Required

ParameterTypeDescription
apiEndpointstringBase URL of your Browsonic API.
appKeystringApplication identifier for multi-tenant routing.
apiKeystringTenant API key — sent as X-API-KEY. Required when trackPageViews is on (the default).

Environment & release

ParameterTypeDefaultDescription
environmentstring"production"Environment name.
clientVersionstring | nullnullClient version tag (surfaced as Versions in the dashboard).
debugbooleanfalseVerbose console logging.

Batching & transport

ParameterTypeDefaultDescription
flushIntervalMsnumber10000Interval between batch sends (ms).
maxBatchSizenumber25Maximum events per batch.
maxPayloadBytesnumber51200Maximum batch payload size (bytes; sized to stay under the 64 KB sendBeacon ceiling).

Event capture

ParameterTypeDefaultDescription
captureLevelsEventLevel[]["error"]Console levels to capture.
cooldownMsnumber60000Dedup time window (ms).

Queue & persistence

ParameterTypeDefaultDescription
maxQueueSizenumber200Maximum events held in memory.
persistQueuebooleanfalseSave queue to localStorage across reloads.

Ignore rules

ParameterTypeDefaultDescription
ignoreExtensionsbooleantrueAuto-ignore browser extension errors.
ignoreScriptErrorsbooleantrueIgnore cross-origin "Script error" messages.
ignorePatternsstring[][]Stack trace patterns to ignore.
ignoreMessagesstring[][]Error message patterns to ignore.
ignoreUrlsstring[][]URL patterns to ignore errors from.

Complete example

import { COMMON_THIRD_PARTY_PATTERNS } from '@browsonic/sdk';
sdk.init({
// Required
apiEndpoint: 'https://api.browsonic.com',
appKey: 'my-production-app',
apiKey: 'pk_live_…',
// Environment
environment: 'production',
clientVersion: 'v2.1.0',
debug: false,
// Batching
flushIntervalMs: 10000,
maxBatchSize: 25,
// Deduplication
cooldownMs: 60000,
// Capture
captureLevels: ['warn', 'error'],
// Queue
maxQueueSize: 300,
persistQueue: true,
// Privacy
redactKeys: ['token', 'password', 'secret'],
// Ignore Rules (filter noise)
ignoreExtensions: true,
ignoreScriptErrors: true,
ignorePatterns: COMMON_THIRD_PARTY_PATTERNS,
ignoreMessages: ['ResizeObserver loop'],
});

API Reference

All methods available on the SDK instance.

captureMessage(message, level?)

Capture a message manually. level defaults to info.

sdk.captureMessage('User completed checkout', 'info');
sdk.captureMessage('Payment failed', 'error');

captureError(error)

Capture an Error instance manually.

try {
await riskyOperation();
} catch (error) {
sdk.captureError(error as Error);
}

setUser(user) · clearUser()

Attach a user context to all subsequent events. Sensitive keys (token / password / secret / auth) are redacted before transport.

sdk.setUser({
id: 'user-123',
email: 'user@example.com',
plan: 'premium',
});
// On logout
sdk.clearUser();

addMetadata(key, value) · removeMetadata(key) · clearMetadata()

Attach custom key/value pairs to every subsequent event.

sdk.addMetadata('feature', 'checkout');
sdk.addMetadata('cartValue', 99.99);
sdk.addMetadata('isNewUser', true);
sdk.removeMetadata('feature');
sdk.clearMetadata();

Lifecycle: flush(), pause(), resume(), destroy()

MethodDescription
flush()Force immediate flush of queued events. Returns a Promise.
pause() / resume()Pause and resume event collection without losing the in-memory queue.
destroy()Destroy the SDK and release all listeners and timers.
getState()Returns 'uninitialized' | 'initializing' | 'running' | 'paused' | 'destroyed'.
getPendingCount()Number of events currently queued for transport.

Event Types

Built-in event types captured by the SDK.

TypeLevelSourceDescription
console_debuginfoconsole.debug()Debug log (verb preserved on telemetry).
console_infoinfoconsole.info()Informational log.
console_warnwarnconsole.warn()Warning log.
console_errorerrorconsole.error()Error log.
errorerrorwindow.onerrorUnhandled exception.
fatalfatalmanualCapture-on-purpose fatal report.
unhandledrejectionerrorPromise rejectionUnhandled promise rejection.
network_errorwarn / errorfetch() / XHRHTTP 4xx / 5xx response.

Wire format

interface BrowsonicEvent {
eventId: string; // Unique UUID
timestamp: string; // ISO 8601
type: EventType;
level: EventLevel;
message: string;
stack?: string | null;
context: EventContext; // url, referrer, pageAge
telemetry?: Timeline; // events leading up to error
metadata?: MetadataEntry[];
}
interface EventBatch {
batchId: string;
timestamp: string;
appKey: string;
environment: string;
clientVersion?: string;
sessionId: string;
sessionContext: SessionContext;
user?: UserContext;
events: BrowsonicEvent[];
}

Telemetry Timeline

Chronological events leading up to each error — the debugging context that turns a stack trace into a story.

Categories

Console
console.log, warn, error calls.
Network
Fetch & XMLHttpRequest requests.
Navigation
SPA route changes (History API).
Visitor
User clicks & inputs (privacy-safe).

How it works

  1. The SDK collects events in a ring buffer (FIFO, configurable size).
  2. When an error fires, the last N events ship with the error payload.
  3. The dashboard renders the timeline in the Timeline tab of each event.
  4. It answers the only question that matters: what happened right before this error?

Configuration

sdk.init({
apiEndpoint: 'https://api.browsonic.com',
appKey: 'my-app',
apiKey: 'pk_live_…',
// Telemetry Timeline
maxTelemetryEntries: 50, // ring buffer size (default 20)
includeTelemetry: true, // attach with errors (default true)
// Network telemetry
captureXHR: true,
networkTelemetry: true,
// Navigation
trackNavigation: true,
// Visitor (see Visitor Tracking)
trackVisitor: false, // OFF by default for privacy
});

Visitor Tracking

Track user interactions (clicks, inputs) in a privacy-safe manner.

Privacy-first design

Visitor tracking is off by default. When you enable it, actual input values are never stored — only patterns and lengths. Password fields are skipped entirely.

What is collected

DataExamplePurpose
Element tagbutton, inputIdentify element type.
Element text"Submit Order"Human-readable identification.
Element IDsubmit-btnIdentify specific element.
CSS classesbtn primaryDebugging context.
Input typeemail, textUnderstand input purpose.
Value length16Know input size without content.
Value patternemail, numericUnderstand input format.

What is never collected

  • Actual input values (e.g. "john@example.com").
  • Password field content — completely skipped.
  • Credit card numbers.
  • Any typed text content.

Configuration

sdk.init({
trackVisitor: true,
visitor: {
click: true, // track click events
input: true, // track input events
inputThrottleMs: 500, // throttle (default 500 ms)
},
});

Session Replay

Reconstruct what the user saw and did in the moments around an error — a DOM-level recording that plays back next to the stack trace. Session Replay is an opt-in plugin; it is not part of the default SDK.

Professional plan

Session Replay is a Professional-tier feature. On lower plans the plugin registers cleanly but the server withholds the recording entitlement, so nothing is captured. The effective sample rate is min(sampleRate × 100, plan ceiling %).

Setup

Import the plugin from the @browsonic/sdk/replay sub-path and register it before init(), exactly like any other plugin.

import { getBrowsonic } from '@browsonic/sdk';
import { sessionReplayPlugin } from '@browsonic/sdk/replay';
const sdk = getBrowsonic();
// Register the plugin BEFORE init().
sdk.register(sessionReplayPlugin());
sdk.init({
apiEndpoint: 'https://api.browsonic.com',
appKey: 'web',
apiKey: 'pk_live_…',
clientVersion: '1.0.0',
});

Masking defaults

Recording is privacy-first by default — matching Sentry parity. You have to loosen masking deliberately; you never have to remember to turn it on.

OptionDefaultEffect
maskAllTexttrueEvery text node is masked before it leaves the browser.
maskAllInputstrueInput values are masked. Password inputs stay masked even when this is false.
blockAllMediatrueImages, video and <canvas> render as sized placeholders.
sampleRate1Host-requested rate (0–1); clamped by the plan ceiling.
sdk.register(sessionReplayPlugin({
maskAllText: true, // default — mask every text node (Sentry parity)
maskAllInputs: true, // default — password inputs stay masked even if false
blockAllMedia: true, // default — images / video / canvas become placeholders
blockSelector: '.pii', // extra elements to block, rendered as a sized box
}));

The active sample ceiling is controlled from the dashboard under Settings → Session Replay — there is no server-bypassing replay switch in the browser.

Web Vitals

Report Core Web Vitals from real sessions. Like Session Replay, it is an opt-in plugin: the collector is a small native PerformanceObserver, but it changes what the SDK reports, so you turn it on consciously.

Setup

webVitalsPlugin is exported from the main @browsonic/sdk entry. Register it before init().

import { getBrowsonic, webVitalsPlugin } from '@browsonic/sdk';
const sdk = getBrowsonic();
// Register BEFORE init(). Not in the default plugin set —
// you opt in explicitly.
sdk.register(webVitalsPlugin());
sdk.init({
apiEndpoint: 'https://api.browsonic.com',
appKey: 'web',
apiKey: 'pk_live_…',
clientVersion: '1.0.0',
});

What it collects

MetricNameMeasures
LCPLargest Contentful PaintLoading — when the largest element renders.
INPInteraction to Next PaintResponsiveness — latency of user interactions.
CLSCumulative Layout ShiftVisual stability — unexpected layout movement.

Samples surface in the dashboard on the Performance page. A per-session cap (maxSamples, default 30) guards against runaway observers in long-lived SPAs.

Source Maps

Ship minified bundles and still read original TypeScript in every stack frame. @browsonic/build-tools provides bundler plugins for Vite, Webpack, Rollup and esbuild that emit and upload source maps to Browsonic at build time.

Professional plan

Symbolication of uploaded maps is a Professional-tier feature. The upload runs on any plan; symbolicated frames appear once the tenant is entitled.

release must equal clientVersion

The release you upload maps under has to match the clientVersion you pass to init() byte-for-byte — that string is the only key the service uses to pair a runtime error with its map. If they drift, frames stay minified.

Vite plugin

Emit hidden source maps (generated, uploaded, but not referenced from the shipped bundle) and add the plugin. Webpack, Rollup and esbuild use the matching sub-path (@browsonic/build-tools/webpack, /rollup, /esbuild).

vite.config.ts
import { defineConfig } from 'vite';
import { browsonicSourceMaps } from '@browsonic/build-tools/vite';
export default defineConfig({
// Emit hidden maps: symbolication works, but the map URL is
// NOT referenced from the shipped bundle.
build: { sourcemap: 'hidden' },
plugins: [
browsonicSourceMaps({
appKey: 'web',
release: '1.0.0', // MUST equal the SDK's clientVersion
// token: process.env.BROWSONIC_SOURCEMAP_TOKEN (default)
baseUrl: 'https://api.browsonic.com',
}),
],
});

Upload token

Uploads authenticate with a dedicated source-map token. It is a build-time secret — read from BROWSONIC_SOURCEMAP_TOKEN when the token option is omitted. Never inline it into browser code. If the token is missing, the plugin prints a warning and skips the upload rather than failing the build.

Terminal window
# Build-time secret — mint under Settings → Source Maps in the
# dashboard. Never expose this in the browser bundle.
export BROWSONIC_SOURCEMAP_TOKEN=bsm_…

Privacy & Security

How Browsonic handles sensitive data and what compliance posture you inherit by default.

Data collection summary

Data typeCollectedHow it is stored
Error messages✅ YesFull text.
Stack traces✅ YesFull text.
Console logs✅ YesFull text (telemetry).
Network URLs✅ YesFull URL — no body / headers.
User clicks⚠️ OptionalElement info only.
User inputs⚠️ OptionalPattern + length only.
Input values❌ NeverN/A.
Passwords❌ NeverCompletely skipped.

Privacy-safe defaults

SettingDefaultDescription
trackVisitorfalseVisitor tracking off by default.
trackNavigationtrueURL changes only, no user data.
networkTelemetrytrueURLs only — no request / response bodies.
captureAsyncStackfalsePerformance opt-in feature.

Automatic redaction

These keys are automatically redacted in localStorage, sessionStorage, cookies, and user context:

const DEFAULT_REDACT_KEYS = [
'token', 'password', 'authorization',
'secret', 'key', 'credential', 'auth',
];
sdk.init({
redactKeys: [
...DEFAULT_REDACT_KEYS,
'ssn',
'creditCard',
'bankAccount',
],
redactCookieNames: ['session_id', 'csrf_token'],
});

Pattern-based redaction

In addition to key-based redaction, the SDK scans free-text values (error messages, telemetry payloads, captured network metadata) and replaces matches with [REDACTED]:

PatternWhat it catches
Emailname@host.tld tokens anywhere in a value.
JWTeyJ… three-segment Base64 tokens.
Credit card12–19 digit runs (with optional spaces / hyphens).
Opaque secret32-char-or-longer continuous [A-Za-z0-9_-] tokens.

HTTP header filtering

Network telemetry captures only headers on an explicit allowlist — everything else is dropped. A hard blocklist (authorization, cookie, set-cookie, token, api-key, password, …) is dropped even if a user tries to add it to the allowlist. Header values are also passed through pattern redaction as defense in depth.

Compliance

GDPR
Data minimization (patterns, not values). Purpose limitation (debugging only). Consent-based tracking (off by default).
CCPA
Transparent data collection. Opt-out via configuration. No sale of personal data.

For the full security policy, see SECURITY.md on GitHub.

Troubleshooting

Common issues and how to resolve them.

SDK not capturing events

  1. Check initialization — sdk.getState() should return "running".
  2. Enable debug mode: sdk.updateConfig({ debug: true }).
  3. Verify captureLevels includes the event type you expect.

Events not reaching the server

  1. Check pending count — sdk.getPendingCount().
  2. Force flush — await sdk.flush().
  3. Verify apiEndpoint is correct and CORS is configured.
  4. Check the Network tab for 4xx / 5xx responses.

High event volume

  1. Increase cooldown — cooldownMs: 300000 (5 minutes).
  2. Reduce capture levels — captureLevels: ['error'].
  3. Increase flush interval — flushIntervalMs: 60000.