Skip to main content

Record Events from a browser

A secret must never reach a browser, so the browser has its own kind of key and its own package, @throughlinehq/browser. It can only record Events, for the signed-in Contact, from the sites you allow. Creating, reading and erasing Contacts stays on your server.

You need two things from Settings → API keys:

  • A publishable key (pk_live_…), with the origins your pages are served from, such as https://app.example.com. It is meant to be public: it will sit in your bundle and in the network tab.
  • The signing secret and key ID of a key, under Signing secret. These stay on your server.

Why the server is involved​

A publishable key says which site may record Events, but anyone can read it from your page and claim to be any Contact. So your server proves who the visitor is: it computes an IdentityHash for the signed-in user and hands it to the page with the user's externalId. The browser API accepts Events only for the Contact the hash was computed for.

1. Compute the IdentityHash on your server​

import { identityHash } from '@throughlinehq/server';

const hash = await identityHash({
signingSecret: process.env.THROUGHLINE_SIGNING_SECRET!,
externalId: user.id,
});

The hash is HMAC-SHA256(signingSecret, externalId) as lowercase hex, so any language can compute it. It never changes for a Contact and does not expire: compute it when you render the page, or store it beside the user.

Never compute it in the browser. That ships the signing secret to every visitor, and any of them can then record Events as anyone.

Pass the hash, the key ID and the user's externalId down with the page.

2. Record Events in the browser​

npm install @throughlinehq/browser
import { ThroughlineBrowserClient } from '@throughlinehq/browser';

const throughline = new ThroughlineBrowserClient({
publishableKey: 'pk_live_…',
externalId: user.id,
identityHash: user.throughlineIdentityHash,
identityKeyId: user.throughlineIdentityKeyId,
});

await throughline.track({ eventName: 'pricing_viewed', properties: { plan: 'pro' } });

The Contact must exist. Upsert it from your server when the person signs up, as in Getting started.

React, Vue and Angular adapters ship in the same package: @throughlinehq/browser/react, /vue and /angular. The package README shows each one.

What it leaves out​

  • No anonymous tracking. No visitor id, no aliasing, no queue of Events from before sign-in.
  • No cookies and no storage. It writes no identifier anywhere, so adding it does not change what your consent banner has to cover.

Rotating the signing secret​

A leaked IdentityHash lets someone record Events for that one Contact. A leaked signing secret lets them do it for every Contact. In both cases, create a new key, sign with its secret and key ID, deploy, then revoke the old key.