Install + identify
The browser SDK does two things: capture the referral token when a visitor arrives through a partner link (and keep it alive across sessions), and stitch that visitor to a user account when they sign up or log in. Revenue events are reported server-side — never from the browser.
Install
npm install @openpartner/sdkimport { OpenPartner } from '@openpartner/sdk';
export const op = OpenPartner.init({ apiUrl: 'https://app.openpartner.dev/api/t/<your-workspace>',});apiUrl is your workspace’s API base:
| Deployment | apiUrl |
|---|---|
| Hosted | https://app.openpartner.dev/api/t/<your-workspace> |
| Hosted, white-label domain | https://portal.yourbrand.com/api |
| Self-hosted | https://openpartner.yourbrand.com (your API URL) |
The SDK ships ESM and CJS bundles, has zero runtime dependencies, and is ~4 KB gzipped.
Capture is automatic
init() runs capture immediately: it reads ?cref=… from the landing URL (a visitor’s
first arrival from a partner link) or the _cref first-party cookie (subsequent pages),
and stashes the token in localStorage so attribution survives Safari’s cookie expiry and
multi-week gaps between first click and signup. There is no separate tracking call — one
init() per page load is the whole thing.
Identify
Call when a user signs up or logs in — any time you learn their ID. This is what stitches the authenticated user to the click that brought them in:
await op.identify(currentUser.id);identify() takes exactly one argument: your internal user ID. Pass the same identifier
you’ll later use when reporting events — that’s the join key for the
whole attribution chain. If you don’t want to expose raw IDs, a stable hash works too
(e.g. sha256(user.id + SALT)), as long as you use the same value everywhere.
Calling identify() repeatedly for the same user is harmless — the same (click, user)
pair always maps to one Identity row.
Config
OpenPartner.init({ apiUrl: 'https://app.openpartner.dev/api/t/<your-workspace>', // Optional: cookieName: '_cref', // first-party cookie set by the click router queryParam: 'cref', // query param on landing URLs storageKey: 'openpartner:cref', // localStorage key onError: (err) => Sentry.captureException(err),});Errors are logged to the console by default — pass onError to hook in observability, or
await the identify() call and handle failures yourself.
Framework notes
- React / Vue / SolidJS — call
OpenPartner.init()once at the top of your app; callop.identify()from your auth callback. - Next.js App Router — put
OpenPartner.init()in a'use client'provider component mounted in your root layout. - SPAs — capture runs on
init(), so one init per page load is enough; client-side route changes don’t need re-initialization.