Implementation worksheet · 6 min read
An Activation Event Contract for Your First SaaS App
Write a contract per event before you instrument anything: the event name in a fixed convention, the precise moment it fires, the properties it carries with their types, whether it can fire more than once per user, and a named owner. For activation specifically, add the sentence that matters most — what the user has actually experienced by the time it fires. 'Completed onboarding' is a system state; 'sent their first message to a real recipient' is an experience. Then treat the contract as versioned: changing what an event means without renaming it silently rewrites your own history, and every chart built on it becomes a comparison between two different definitions.
The first analytics events in a new app are usually added in an afternoon, named after whatever the code was doing, and never written down. Six months later nobody can say whether signup_complete fires before or after email verification, two events overlap, and the activation number in the board deck cannot be reproduced. None of that is an analytics problem — it is a missing contract.
Put it into practice
1. Name activation as an experience, not a state
The test: could a user hit this event and have received no value? If yes, it is a system milestone rather than activation. 'Account created' and 'onboarding finished' both fail the test. 'Created a project and invited one person' or 'sent the first real message' pass, because something happened that the user would notice.
2. Write the exact firing moment
Not 'when the user signs up' but 'on successful creation of the user record, server-side, after the password is set and before the welcome email is queued'. Client-side and server-side firing produce different numbers — ad blockers, closed tabs and failed requests all eat client events — and the difference only surfaces when two dashboards disagree.
3. List properties with types and allowed values
plan: string, one of free|starter|pro. source: string, from a closed list. Free-text properties become unusable within months because every spelling variant is a separate value. Where a property is optional, say what it means when it is absent — absent and empty are different, and someone will treat them as the same.
4. State the cardinality rule
Once per user ever, once per session, or unbounded. This single field prevents the most common counting bug: an event that was assumed unique firing on every page load, quietly inflating a funnel's top by a factor nobody notices until the conversion rate looks impossible.
5. Give every event an owner and a version
A name, and a version number that increments when the meaning changes. If activation starts including a step it did not include before, that is version 2 and the charts must show the boundary. Silently redefining an event is how a team ends up confidently comparing two different metrics across a quarter.
6. Test the contract against a real session before shipping
Walk the flow once and check each event fired at the stated moment with the stated properties. This takes twenty minutes and catches ordering mistakes that are invisible in code review, because the order events fire is a runtime property rather than a source-code one.
The event contract
Copy this structure into your review document and record your observed result for each row.
| Field | Your value | Why it matters | Set |
|---|---|---|---|
| Event name | fixed convention, never reused | ||
| Fires exactly when | client vs server changes the number | ||
| Client or server | ad blockers eat client events | ||
| Properties + types | free text becomes unusable | ||
| Allowed values | closed lists stay countable | ||
| Meaning of absent property | absent is not empty | ||
| Cardinality | once per user / session / unbounded | ||
| Owner | someone answers questions about it | ||
| Version | meaning changes get a boundary | ||
| Activation: what the user experienced | the sentence that makes it real |
A failure worth checking
Redefining activation without renaming it. A step is added to onboarding, the activation event now fires later, and the rate drops eight points. Nobody connects the two, so a quarter is spent investigating a decline that is entirely definitional. The version field exists so that this shows up as a boundary on the chart rather than as a mystery — and so the person who reads the chart in six months knows which definition they are looking at.
Common questions
Should events fire client-side or server-side?
Server-side wherever the action has a server consequence, because those events are not lost to ad blockers, closed tabs or failed requests. Client-side for genuinely client-only behaviour like scroll depth or a modal being dismissed. Whichever you choose, write it in the contract — a team that does not know which side an event fires on cannot explain a discrepancy.
How many events should a new app have?
Fewer than you think. Ten well-defined events beat sixty half-defined ones, because the cost of an event is not sending it but maintaining agreement about what it means. Add events when a question needs them, not in anticipation of questions.
Basis and scope
This is a proposed implementation method using illustrative examples, not a measured benchmark or a customer case study. Prepared with AI assistance. Validate product-specific behavior against current documentation and your own test environment.