# Web SDK

@cohorly/web is the browser SDK. It is built on the shared core with localStorage persistence, a fetch/sendBeacon transport (so events survive tab close and navigation), and optional SPA pageview autotracking.

## Install

```bash
pnpm add @cohorly/web
# or: npm install @cohorly/web
```

## Initialize

Call `init()` once at your app entry point. Pass your project `token` so
events route to the right project; `apiHost` defaults to the hosted Cohorly
API.

```ts
import { init } from "@cohorly/web";

const cohorly = init({
  token: "YOUR_PROJECT_TOKEN",
  trackPageviews: true,
});
```

After `init()` has run once, you can import the named `cohorly` singleton
anywhere instead of threading the returned instance through your app:

```ts
import { cohorly } from "@cohorly/web";

cohorly.track("Button Clicked", { button: "checkout" });
```

`init()` is SSR-safe: browser-only APIs (localStorage, `window`,
`sendBeacon`) fall back to in-memory/no-op implementations when
unavailable.

### Options

| Option               | Type    | Notes                                                                                                                                                                             |
| -------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiHost`            | string  | Optional. Ingestion API base URL. Defaults to the hosted Cohorly endpoint.                                                                                                        |
| `token`              | string  | Project token stamped on every event and request.                                                                                                                                 |
| `trackPageviews`     | boolean | Autotrack SPA pageviews.                                                                                                                                                          |
| `flushIntervalMs`    | number  | How often the queue auto-flushes.                                                                                                                                                 |
| `batchSize`          | number  | Flush once this many events are queued.                                                                                                                                           |
| `superProperties`    | object  | Properties merged into every event from the start.                                                                                                                                |
| `debug`              | boolean | Verbose logging.                                                                                                                                                                  |
| `loadFeatureFlags`   | boolean | Evaluate feature flags on init. Default true.                                                                                                                                     |
| `sendExposureEvents` | boolean | Track `$feature_flag_called` on flag reads. Default true.                                                                                                                         |
| `flagPersistence`    | string  | `"networkOnly"`, `"networkFirst"`, or `"persistenceUntilNetworkSuccess"` (default). See [Feature flags](/feature-flags#flag-persistence).                                         |
| `flagCacheTtlMs`     | number  | How long the persisted flag cache stays valid, checked when the cache is adopted - adopted values keep serving until the next reload or identity change. Default 86400000 (24 h). |

## Track events

`track(event, properties?)` queues an event. The SDK automatically merges in
`$browser`, `$os`, `$current_url`, and screen size, plus `distinct_id`,
`time`, `$insert_id`, and `$lib`.

```ts
cohorly.track("Signed Up", { plan: "pro" });
cohorly.track("Item Added", { sku: "A-100", price: 29 });
```

## Identify

`identify(id)` switches the current user to a known id. The switch (distinct
id, persisted state) is applied synchronously, before any network call, so a
slow or offline request never delays it. On the first call after being
anonymous it then sends an `/alias` request so pre-login and post-login
activity link into one profile; the request is best effort - a failure
neither throws nor blocks, since the server also links the anonymous and
identified ids implicitly from the next event that carries both (see
[Identity](/reference/data-model#identity)).

```ts
await cohorly.identify("user_123");
```

Use `reset()` on logout to generate a fresh anonymous id. This matters on
shared devices: without it, the next person to use the device inherits the
previous person's anonymous history.

```ts
cohorly.reset();
```

## Super properties

Super properties are merged into every subsequent event. Register and
unregister them with `register()` / `unregister()`. Event-level properties
override super properties of the same key.

```ts
cohorly.register({ app_version: "1.2.3", plan: "free" });
cohorly.track("Viewed Page"); // includes app_version and plan
cohorly.unregister("plan");
```

## People profiles

Update the current user's profile through `cohorly.people`. These map to the
`/engage` endpoint.

```ts
await cohorly.people.set({ name: "Ada Lovelace", plan: "pro" });
await cohorly.people.setOnce({ first_seen: Date.now() });
await cohorly.people.increment({ logins: 1 });
await cohorly.people.unset(["temp_flag"]);
await cohorly.people.delete();
```

## Feature flags

Flags are evaluated on init by default and cached per identity (localStorage,
scoped by `distinct_id`, bounded by `flagCacheTtlMs`). The cache serves
immediately on the next launch until the first successful reload replaces it;
`flagPersistence` controls that policy - under the default
`"persistenceUntilNetworkSuccess"` an expired cache still serves up to 7
days stale, while `"networkFirst"` serves only inside the TTL.

```ts
const value = cohorly.getFeatureFlag("checkout-redesign", "control");
const enabled = cohorly.isFeatureEnabled("checkout-redesign", false);
const details = cohorly.getFeatureFlagDetails("checkout-redesign");
// details.source: "network" | "persistence" | "fallback"
```

A read that resolves to the fallback is a default, not an exposure. See
[Feature flags](/feature-flags#flag-persistence) for the persistence policies,
TTL, and source semantics.

## Flush and accessors

```ts
await cohorly.flush(); // send the queued batch now
const id = cohorly.getDistinctId();
const anon = cohorly.isAnonymous();
```

The queue is persisted to localStorage, so events survive reloads and are
retried on the next flush if a request fails.

## Keeping the token out of the bundle

Passing `token` to `init()` embeds it in your client JS. If you would rather
not, proxy ingestion server-side with `@cohorly/nextjs`'s
`createCohorlyProxy()` and omit `token` here. See the
[Next.js SDK](/sdks/nextjs).
