Widget
Track revenue
There are two kinds of revenue in Clickbase, and the difference matters.
- Verified revenue is money we confirmed with your payment provider. It comes from your Stripe connection or the Payments API, and it drives the numbers you bill and forecast on: the Revenue page, MRR, and the payments ledger.
- Reported revenue is a signal your own site sends from the browser. It is great for attribution — which visits turn into sales — but anyone can fire it from their console, so it is never treated as money. It shows up against your goals, never on the Revenue page.
Pick the surface that fits what you need. Attribution and funnels? Report it from the browser. Accounting, MRR, invoices? Send it verified from your server.
Report revenue from the browser
The tracker gives you three ways to report revenue. All amounts are integer minor units — cents, not dollars — so $49.90 is 4990.
// A one-off sale.
clickbase.sale('purchase', { amount: 4990, currency: 'USD' });
// A recurring charge.
clickbase.subscription('pro-plan', { amount: 2900, currency: 'USD' });
// Any named event can carry revenue too.
clickbase.event('upgrade', { revenue: { amount: 2000, currency: 'USD' } });
The first argument is the goal name. For the amount to count, create a matching revenue goal with that name and currency in your site's goals — the event still records without it, just without the revenue attached. This mirrors how handles revenue events.
sale() and subscription() read the same way but tell a clearer story than a generic event: a sale is a first-class thing, not "just another custom event." They record identically; the names are for you.
You cannot fake verified revenue this way. The browser refuses to send the reserved Stripe lifecycle names — payment, free_trial, trial_started, trial_converted, subscription_started, subscription_renewed, and the other subscription_* events — the names that only Stripe and the Payments API are allowed to write. After Connect they appear as filterable goals automatically. A spoofed sale can inflate a custom goal's reported number, but it can never reach your MRR or your Revenue page.
Send verified revenue from your server
Verified revenue comes from one of two places.
- Connect Stripe on your site's Revenue settings. We import your full payment history — charges, refunds, disputes, subscriptions — and keep it live from then on. No code.
- Call the Payments API from your own backend when you record a sale in your system. Authenticate with a Developers personal access token (
Authorization: Bearer {token}) — the same Passport token as the rest of the API, not the tracking key. Create one on Settings → Developers; optionally limit it to specific sites. Requires workspace admin and an active subscription. See Tracking & events → Payments for the full request shape.
Attribute a Stripe checkout to a visitor
A Stripe Checkout Session is created on your server, so it has no idea which visit it belongs to. Bridge the two: read the visitor id (and optionally the session id) from the SDK in the browser, send them to your server, and attach them to the session.
// 1. In the browser, ask the SDK for the visitor id — never read the
// cookie yourself — and send it to your server at checkout.
const visitorId = window.clickbase?.getVisitorId?.() ?? null;
// Optional companion: same-visit session id from the SDK (`_cb_sid`).
// Visitor alone is enough for attribution; omit session if null.
const sessionId = window.clickbase?.getSessionId?.() ?? undefined;
const clickbaseMetadata = {
clickbase_visitor_id: visitorId,
...(sessionId ? { clickbase_session_id: sessionId } : {}),
};
// 2. On your server, attach them to the Checkout Session.
// Subscription Checkout — Stripe rejects payment_intent_data in this mode.
const session = await stripe.checkout.sessions.create({
// ...line_items, success_url
mode: 'subscription',
metadata: clickbaseMetadata,
subscription_data: {
metadata: clickbaseMetadata,
},
});
// One-time payment Checkout — stamp the PaymentIntent so
// payment_intent.succeeded still attributes when it races ahead of
// checkout.session.completed. Do NOT pass subscription_data here.
// const session = await stripe.checkout.sessions.create({
// mode: 'payment',
// metadata: clickbaseMetadata,
// payment_intent_data: { metadata: clickbaseMetadata },
// });
getVisitorId() returns the current visitor id, or null before the tracker has loaded or for a visitor with no cookie. When the payment lands, we match it back to the visit that earned it. Without the id, the payment still counts — it just lands under Unattributed. The same id on subscription_data.metadata stamps first-touch attribution onto the subscription for MRR filters. Always optional-chain window.clickbase?.getVisitorId?.() / getSessionId?.() so an older CDN tracker cannot throw before the method exists.
getSessionId() returns the current analytics session id (_cb_sid), or null before load / in cookieless mode. clickbase_session_id is optional metadata — when present we store it on the payment and subscription; money filters still join on visitor_id. The same metadata keys work on PaymentIntent creation for Elements / mobile SDKs.
Where each one shows up
| You sent | Trusted as | Appears on |
|---|---|---|
sale() / subscription() / event({ revenue }) |
Reported | Goals |
| Stripe / Payments API | Verified | Revenue page, MRR, payments ledger |