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

# Browser SDK

> Send user events from your frontend with @dataframer/signals

`@dataframer/signals` is a small browser SDK that sends user/product events to DataFramer and generates the journey id that ties those events to your AI traces.

## Install

```bash theme={null}
npm install @dataframer/signals
```

## Get a write key

In your DataFramer dashboard, go to **Profile → Keys** and create a Signals SDK Write Key. On DataFramer hosted by us that's [app.dataframer.ai/profile?tab=keys](https://app.dataframer.ai/profile?tab=keys); on a private deployment it's the same **Profile → Keys** page on your own instance.

This key is meant to only send events in, not read data out.

## Initialize once, at app start

```ts theme={null}
import { DataframerSignals } from '@dataframer/signals';

const signals = new DataframerSignals({
  apiBase: 'https://your-dataframer-api',   // where /api/signals/* lives
  writeKey: 'YOUR_WRITE_KEY',
});
```

## Send events

```ts theme={null}
signals.identify('user@acme.com', { plan: 'pro' });   // sets the journey's user
signals.track('RecommendationCancelled', { itemId: 'abc', reason: 'irrelevant' });
```

Sending is best-effort: it will never throw an error into your app.

## Journey ids

Every event you send carries a `journey_id` automatically:

* **Default:** a journey id is created for you. By default, it resets after 30 minutes of inactivity. It is configurable.
* **Use your own id:** if you already have a natural id (order id, conversation id, ticket id), you can use that:

```ts theme={null}
signals.startJourney('order-1234');   // pin your own id, never auto-resets
signals.resetJourney();               // end it, start a fresh one
signals.journeyId;                    // read the current id
```

## CORS: getting the journey id to your own backend

This section is about attaching the journey id to the requests your frontend makes to **your own backend**, so your server can stamp that id onto your AI traces.

How the journey id reaches your backend depends on where your frontend and your backend live.

<Tabs>
  <Tab title="Same site: nothing to do">
    If your frontend and your backend are on the same domain (or share a parent domain, like `app.acme.com` and `api.acme.com` with cookies set on `.acme.com`), the journey id rides along as a cookie automatically. **You don't need to configure anything.**
  </Tab>

  <Tab title="Different domains: two steps">
    If your frontend and your backend are on different domains (for example, your product frontend calling your own API on a separate domain), cookies do not reliably cross that boundary. Do two things:

    1. **Tell the SDK to forward the id as a header** on requests to your backend, by listing your backend's URL:

    ```ts theme={null}
    const signals = new DataframerSignals({
      apiBase: 'https://your-dataframer-api',
      writeKey: 'YOUR_WRITE_KEY',
      propagateHeaderTo: ['https://your-backend'],   // your own backend, not DataFramer
    });
    ```

    This patches `fetch`/`XHR` so any request to `https://your-backend` automatically gets an `X-Journey-Id` header attached.

    2. **Allow that header in your backend's CORS config**, the same way you'd already allow `Authorization` or `Content-Type`. For example, with Django's `django-cors-headers`:

    ```python theme={null}
    from corsheaders.defaults import default_headers

    CORS_ALLOW_HEADERS = list(default_headers) + ['x-journey-id']
    ```

    Without this, the browser's CORS preflight will reject the request before it reaches your server. This is exactly what we had to add for our own dashboard, since our frontend and backend sit on different domains.
  </Tab>
</Tabs>

## Config reference

| Option                | Default | What it does                                                    |
| --------------------- | ------- | --------------------------------------------------------------- |
| `apiBase`             | none    | Base URL where `/api/signals/*` lives                           |
| `writeKey`            | none    | Your public write key                                           |
| `journeyIdleMinutes`  | `30`    | Minutes of inactivity before an auto journey id resets          |
| `disableAutoRotation` | `false` | Never auto-reset the journey id                                 |
| `propagateHeaderTo`   | `[]`    | Origins to attach the `X-Journey-Id` header to (see CORS above) |

## Next steps

<Card title="Server Instrumentation" icon="server" href="/user-signals/server-instrumentation">
  Stamp this same journey id onto your AI traces automatically.
</Card>
