← All docs

Tracking events

Sending custom events from your app, and the property limits the collector enforces.

Pageviews tell you people showed up. Custom events tell you what they did. This page covers sending them.

The basic call

lp.track('ticket_purchased', { ticket_type: 'vip', seats: 2 });

First argument is the event name. Second is an optional object of properties. That’s the whole API for events — see the SDK reference for the rest of the tracker.

The event goes out immediately. You don’t await it, and a failed request never breaks your page.

Event names

RuleValue
Allowed charactersletters, digits, ., _, -
Length1–64 characters
Leading $Rejected with a console warning

The $ prefix is reserved for built-in events. If you call lp.track('$something'), the tracker refuses to send it and warns in the console.

There are exactly two built-in events:

  • $pageview — fired on init, and again on SPA navigation.
  • $identify — fired by identify().

Everything else in your data is something you sent on purpose.

Properties

Properties are the details that make an event answerable later: which plan, how many seats, which variant. They show up in the events log row detail.

The collector enforces hard limits. Violating any one of these rejects the entire event — not just the offending property. The event is dropped, so it’s worth staying well inside the lines.

LimitValue
Max properties per event64
Max key length64 characters
Key charsetletters, digits, _, ., -
Max string value length1024 characters
Max nesting depth3
Max array/object entries32
Max serialized JSON size8192 bytes

In practice you’ll never come near these with a sensible event. If you’re bumping into the 8 KB ceiling, you’re probably shipping a whole API response as a property — send the three fields you’ll actually filter on instead.

Reserved $ property keys

$revenue and $currency are the only $-prefixed property keys allowed. Any other $-prefixed key rejects the whole event.

lp.track('order_completed', { $revenue: 49.00, $currency: 'USD' });

Revenue has its own page — see revenue tracking.

Best practices

These are the habits that separate data you trust from data you argue with.

Fire conversion events only after the action truly succeeds

Not on button click. Not on form submit. After the account is actually created.

// Wrong — counts people whose signup failed
form.addEventListener('submit', () => {
  lp.track('account_created');
});

// Right — counts people who have an account
const res = await createAccount(payload);
if (res.ok) {
  lp.track('account_created');
}

An inflated conversion number is worse than no number. It hides the exact failure — a broken signup — that you most need to see, because the graph keeps looking healthy while nobody gets in.

Prefer your backend for first-value events

First value means the user got something real. Your backend is where you know that for certain: the project was created, the export finished, the invite was accepted. Fire it from there, after the operation completes, using the server-side API.

Client-side first-value events fire when the UI thinks something worked. Backend events fire when it did.

Never send personal information

No emails, names, phone numbers, or addresses — not in event properties, and not in identify(). Use an internal user id.

// Don't
lp.track('plan_upgraded', { email: 'ana@example.com' });

// Do
lp.track('plan_upgraded', { user_id: 'u_8813', plan: 'pro' });

An internal id answers every question an email would, and it keeps personal data out of a system that doesn’t need it. See security for what LaunchPulse does and doesn’t store.

Be consistent, and treat names as permanent

Pick snake_case and stick to it. account_created, not accountCreated in one place and Account Created in another — they’re three different events as far as any analytics tool is concerned, and your funnel splits three ways.

A name is forever, or it needs an alias. Renaming an event in your code splits your history at the moment of deploy: old data under the old name, new data under the new one. That’s fixable — event aliases map the incoming name onto your configured name at query time, across all past and future data — but the cleanest path is to name it well the first time.

A quick check before you ship a name: would you understand it in six months, in a dropdown, with no context? If not, rename it now while it’s free.

Track the few events that change decisions

You’re capped at 12 extra tracked events in your project config, and that’s a feature. Most products have a handful of moments that matter: signup, first real use, upgrade, cancel. Instrument those properly instead of instrumenting everything badly.

Making events count

Sending an event isn’t the same as measuring it. Once your app is sending a name, go to Settings → Tracking and give it a job — conversion, first value, or one of the extra tracked events.

The Detected events panel there lists names your site is sending that aren’t mapped to anything yet, with one-click buttons to assign them. It’s the fastest way to close the loop after a deploy. See event aliases for how that panel works.

Because all of that is read-time configuration, you can send events now and decide what they mean later. Nothing is lost while you make up your mind.

Where to go next

NextEvent aliases and discovery

Talk to us

Questions about LaunchPulse, or want a walkthrough? Send a note and a real person replies.

Or email us at hello@launchpulse.dev