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

# Node

> Server-side assignment and tracking for Node, Bun, Deno, and edge runtimes.

`@trevosdk/node` runs experiments and records conversions from your backend. It uses
web-standard APIs only — `fetch`, `AbortSignal`, timers — so it runs on Node 18+, Bun, Deno,
and edge runtimes.

```bash theme={null}
npm install @trevosdk/node
```

## Why use it

**Conversions that cannot be blocked.** A `subscription_started` fired from a Stripe webhook
is never lost to an ad blocker, a closed tab, or a failed beacon. This improves the numbers
for your *browser* experiments too, which is the most common reason to adopt it before
running a single backend experiment.

**Experiments only a backend can run** — pricing and packaging, search ranking, prompt or
model choices, lifecycle email content. The variant is a code path, not a UI element.

**Properties the browser shouldn't be trusted with** — plan, seat count, MRR.

## Create a client

You need a **server** key: `tsk_secret_…`, from Settings → API keys with platform `Server`.
It is shown once. Never put it in client code — secret keys sent with an `Origin` header are
rejected.

```ts theme={null}
import { createClient } from '@trevosdk/node';

const trevo = createClient({ secretKey: process.env.TREVO_SECRET_KEY });

// Assignment needs config. Await once at startup, or every request in the
// first moments after a deploy falls back to control.
await trevo.ready();
```

Create the client at module scope — one per process, reused across requests.

## Identity is passed per call

A server holds no ambient user state, so every call takes the identity explicitly:

```ts theme={null}
const variant = trevo.getVariant('pricing-algo-v2', {
  userId: req.user?.id,
  anonymousId: req.cookies.trevo_id,
});

trevo.track('subscription_started', { userId, anonymousId }, { plan: 'pro' });
```

**Send both ids whenever you have them.** Trevo links an anonymous journey to a user the
first time it sees an event carrying both. A backend that only ever sends `userId` never
creates that link, and the user's pre-login activity is never attached to their conversion.

**Bucket with the identity the browser is using at that moment** — the user id when
identified, otherwise the `trevo_id` cookie. Anything else assigns the same person different
variants on the client and the server.

## Linking identities without a cookie

Webhooks and cron jobs have no cookie to read. State the link directly:

```ts theme={null}
await trevo.alias(anonymousId, userId);
```

Idempotent and safe to retry. An `anonymousId` belongs to one user — pointing it at a
different user is rejected and changes nothing.

## Idempotency

Payment providers replay webhooks, and a conversion counted twice inflates whichever variant
that user was in. Pass a stable `insertId` derived from the thing that happened:

```ts theme={null}
trevo.track(
  'subscription_started',
  { userId },
  { plan: 'pro' },
  { insertId: subscription.id },
);
```

Trevo records that event once regardless of how many deliveries arrive.

## Serverless

Timers do not survive between invocations, so flush explicitly before returning:

```ts theme={null}
export async function handler(event) {
  trevo.track('order_placed', { userId }, { value: 42 });
  await trevo.flush();          // rejects if delivery failed, so you can react
}
```

For long-running processes, call `shutdown()` before exit — it stops timers and flushes once.

## Local development

Pin variants without touching Trevo:

```bash theme={null}
TREVO_FORCE_VARIANTS=checkout-cta:treatment,pricing-v2:control
```

Or in code:

```ts theme={null}
createClient({ secretKey, forceVariants: { 'checkout-cta': 'treatment' } });
```

Forced reads never record an exposure, so your local testing never pollutes results.

## Options

| Option            | Default | Notes                                                                                     |
| ----------------- | ------- | ----------------------------------------------------------------------------------------- |
| `secretKey`       | —       | Required. `tsk_secret_…`                                                                  |
| `pollIntervalMs`  | —       | Config refresh cadence                                                                    |
| `flushIntervalMs` | —       | Background flush cadence; serverless should `await flush()` instead                       |
| `maxBatchSize`    | —       | Capped at 500 by the server                                                               |
| `bootstrapConfig` | —       | Assign against this before the first fetch resolves, so a cold start never serves control |
| `forceVariants`   | —       | Pin variants locally; also read from `TREVO_FORCE_VARIANTS`                               |
| `fetch`           | global  | Override for tests or runtimes without a global fetch                                     |
| `onError`         | —       | Background failures that have nowhere else to surface                                     |

## API summary

| Method                                         | Purpose                                                           |
| ---------------------------------------------- | ----------------------------------------------------------------- |
| `ready()`                                      | Resolves once initial config is loaded, or the first fetch failed |
| `getVariant(key, identity, options?)`          | Deterministic assignment                                          |
| `track(name, identity, properties?, options?)` | Queue a conversion                                                |
| `alias(anonymousId, userId)`                   | Link an anonymous journey to a user                               |
| `flush()`                                      | Send everything queued; rejects on failure                        |
| `shutdown()`                                   | Stop timers and flush once                                        |
