> ## Documentation Index
> Fetch the complete documentation index at: https://docs.onepatch.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser monitoring

> See what a person did in your web app — pages, clicks, failed requests and JS errors, joined to your backend traces.

`@onepatch/rum` records page views, clicks, `fetch` and XHR requests, JS errors and web vitals, and links each request to the backend trace it caused.

Paste this into your coding agent, with the endpoint and token from **Workspace settings → integrations**:

```text theme={null}
Add OnePatch browser monitoring to this frontend.

First, install the setup skill (one-time):
npx skills add 1patch/skills -s rum-instrument

Then instrument the browser with the URL and write-only token.
Endpoint: https://your-org.logger.onepatch.dev. Token: op_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
```

The token is write-only, so it is safe to commit and ship in your bundle, like a Sentry DSN.

Commit and push. Sessions arrive the next time someone loads your app.

## If your API is on another origin

Same-origin requests need no setup. For an API on a different origin:

1. List that origin in `connectTracesTo`.
2. Add `traceparent` to that API's `Access-Control-Allow-Headers`. If the API accepts cookies, list `traceparent` by name — a `*` wildcard is not valid for requests that send credentials.

## By hand

```sh theme={null}
bun add @onepatch/rum
```

```ts theme={null}
import { identifyUser, startRum } from "@onepatch/rum";

await startRum({
  ingestUrl: "https://your-org.logger.onepatch.dev",
  ingestToken: "op_…",
  appName: "acme-web",       // becomes service.name; pick one and keep it
  environment: "production",
  appVersion: commitSha,     // required
  connectTracesTo: ["https://api.acme.com"], // cross-origin backends only
  // Required: the person, a resolver awaited once, or "anonymous".
  user: async () => (await getSession())?.user ?? null,
});

// Later, when it changes — a sign-in, a workspace switch, a sign-out:
identifyUser({ id: user.id, email: user.email, orgId: user.orgId });
```

Call `startRum` once, on a path that only runs in the browser — it does not work during server-side rendering.

`user` has no default, because telemetry with nobody attached cannot answer "what did *this* person do", which is most of why you want it. A resolver may return `null` — a login page, a load before the session request lands — and `(await startRum(…)).identified` tells you whether anyone was attached. Pass `user: "anonymous"` only for an app with no sign-in.

On a sign-out or a workspace switch, pass `null` for what no longer applies: `identifyUser({ id: null, orgId: null })`. An absent key leaves the previous value on every later span.

The [package README](https://github.com/1patch/rum) documents every option.

## Privacy

The DOM is never captured, so keystrokes and form contents never leave the browser.

Query strings are recorded. Set `scrubQueryStrings: true` if your URLs carry password-reset tokens or email addresses.
