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

# Exposure vs conversion

> The two events Trevo records, and why the distinction decides whether your results mean anything.

Trevo records exactly two kinds of event. Getting the difference right is most of what
separates a trustworthy experiment from a misleading one.

|       | Exposure                             | Conversion                            |
| ----- | ------------------------------------ | ------------------------------------- |
| Means | "This user saw this variant"         | "This user did the thing that counts" |
| Fires | Automatically, inside `getVariant()` | Only where you call `track()`         |
| Role  | The denominator                      | The numerator                         |

```
conversion rate = conversions ÷ exposures
```

Both halves have to be right. A wrong denominator is harder to notice than a wrong numerator,
and does just as much damage.

## Exposure

Recorded when `getVariant()` resolves:

```ts theme={null}
const variant = trevo.getVariant('checkout-cta');   // exposure recorded here
```

Deduplicated per identity and variant, so calling it repeatedly in a render loop doesn't
inflate anything.

### Only where the user genuinely sees it

This is the trap. Exposure means *saw*, not *was assigned*. Resolve a variant in a component
that renders off-screen, or on a page where the tested element is below the fold and never
scrolled to, and you've added people to the denominator who never had the chance to convert.

The effect is always the same: your measured lift shrinks toward zero, because you've diluted
both arms with users who couldn't have responded. A real winner can be buried this way.

Two tools:

```ts theme={null}
// read the assignment without recording an exposure
const variant = trevo.getVariant('checkout-cta', { trackExposure: false });

// record it later, when the thing is actually visible
trevo.trackExposure('checkout-cta', variant);
```

Use that pair when you need the variant early — to fetch data, to decide a layout — but the
user won't see the result until later.

## Conversion

Nothing is captured automatically. There is no autocapture, no click tracking, no implicit
pageviews. A conversion exists only where you write one:

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

Names are up to 500 characters; properties up to 8KB serialised.

### Pick events that mean something

The best conversion events sit as close to real value as you can get while still happening
often enough to measure.

* `purchase_completed` — unambiguous, but rare, so it needs a lot of traffic
* `checkout_started` — more frequent, and a decent proxy
* `button_clicked` — plentiful and nearly meaningless on its own

A common shape is a frequent event as the decision metric and a rarer, more valuable one as a
[guardrail](/concepts/reading-results), so you can move fast without optimising for a click that
leads nowhere.

### Record them server-side where you can

Browser events are lost to ad blockers, closed tabs, and flaky networks. Anything your
backend knows about — payments especially — is more reliably recorded there:

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

See [Node](/install/node) or [REST](/install/rest). This improves the numbers for
your browser experiments too, because the denominator stays in the browser while the
numerator becomes reliable.

## How they meet

An exposure and a conversion are joined by **identity**. Trevo counts a conversion toward
whichever variant that identity was exposed to.

Which is why sending both `userId` and `anonymousId` from a backend matters: an event
carrying only `userId` from a user whose exposure was recorded anonymously has nothing to
join on, and that conversion is lost from the results. The SDKs send both automatically when
they can; on REST, it's your job.

## Symptoms of getting this wrong

| What you see                     | Likely cause                                                          |
| -------------------------------- | --------------------------------------------------------------------- |
| Exposures, no conversions        | No `track()` call is being reached                                    |
| Conversions far exceed exposures | Converting users were never exposed — check where `getVariant()` runs |
| Lift always near zero            | Denominator diluted with users who never saw the change               |
| Backend conversions don't count  | Events sent with only `userId`, no `anonymousId`                      |
