saasboost
v0.1.2
Published
SaaSBoost SDK — track signups, trials, and conversions from your referral sellers.
Downloads
172
Readme
saasboost
Two-line tracking SDK for SaaSBoost. Attribute signups, trials and paid conversions back to the seller who sent the visitor.
- ✅ 2 KB minified browser bundle (sub-1 KB gzipped)
- ✅ Zero dependencies on the server SDK (Node 20+ native fetch)
- ✅
keepalive: true— fire-and-forget event delivery, survives page unload - ✅
localStoragewith 60-day ref expiry - ✅ Never throws — silent failure path. Your SaaS keeps running.
- ESM + CJS + minified CDN bundle
Install
npm install saasboost
pnpm add saasboost
yarn add saasboostOr drop the CDN bundle into your HTML (see Plain HTML below).
Quickstart — browser
import saasboost from 'saasboost'
// One-line init. Do this at the root of your app.
saasboost.init('sk_live_acme_a8e92f...')
// Track any event. saasboost auto-attaches the ref_id captured from ?ref= on init.
saasboost.track('signup', { email: '[email protected]' })
// Fire when real money moves.
saasboost.conversion({ amount: 199.00, email: '[email protected]' })The first time a visitor lands on your site via https://yoursite.com?ref=jane123-acme, saasboost captures jane123-acme and stores it in localStorage for 60 days. Every subsequent track() or conversion() call automatically attaches that ref id — even on different pages, days later, after sign-in.
⚠️ Common gotchas
Three things to get right or your events will silently vanish into a void:
apiBaseis the host root. The SDK appends the/api/v1/...path itself. In dev, setapiBase: 'http://localhost:3333'— not'http://localhost:3333/api/v1'. A wrong base posts to a 404 and the SDK swallows the error by design (so your signup form never breaks).amountis in dollars, not cents. Pass49for a $49 conversion. Passing4900books a $4,900 conversion with a 100× commission.- Server SDK: the field is
refId, notref. Backend validator silently drops unknown fields. A conversion with norefIdsaves with no attribution — the seller dashboard joins on the ref link, so unattributed events are invisible.
If a conversion does not appear on the seller dashboard:
- The
refIdslug must exactly match a realReferralLink.slugrow. - That link must belong to the same listing whose API key you're using.
- Both conditions, or attribution silently fails.
Framework recipes
Vue 3 / Nuxt
// plugins/saasboost.client.ts
import saasboost from 'saasboost'
export default defineNuxtPlugin(() => {
saasboost.init('sk_live_acme_a8e92f...')
})
// Then anywhere in your components:
import saasboost from 'saasboost'
saasboost.track('signup', { email: user.value.email })React / Next.js
// app/providers.tsx (Next 13+) or a top-level layout
'use client'
import { useEffect } from 'react'
import saasboost from 'saasboost'
export function SaaSBoostProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
saasboost.init('sk_live_acme_a8e92f...')
}, [])
return children
}
// In a component:
import saasboost from 'saasboost'
function SignupForm() {
async function onSubmit(email: string) {
await fetch('/api/signup', { method: 'POST', body: JSON.stringify({ email }) })
saasboost.track('signup', { email })
}
...
}Plain HTML
<script src="https://cdn.jsdelivr.net/npm/saasboost/dist/saasboost.min.js"></script>
<script>
saasboost.init('sk_live_acme_a8e92f...')
saasboost.track('signup', { email: '[email protected]' })
</script>The IIFE bundle exposes a global saasboost.
Server SDK
Use the server SDK from inside your billing webhook so conversions are reported the moment money moves — they don't depend on a browser session.
import { SaaSBoostServer } from 'saasboost/server'
const sb = new SaaSBoostServer(process.env.SAASBOOST_KEY!)
// Inside your Stripe / Polar / Lemonsqueezy webhook handler:
export async function handleOrderPaid(order) {
await sb.conversion({
// The slug you captured at signup and stored alongside the user.
// Without this, the conversion lands unattributed.
refId: order.metadata?.saasboost_ref,
email: order.customer_email,
// Stripe sends cents → divide by 100 to get dollars.
amount: order.amount_total / 100,
currency: order.currency,
})
}The server SDK uses Node 20+'s native fetch — zero runtime deps.
By default it never throws. If you want errors to propagate (e.g. so your queue retries):
const sb = new SaaSBoostServer(key, { throwOnError: true })API reference
Browser
saasboost.init(apiKey, options?)
Initialize the SDK. Captures ?ref= from the URL if present (otherwise reads the stored ref) and prepares the network client.
| Option | Type | Default | Description |
| --------------------- | -------- | -------------------------------- | ------------------------------------------------- |
| apiBase | string | https://api.saasboost.app | Host root only — the SDK appends /api/v1/... itself. Pass http://localhost:3333 in dev, not http://localhost:3333/api/v1. |
| forceClearOnEmptyUrl| boolean | false | When true and URL has no ?ref=, wipe stored ref.|
| now | function | Date.now | Deterministic clock (mostly for tests). |
saasboost.track(event, payload?)
Send a non-conversion event. Common values: 'signup', 'trial', 'demo', 'newsletter'. Custom names are allowed and recorded as type='custom' with customName=<event>.
saasboost.conversion(payload)
Fire a conversion. amount is required. The backend attributes this to the stored ref id.
| Field | Type | Required | Notes |
| ---------- | -------- | -------- | ---------------------------------------------- |
| amount | number | ✅ | In dollars (or your full currency unit), NOT cents. 49.00 is $49.00, not $0.49. |
| email | string | optional | Privacy-truncated server-side before display. |
| currency | string | optional | 3-letter ISO code. Defaults to USD server-side.|
| refId | string | optional | Browser: filled in automatically from the stored ref. Server: pass it yourself if you have it. |
saasboost.getRef() / saasboost.clearRef()
Read or clear the currently-stored ref id. Useful for "Was this user referred?" UI or to drop attribution on logout.
Server
new SaaSBoostServer(apiKey, options?)
| Option | Type | Default | Notes |
| -------------- | --------- | ----------------------------- | ---------------------------------------------- |
| apiBase | string | https://api.saasboost.app | Host root only, like the browser SDK. No /api/v1. |
| throwOnError | boolean | false | Recommended true inside webhook handlers so retries fire. |
| fetch | function | globalThis.fetch | Custom fetch impl (mostly tests). |
sb.track(event, payload?) / sb.conversion(payload)
Same shape as the browser SDK. Both are async.
await sb.conversion({
refId: 'jane123-acme', // attribution slug (the part after /r/)
amount: 199.00, // DOLLARS, not cents
currency: 'USD',
email: '[email protected]',
})⚠️
refId, notref. Both the SDK type and the backend validator expectrefId. Sendingrefis silently ignored and your conversion lands unattributed (invisible on the seller dashboard).
How attribution works
- A seller shares
https://saasboost.app/r/jane123-acme - The visitor clicks. SaaSBoost logs the click and 302-redirects to
https://yoursite.com?ref=jane123-acme - Your site loads.
saasboost.init()capturesjane123-acmeand stores it inlocalStoragefor 60 days. - The visitor browses, signs up, starts a trial, eventually pays.
- Each event (
signup,trial,conversion) is reported with the stored ref id. - SaaSBoost attributes the conversion to
janeand pays her per your listing's terms.
If the visitor returns from a different referral link later, the most recent click wins. If they wipe their browser storage, the chain is broken — that's by design (and matches every other attribution tool).
License
MIT
