blog · Jul 9, 2026

define product events from clicks you already captured

Arjun Varma · maker of smolanalytics

a retroactive event is a business event you define after the fact, by pointing at clicks you already captured, instead of shipping tracking code and waiting for new data. smolanalytics autocaptures every click with its full element selector chain from the moment you install, so you can name "checkout completed" from a button that fired 10,000 times last month. no re-deploy, no coding agent, retroactive to install. this is the "virtual event" idea from heap, in a single open source go binary.

what does "define events from clicks you already captured" actually mean?

it means the data is already there and you are just labeling it. when you drop the smolanalytics snippet in, autocapture starts recording pageviews, clicks, scroll depth, rage clicks, dead clicks, form submits, and js exceptions with zero tracking code. every click event carries a $elements selector chain, so a click on your buy button is stored as something like button.btn-primary > span "Complete order" with its full ancestry. a defined event is just a saved query over that raw click history. you give it a name and a matcher, and smolanalytics counts every past and future click that matches. because the underlying clicks were captured from day one, the new event has history the instant you save it.

why not just add a track() call?

adding a track() call means a code change, a review, a deploy, and then weeks of waiting before the number is worth looking at. that loop is fine when you are a dev, but it is death for a PM who needs the checkout number in the next standup. with retroactive events you skip all of it. the click was captured whether or not anyone thought to instrument it, so the analytics question "how many people completed checkout in june" is answerable in june, not in august after someone shipped the tag. you also do not touch the codebase, which means no risk of breaking anything and no waiting on eng priorities.

smolanalytics still gives you the code path when you want it. connect your coding agent over MCP and propose_instrumentation reads your repo and writes real track() calls, verify_instrumentation proves each one fires. defined events are the other door, the one that needs no repo and no agent at all.

how do i define one? a concrete example.

open the events view, click new defined event, and match against captured clicks. say your checkout button renders as <button class="btn-primary" data-testid="place-order">Complete order</button>. you create an event:

name: checkout_completed
match: click where $elements contains
  css "button[data-testid=place-order]"
  and text = "Complete order"

save it. smolanalytics scans the click history and, if that button was clicked 4,213 times since you installed on may 1, your checkout_completed event shows 4,213 the moment you hit save. now it is a first-class event. it shows up in funnels, you can break it down by referrer or country, and it keeps counting every new matching click going forward. no deploy happened. the button did not change. you named a thing that was already being recorded.

the selector chain is what makes this reliable. because every click stores its full ancestry, you can disambiguate two buttons that both say "submit" by matching on the parent form or a data-testid, instead of hoping a generic text match is unique.

can the numbers be trusted, or is this a fuzzy guess?

the numbers are exact and provable, which is the part most "ask your data" tools get wrong. every answer smolanalytics returns ships a computed_by receipt: the exact deterministic report and scope that produced it, so a defined event count is not an llm estimate, it is a real query you can read. on top of that there is a committed tracking plan and a CI agreement test that asserts the AI answer equals the /v1 api response equals the dashboard number, byte for byte. if those three ever disagree, CI fails. you can read the test here: github.com/Arjun0606/smolanalytics/blob/main/internal/api/agreement_test.go. so when your retroactive checkout_completed says 4,213, that is the count, not a vibe.

where does this fit next to code-defined events?

use retroactive events for anything a click can express, and code events for the rest. clicks, form submits, and page reaches cover a huge amount of product analytics, checkout, signup, upgrade, feature-tapped, and you can define all of those with no code and full history. for events that carry rich properties from your backend, like revenue or plan_tier on a server-side purchase, you want a real track() call, and that is where the MCP agent flow earns its keep. most teams end up with a mix. the point is you are never blocked waiting on a deploy to answer a question about something a user already clicked.

try it

self-host the single go binary free under MIT and own your data, or start on Solo at $9/mo. import your history from PostHog, Mixpanel, Umami, or CSV so your defined events have even more to match against. play with it live at smolanalytics.com and try naming an event from clicks that are already sitting there.

smolanalytics is the analytics that tells you what to fix. try the cloud or self-host free.