# SDK options

> release, environment, sampling, ignoring errors, opt-in breadcrumbs and attachments, delivery and queue limits.

Options for `init` (browser, React, TanStack Start client) and for the `withBelvador` callback (Cloudflare Workers, TanStack Start server). Every option except `dsn` is optional. Invalid values fall back to the default instead of throwing.

## Common options

| Option | Type | Default | What it does |
|---|---|---|---|
| `dsn` | `string` | — (required in the browser) | The project DSN. On Workers it can come from `BELVADOR_DSN` instead. |
| `release` | `string` | not set | Your app's version (build number, commit). Used to find regressions and to match [source maps](https://belvador.dev/en/docs/source-maps). |
| `environment` | `string` | `"production"` | `production`, `staging`, `preview`… Shown and filterable on issues. |
| `dist` | `string` | not set | A build variant within one release, if you ship several. |
| `sampleRate` | `number` 0–1 | `1` | Share of error events to send. `0.25` sends about a quarter. |
| `ignoreErrors` | `(string \| RegExp)[]` | `[]` | Errors to drop. A string matches part of the message; a RegExp is tested against it. |
| `captureAborted` | `boolean` | `false` | Also report work the visitor cancelled: an `AbortError` (or an error caused by one), or a server error thrown after the client disconnected. Off because a closed tab is not a bug. A `TimeoutError` is always reported. Since 0.2.0. |
| `tracePropagation` | `{ targets: (string \| RegExp)[], baggage?: boolean }` | off | Add `traceparent` and `sentry-trace` headers to requests to these URLs, so errors and job runs across your services link up. See [Link errors across tiers](https://belvador.dev/en/docs/sdk/tracing). Since 0.3.0. |
| `beforeSend` | `(event, hint) => event \| null` | not set | Change or drop an event. Return `null` to drop. May return a promise. |
| `beforeBreadcrumb` | `(crumb, hint) => crumb \| null` | not set | Change or drop a breadcrumb. |
| `maxBreadcrumbs` | `number` 0–100 | `50` | How many recent breadcrumbs travel with an event. |
| `attachments` | `boolean` | `false` | Allow attachments to be sent. Off means attachments are dropped in your app. |
| `privacyPolicy` | `privacyPolicy(json)` | not set | Apply the project's privacy rules in your app too, from **Copy as code** on the Privacy page. Your node applies them either way. See [Data protection](https://belvador.dev/en/docs/privacy). Since 0.6.0. |
| `preview` | `boolean` | `false` | Print each envelope as it would be sent and send nothing. On servers `BELVADOR_PREVIEW=1` does the same. Since 0.6.0. |
| `debug` | `boolean` | `false` | Log the SDK's decisions to the console. Turn off again for production. |
| `disabled` | `boolean` | `false` | Keep the SDK off, for example in tests. |

`beforeSend` and `beforeBreadcrumb` run **before** Belvador's mandatory scrubbing, so they cannot add data the scrubber would remove. If a hook throws or its promise rejects, that event or breadcrumb is dropped.

```ts
init({
  dsn: "https://<public-key>@ingest.belvador.dev/<project-id>",
  release: import.meta.env.VITE_APP_VERSION,
  environment: import.meta.env.MODE,
  sampleRate: 1,
  ignoreErrors: ["ResizeObserver loop completed with undelivered notifications"],
  beforeSend(event) {
    // Drop errors from an internal admin tool.
    if (event.request?.url?.includes("/internal-admin/")) return null;
    return event;
  },
});
```

## Breadcrumbs

Breadcrumbs are the steps before an error. Automatic breadcrumbs are **off** in Belvador's SDKs: they record what users click and which URLs they call, which can include personal data. Turn on only what you need.

**Browser** (`@belvador/browser`, `@belvador/react`, TanStack Start client):

```ts
init({
  dsn: "https://<public-key>@ingest.belvador.dev/<project-id>",
  instrument: { console: true, dom: true, fetch: true, xhr: true, history: true },
});
```

**Workers** (`withBelvador` callback): `instrument: { console: true, fetch: true }`.

Breadcrumbs you add with `addBreadcrumb` are always recorded and need no option.

## Browser-only options

| Option | Default | What it does |
|---|---|---|
| `instrument` | all off | Automatic breadcrumbs, see above. |
| `wrapBrowserApis` | `false` | Wraps timers, `requestAnimationFrame` and event listeners so errors inside them carry more context. |
| `allowUrls` | all | Only report errors whose stack comes from these script URLs (strings or RegExps). |
| `denyUrls` | none | Ignore errors from these script URLs. Browser extension URLs are always ignored. |
| `captureViewTransitionSkips` | `false` | Also report a View Transition the browser skipped on purpose (page hidden, another transition started, `skipTransition()`). Off because the browser is working as designed. Since 0.3.0. |
| `offline` | `false` | Keep errors that fail on the network in this browser (IndexedDB) and send them when it is back online or on the next load. See [Offline and durable delivery](https://belvador.dev/en/docs/sdk/offline). Since 0.6.0. |

## Workers-only behaviour

The `withBelvador` callback returns the same options without a required `dsn`, plus `instrument: { console, fetch }`. Values not returned come from Worker variables:

| Setting | Taken from, in order |
|---|---|
| DSN | callback `dsn` → `BELVADOR_DSN` |
| Release | callback `release` → `BELVADOR_RELEASE` → `CF_VERSION_METADATA.id` |
| Environment | callback `environment` → `BELVADOR_ENVIRONMENT` → `production` |
| Off switch | callback `disabled` → `BELVADOR_DISABLED` = `1` or `true` |

## Fixed by Belvador

These cannot be changed, so the SDK sends only errors and never adds personal data on its own:

- personal data from the SDK (`sendDefaultPii`) is always off;
- tracing, session replay, logs and profiling are off and their data is dropped;
- no tunnel, no Spotlight, no default integrations beyond what Belvador sets up;
- string values are cut at 8,192 characters.

## How sending works

| Rule | Value |
|---|---|
| Waiting to be sent | at most 30 envelopes, 1 MiB in total (6 MiB with attachments) |
| One attachment | at most 5 MiB |
| One attempt | aborted after 10 seconds |
| Network errors and `500`/`502`/`503`/`504` | up to 5 attempts; each wait is random, below 1, 2, 4, then 8 seconds (full jitter, since 0.6.0); the same envelope and event id every time |
| `503` with `Retry-After` | waits as asked, up to 60 seconds |
| `429` | follows the rate-limit headers: new events of that kind are dropped in your app until the limit lifts |
| `401` / `403` | the key is invalid or revoked: sending stops for the life of the page or Worker isolate |
| Storage | memory only — nothing is written to disk or `localStorage`, unless you turn on [`offline`](https://belvador.dev/en/docs/sdk/offline) (browser) or use `@belvador/spool` (servers) |

When a page is closing, the SDK sends with `keepalive` within the browser's limits (up to 60,000 bytes, 15 requests at a time). Events that do not fit, or that are still waiting when the page closes, are lost — unless `offline: true` keeps them for the next load.
