> ## 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.

# Quickstart

> Install the SDK, read a variant, and record a conversion in about ten minutes.

By the end of this page your app will be assigning users to a variant and sending
conversions back to Trevo.

You need a workspace and its SDK key. The key is in **Settings → SDK keys** and looks like
`tsk_live_…`. It is publishable — it ships in your JavaScript bundle by design, and it can
read your experiment config and post events, and nothing else: it cannot read results,
change experiments, or touch workspace settings.

## Where the key goes

Pasting the key into `init()` is fine for a first test. For anything deployed, put it in
an environment variable so rotating it never needs a code change:

| Where you run | Set this variable | Read it as |
| - | - | - |
| Next.js | `NEXT_PUBLIC_TREVO_API_KEY` | `process.env.NEXT_PUBLIC_TREVO_API_KEY` |
| Vite | `VITE_TREVO_API_KEY` | `import.meta.env.VITE_TREVO_API_KEY` |
| Node server | `TREVO_SECRET_KEY` | `process.env.TREVO_SECRET_KEY` |
| Python server | `TREVO_SECRET_KEY` | `os.environ["TREVO_SECRET_KEY"]` |
| Ruby server | `TREVO_SECRET_KEY` | `ENV.fetch("TREVO_SECRET_KEY")` |

Two rules keep this from going wrong:

* **Match the key class to the surface.** Browsers use the publishable `tsk_live_…` key.
  Server SDKs use a **server** key (`tsk_secret_…`, created in Settings → SDK keys with
  platform *Server*), which must never reach a browser — the API rejects a secret key
  sent with an `Origin` header for exactly that reason.
* **Redeploy after adding or rotating a key.** Bundlers compile public env vars into
  your JavaScript at build time, and hosts like Vercel apply env changes to *new*
  deployments only. Until you redeploy, the live build keeps the old key and its events
  are rejected.

## 1. Install

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

No bundler? Load the CDN build instead — it exposes a global called `Trevo`. Pin the exact
version in production:

```html theme={null}
<script src="https://cdn.trevosdk.com/browser/v0.3.6/trevo.min.js"></script>
```

## 2. Initialise

Once, as early as your app boots:

```ts theme={null}
import trevo from '@trevosdk/browser';

trevo.init({ apiKey: 'tsk_live_…' });
```

Call `identify()` when you know who the user is — usually right after login:

```ts theme={null}
trevo.identify(currentUser.id);
```

Identity decides which variant a user gets, so calling `identify()` later can move
someone from one variant to another. Call it as early as you can, and before rendering
anything you are testing.

## 3. Read a variant

```ts theme={null}
export function subscribeToCheckoutVariant() {
  let active = true;
  const apply = () => {
    if (!active || !trevo.isReady()) return;
    const variant = trevo.getVariant('checkout-cta');
    renderCheckout(variant);
  };

  const unsubscribe = trevo.onChange(apply);
  void trevo.ready().then(apply);
  return () => {
    active = false;
    unsubscribe();
  };
}

// Start once at the page/component lifecycle boundary. In an SPA, call the
// returned function when that boundary unmounts.
const stopCheckoutExperiment = subscribeToCheckoutVariant();
```

`getVariant()` is synchronous and does no network call — assignment is a local hash of
the user's identity and the experiment key. The same user always gets the same variant.
Keep your existing loading boundary visible until the first `apply`; reading once before
`ready()` can return control and never update when config arrives.

It returns `'control'` for an experiment key it has never seen, so shipping this code
before the experiment exists in Trevo is safe.

Calling `getVariant()` also records an **exposure** — "this user saw this variant". That
is the denominator for your results, so only call it where the user genuinely sees the
thing. To read an assignment without recording an exposure:

```ts theme={null}
const variant = trevo.getVariant('checkout-cta', { trackExposure: false });
```

## 4. Record a conversion

Nothing is tracked automatically by the browser SDK (`@trevosdk/react`'s provider is the
one exception — it emits a `page_view` on mount). A conversion exists only where you write
one:

```ts theme={null}
trevo.track('purchase_completed', { value: 49.99, plan: 'pro' });
```

Events are batched and sent in the background, including on page unload. You do not need
to flush manually, though `trevo.flush()` exists if you want to force it.

## 5. Check it arrived

Open **Data → Events** in your workspace and refresh — the table doesn't auto-update. Your
event should appear within a few seconds. The **Connect your app** drawer (topbar →
**Connect SDK**) polls live if you'd rather watch it arrive.

From a terminal, the [CLI](/install/cli#check-an-sdk-install) can wait for it instead:

```bash theme={null}
trevo connect --wait
```

It exits `0` once a new event arrives, so a coding agent can confirm the install on its own.

If nothing shows up, see [No events arriving](/troubleshooting/no-events-arriving).

## What to do next

* **Using React?** [`install/react`](/install/react) has a provider and a hook that
  handle initialisation for you.
* **On Next.js?** [`install/nextjs`](/install/nextjs) resolves variants on the server
  so the first paint is already correct — no flash of the control version.
* **Tracking conversions on a backend?** [`install/node`](/install/node),
  [`install/python`](/install/python), or [`install/ruby`](/install/ruby). Server-side
  events are immune to ad blockers and closed tabs, which improves the numbers for your
  browser experiments too.
* **Building a mobile app?** [`install/react-native`](/install/react-native) buckets users
  identically to the web and is built for offline queues and app-store constraints.
* **Multiple variants?** `defineExperiment()` gives you a typed union and makes an
  unhandled variant a compile error. See [Typed variants](/install/browser#typed-variants).

## The whole thing

```ts theme={null}
import trevo from '@trevosdk/browser';

trevo.init({ apiKey: 'tsk_live_…' });
// Call this from the app's real signed-in lifecycle, before experiment UI.
trevo.identify(currentUser.id);

const render = () => {
  if (!trevo.isReady()) return;
  const variant = trevo.getVariant('checkout-cta');
  if (variant === 'treatment') {
    showNewCheckout();
  } else {
    showCurrentCheckout();
  }
};
const unsubscribe = trevo.onChange(render);
void trevo.ready().then(render);

// later, when the user converts
trevo.track('purchase_completed', { value: 49.99, plan: 'pro' });

// In an SPA, call unsubscribe() when this UI boundary unmounts.
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.