SDK reference
Everything is imported from @marsadco/sdk, with React helpers under @marsadco/sdk/react (web) and @marsadco/sdk/react-native (React Native / Expo). The runtime (browser / Node / React Native) is auto-detected and the right integration — including runtime context and breadcrumbs — installed for you.
init(config)
Configures and starts the SDK, returning a MarsadClient. Everything is optional — key falls back to the environment (see below).
| Option | Type | Default | Description |
|---|---|---|---|
| key | string | env | Project ingest key (bk_live_…). Optional if set in the env — init() reads MARSAD_KEY / NEXT_PUBLIC_MARSAD_KEY / EXPO_PUBLIC_MARSAD_KEY. With neither arg nor env, init() throws (pass enabled: false to disable). |
| endpoint | string | hosted | Ingest base URL. Override only for self-hosted / staging. |
| environment | string | NODE_ENV | Deployment environment tag. |
| release | string | auto | Release / version identifier for grouping by deploy. Auto-detected from CI env vars when unset (see Release auto-detection). |
| platform | Platform | auto | Override the detected platform: web | node | ios | android. |
| enabled | boolean | true | Master switch. Set false to no-op the SDK (e.g. in dev). |
| debug | boolean | false | Log SDK activity to the console. |
| sampleRate | number | 1 | Fraction of error / log events to send, 0–1. Fallback for the per-type rates below. |
| errorSampleRate | number | sampleRate | Fraction of error events to send. Set 1 to keep every error while sampling logs. |
| logSampleRate | number | sampleRate | Fraction of log / message events to send. |
| networkSampleRate | number | 1 | Fraction of network events to send (not governed by sampleRate). |
| tracesSampleRate | number | 0 | Fraction of traces to record (performance spans), 0–1. Default 0 (tracing off). Decided once per trace at the root; children inherit, so a trace is recorded whole or not at all. |
| tracePropagationTargets | Matcher[] | same-origin | URLs to inject the W3C traceparent header on so a backend continues the trace. Default: same-origin only — keep it to your own APIs (sending trace ids to third parties triggers CORS preflights). |
| tags | Record<string, string> | {} | Initial indexed tags applied to every event. Add more at runtime via setTag() / setTags(); filterable on the dashboard. |
| ignoreErrors | Matcher[] | [] | Drop errors whose message / type matches. |
| denyUrls | Matcher[] | [] | Drop errors originating from a matching script URL. |
| captureUnhandled | boolean | true | Install global error / unhandledrejection handlers. |
| captureNetwork | boolean | true | Capture fetch / XHR as network events. |
| ignoreUrls | Matcher[] | [] | Exclude matching URLs from capture (errors + network). |
| network | NetworkOptions | — | Fine-grained network controls (see below). |
| sessions | boolean | true | Track a session per app start, marked crashed on an unhandled error — powers crash-free rate on the Overview. |
| webVitals | boolean | true (web) | Capture Web Vitals (LCP / CLS / INP / FCP / TTFB) on the web — shown as a p75 card on the Overview. |
| feedback | boolean | true | Show the built-in FeedbackWidget + the crash-report form in the default ErrorBoundary. captureFeedback() still works when off. |
| screenshot | ScreenshotCapture | — | Capturer for screenshot-on-error (off by default; you supply the pixel capture, e.g. html2canvas). The shot is attached to the issue. See Screenshots & attachments. |
| breadcrumbs | boolean | true | Auto-capture console logs, navigation, clicks and network requests as breadcrumbs, attached to the next error. |
| maxBreadcrumbs | number | 30 | Breadcrumbs retained and attached to errors. 0 disables. |
| storage | MarsadStorage | null | auto | Persist the event queue so events survive reload / offline / crash (localStorage on web, AsyncStorage on React Native). Set null to disable. |
| flushInterval | number | 5000 | Flush the queue every N ms. |
| maxBatchSize | number | 30 | Max events per request. |
| maxQueueSize | number | 100 | Max events buffered before the oldest are dropped. |
| maxQueueAgeMs | number | 86400000 | Drop persisted events older than this (default 24h) so stale events from old sessions aren't replayed. |
| user | MarsadUser | — | Initial user. Change later via setUser(). |
| beforeSend | (e) => e | null | — | Last-chance transform / drop per event. Return null to drop. |
| beforeBreadcrumb | (c) => c | null | — | Last-chance transform / drop per breadcrumb (mirrors beforeSend). |
| breadcrumbCategories | { console?, navigation?, click?, http? } | {} | Turn off individual auto-breadcrumb categories, e.g. { console: false }. |
NetworkOptions
Passed as network on the config. Matcher is a string (substring) or RegExp; StatusMatcher is a code (404) or an inclusive range ([500, 599]).
| Option | Type | Default | Description |
|---|---|---|---|
| captureBody | boolean | false | Capture request / response bodies (truncated). |
| maxBodyLength | number | 2048 | Max captured body length in chars. |
| captureHeaders | boolean | false | Capture request / response headers (scrubbed). |
| minDuration | number | — | Only capture requests slower than N ms. |
| onlyStatusCodes | StatusMatcher[] | — | Only capture matching statuses (failures always kept). |
| ignoreStatusCodes | StatusMatcher[] | — | Never capture matching statuses. |
| allowUrls | Matcher[] | — | Allow-list: capture ONLY matching URLs; all others dropped. |
Methods
Top-level functions are no-ops before init(), so they're always safe to call.
init(config): MarsadClientInitialize and install the runtime integration. Call once at startup; a second call is ignored.captureException(error, ctx?): stringReport an error manually. ctx can set level, user, context, mechanism, handled, tags (indexed/filterable) and fingerprint (string[] to force grouping). Returns a short reference code (MAR-…) for the event.captureMessage(message, level?, ctx?): stringReport a string event. level defaults to "info". Same ctx as captureException (incl. tags + fingerprint). Returns a short reference code (MAR-…).captureFeedback({ message, name?, email?, eventCode? }): voidSend a user feedback report. Pass the reference code returned by captureException as eventCode to tie it to that issue. Surfaced on the Feedback page.setUser(user | null): voidAttach the current user to subsequent events, or pass null to clear it on logout.setTag(key, value): voidSet a single indexed tag on subsequent events — filterable on the dashboard and shown as clickable chips on the issue detail.setTags(tags): voidSet multiple indexed tags at once, e.g. { region: "eu", tenant: "acme" }.setContext(key, value): voidAttach arbitrary structured data to every event.addBreadcrumb(crumb): voidRecord a trail entry (navigation, click, log) shown with the next error, on top of the auto-captured breadcrumbs.metrics.increment / distribution / gauge(name, value?, { unit?, tags? })Record a custom metric — counters, distributions (percentiles) and gauges, aggregated on the Metrics page.startSpan({ op, name?, attributes? }, callback): TRun a callback inside a performance span (sync or async); records its duration on the Performance page. Spans started inside it nest into one trace. Requires tracesSampleRate > 0.startInactiveSpan(opts) / getActiveSpan()Start a span you end() manually, or read the current active span (e.g. to add attributes or read its traceparent for manual propagation).flush(): Promise<void>Send any queued events now — e.g. before a serverless function exits.getClient(): MarsadClient | nullThe active client, or null before init().close(): Promise<void>Uninstall handlers, flush and reset. Mainly for tests / HMR.ErrorBoundary
Captures render-time errors in its subtree (with the component stack) and renders a fallback. Import from @marsadco/sdk/react on the web and @marsadco/sdk/react-native in React Native / Expo. Props: children, fallback (a node, or (error, reset) => node), and onError(error, info). With no fallback, the default UI shows Something went wrong, the event's reference code (Reference: MAR-…), a Try again button, and a one-tap Report a problem form pre-linked to that crash (hidden when init({ feedback: false })) — rendered with native View / Text on React Native.
Both the ErrorBoundary and FeedbackWidget live in @marsadco/sdk/react (web) and @marsadco/sdk/react-native (React Native) — @marsadco/sdk/expo re-exports the React Native ones alongside its init().
import { ErrorBoundary } from "@marsadco/sdk/react";
<ErrorBoundary
fallback={(error, reset) => (
<Crash message={error.message} onRetry={reset} />
)}
>
<App />
</ErrorBoundary>;import { ErrorBoundary } from "@marsadco/sdk/react-native";
// No fallback → native "Something went wrong" + "Reference: MAR-…"
<ErrorBoundary>
<RootNavigator />
</ErrorBoundary>;Framework helpers
Thin wrappers that read the key from the environment for you, so most apps never pass key at all.
@marsadco/sdk/nextjs exports register (server, reads MARSAD_KEY) and initBrowser() (reads NEXT_PUBLIC_MARSAD_KEY), and re-exports ErrorBoundary.
// instrumentation.ts (server)
export { register } from "@marsadco/sdk/nextjs";
// a "use client" component mounted once in app/layout.tsx
"use client";
import { useEffect } from "react";
import { initBrowser } from "@marsadco/sdk/nextjs";
export function MarsadInit() {
useEffect(() => initBrowser(), []);
return null;
}@marsadco/sdk/expo exports init() (reads EXPO_PUBLIC_MARSAD_KEY) and re-exports the React Native ErrorBoundary.
import { init, ErrorBoundary } from "@marsadco/sdk/expo";
init(); // key from EXPO_PUBLIC_MARSAD_KEY
export default function App() {
return (
<ErrorBoundary>
<RootNavigator />
</ErrorBoundary>
);
}Tags
Tags are indexed string key/value pairs you can filter by on the dashboard (the issue detail shows them as clickable chips). Set defaults via the tags config option, update them at runtime with setTag() / setTags(), or attach per-event tags via ctx.tags.
import { setTag, setTags, captureException } from "@marsadco/sdk";
setTag("plan", "pro");
setTags({ region: "eu", tenant: "acme" });
captureException(err, { tags: { feature: "checkout" } });Web Vitals
On the web, the SDK captures Core Web Vitals — LCP, CLS, INP, FCP and TTFB — automatically, with no extra code. They appear as a p75 card on your Overview. Disable with webVitals: false.
User feedback
Collect a report from the user and tie it to the issue they hit by passing the reference code returned from captureException() as eventCode. Feedback shows up on the dashboard's Feedback page.
import { captureException, captureFeedback } from "@marsadco/sdk";
const code = captureException(err);
captureFeedback({
message: "Pay button did nothing!",
email: "a@b.com",
eventCode: code, // links the report to that issue
});For a ready-made form, drop in <FeedbackWidget /> from @marsadco/sdk/react (or /react-native) — a floating button that opens a message form and submits for you, auto-tagged with the latest event code.
import { FeedbackWidget } from "@marsadco/sdk/react";
<FeedbackWidget />Custom metrics
Track counters, distributions and gauges. They're aggregated on the Metrics page (counters → count + total; distributions → avg / p50 / p95 / max) over the selected window. Add dimensions with tags.
import { metrics } from "@marsadco/sdk";
metrics.increment("checkout.completed");
metrics.distribution("image.upload.ms", 842, { unit: "ms", tags: { region: "eu" } });
metrics.gauge("queue.depth", 12);Performance tracing
Set tracesSampleRate above 0 to record traces. On the web the SDK opens a pageload transaction on load and a navigation one per route change, and wraps every fetch / XHR in an http.client span — so each page becomes one trace you can open as a waterfall on the Performance page. Wrap your own work with startSpan; nested spans join the same trace.
import { init, startSpan } from "@marsadco/sdk";
init({ tracesSampleRate: 1.0 }); // record all traces (lower in production)
const user = await startSpan({ op: "db.query", name: "load user" }, async () => {
return db.users.find(id); // duration recorded; child spans nest under it
});The SDK injects a W3C traceparent header on same-origin requests (configurable via tracePropagationTargets). On a Next.js backend, wrap route handlers / server actions to continue that trace — the server spans nest under the same trace, so a slow page links to the backend call that caused it:
import { wrapRouteHandler, withMarsad } from "@marsadco/sdk/nextjs";
export const GET = wrapRouteHandler(async (req) => { /* … */ });
export const submit = withMarsad(async (data: FormData) => { /* … */ }, "submit");Screenshots & attachments
Attach a screenshot of the broken screen to every error. It's off by default (screenshots can contain personal data), and the SDK stays dependency-free — you supply the pixel capture, the SDK handles timing (raced against a 2s timeout), size-capping, and attaching it. Pass a function that returns a data URL / base64:
import html2canvas from "html2canvas";
init({
screenshot: async () =>
(await html2canvas(document.body)).toDataURL("image/jpeg", 0.7),
});
// React Native: react-native-view-shot's captureScreen() works the same way.The shot shows up inline on the issue. You can also attach arbitrary files to a specific capture as an escape hatch (a log dump, serialized state) via captureException(err, { attachments }) — keep them small and free of secrets; most teams are better served by tags, breadcrumbs, and screenshots.
Native crashes — React Native (experimental)
The JS SDK already captures JS-thread errors. Native crashes (iOS signals/NSException, Android JVM/ANR) happen below JS and need a native module — added via our Expo config plugin. It captures the crash, persists it, and the SDK sends it on the next launch with your latest user / tags / breadcrumbs attached.
{
"expo": {
"plugins": [
["@marsadco/sdk", { "key": "bk_live_…", "endpoint": "https://…" }]
]
}
}Then npx expo prebuild + a dev/EAS build (native crash capture can't run in Expo Go). Upload debug files in CI so stacks symbolicate — iOS dSYM UUIDs are read automatically; pass the Android ProGuard mapping's id:
npx marsad upload-debug-files --platform ios ./ios/build
npx marsad upload-debug-files --platform android --proguard-id <uuid> ./android/app/buildVerify with nativeCrash() from @marsadco/sdk/expo (release builds only). Native capture is rolling out — Android JVM/ANR + iOS exceptions first; full NDK minidumps later.
Auto-context — setContextProvider
Every event is auto-enriched with runtime context: on the browser — url, userAgent, language, screen, viewport, referrer; on Node — runtime, nodeVersion, os, arch; on React Native — runtime and hermes. Augment or replace it with a provider on the client:
import { getClient } from "@marsadco/sdk";
getClient()?.setContextProvider(() => ({
tenant: currentTenant(),
featureFlags: activeFlags(),
}));Release auto-detection
If release isn't set, the SDK resolves it from the first of these env vars it finds — so deploys group themselves in CI without extra config:
MARSAD_RELEASE, NEXT_PUBLIC_MARSAD_RELEASE, EXPO_PUBLIC_MARSAD_RELEASE, VERCEL_GIT_COMMIT_SHA, SOURCE_VERSION (Heroku), RENDER_GIT_COMMIT, CF_PAGES_COMMIT_SHA, GITHUB_SHA.
Either run npx marsad inject after your build (debug ids match maps to the exact build automatically) or pass the same release value to npx marsad upload-sourcemaps --release … so your maps line up with the events from that build.
Event reference codes
captureException() and captureMessage() return a short, user-facing code like MAR-K7P2QX9M. Show it to users (the ErrorBoundary default fallback already does, as Reference: MAR-…), then search it in the dashboard's ⌘K command palette to land on the exact issue.
Example
A typical setup with route exclusions, a user, and manual capture:
import {
init, setUser, addBreadcrumb, captureException,
} from "@marsadco/sdk";
init({
key: process.env.MARSAD_KEY!,
release: "web@1.4.0",
ignoreUrls: ["/health", /\/_next\//],
network: {
allowUrls: ["https://api.myapp.com"], // only our backend
ignoreStatusCodes: [[200, 399]], // and only 4xx/5xx + failures
minDuration: 500, // …that are also slow
},
});
setUser({ id: user.id, email: user.email });
addBreadcrumb({ category: "ui", message: "Checkout opened" });
try {
await checkout(cart);
} catch (err) {
captureException(err, { context: { orderId } });
throw err;
}The SDK never reports requests to its own ingest endpoint, and scrubs obvious secrets (auth headers, secret-looking params and JSON fields) before anything leaves the device.