Events
Track custom events and revenue, understand the automatic lifecycle events, and design an event schema that stays useful.
An event is a named thing that happened, with optional properties.
ScaleBun.track('checkout_started', { cartValue: 42.5, currency: 'USD', items: 3 });What is emitted for you#
You do not need to instrument these:
| Event | When |
|---|---|
first_open | First launch after install |
app_open | Each foreground |
session_start | Each new session |
Controlled by autoLifecycleEvents (default true). Screens, deep links,
interactions and push events come from the automatic instrumentation — enabled by
default when you use ScaleBunProvider, and opt-in via automaticEventTracking
when you call init() directly.
Revenue#
Purchases have a dedicated call because revenue needs a consistent shape to be aggregated:
ScaleBun.trackPurchase({ revenue: 42.5, currency: 'USD', transactionId: 'txn_abc123', // any additional properties are preserved sku: 'pro_monthly',});transactionId must be unique and stable. It is what deduplicates a purchase that
gets reported twice — from a receipt validation retry, for example — so a random
value per call will inflate your revenue.
Use the real amount charged in the real currency. Do not pre-convert to USD; the platform does currency handling, and pre-converting loses the original.
Designing an event schema#
The names you pick become your analytics vocabulary. A few rules that prevent the most common mess:
// Consistent snake_case, past tense, one concept per name.ScaleBun.track('checkout_started', { cart_value: 42.5, item_count: 3 });ScaleBun.track('checkout_completed', { cart_value: 42.5, item_count: 3 });ScaleBun.track('checkout_failed', { reason: 'card_declined' });// Mixed casing, tense and granularity; values baked into names.ScaleBun.track('CheckoutStart');ScaleBun.track('checkout-complete-3-items');ScaleBun.track('checkoutFailedCardDeclined');Put variables in properties, not names.
checkout_failedwith{ reason: 'card_declined' }stays one funnel step. Encoding the reason in the name creates one event per failure mode, and no funnel.Keep property values low-cardinality where you intend to group by them. A timestamp or an id as a property is fine to store, but you will never group by it usefully.
Never put secrets or PII in properties. Events are readable by everyone with dashboard access and appear in exports.
Flushing#
Events batch by design — that is what keeps capture cheap. The batch drains on its
own schedule (flushIntervalMs, default 5000).
Force a drain when you are about to lose the process:
await ScaleBun.flush();Worth doing before a deliberate sign-out or an app-initiated restart. Not worth
doing after every track() call: it defeats batching and adds network churn for no
benefit, since the outbox already survives an app kill.
Offline behaviour#
Nothing is lost when there is no connection. Envelopes are written to an on-device
outbox, persisted across restarts, and drained with retry and backoff when
connectivity returns. track() does not wait on the network and does not fail when
it is unavailable.
Debugging what you send#
// Confirm the current identity and ids the SDK will attach.ScaleBun.events.ids();
// Start a fresh analytics session (useful when testing session-scoped funnels).ScaleBun.events.newSession();For live inspection during development, enable the desktop debugger:
await ScaleBun.init({ projectId: '…', publishableKey: '…', apiBaseUrl: '…', desktopDebug: true, verbose: true,});Related#
Identity — attributing events to a user.
Events namespace reference — every member, generated from source.
Configuration —
flushIntervalMs,maxQueueSize,autoLifecycleEvents,automaticEventTracking.