Skip to main content
@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.

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.
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:
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. Webhooks and cron jobs have no cookie to read. State the link directly:
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:
Trevo records that event once regardless of how many deliveries arrive.

Serverless

Timers do not survive between invocations, so flush explicitly before returning:
For long-running processes, call shutdown() before exit — it stops timers and flushes once.

Local development

Pin variants without touching Trevo:
Or in code:
Forced reads never record an exposure, so your local testing never pollutes results.

Options

API summary