Design a tracking plan

A tracking plan is a short, shared document: the questions you want answered, the events and properties that answer them, and who sends each one. This walkthrough writes Acme CRM’s, with a template you can copy for the hand-off between analyst and developer.

Updated

The goal

By the end you will have a one-page plan the analyst and the developer have both signed off, events in ClickClacks that match it, and every event described and verified in Lexicon. Do this before the developer walkthroughs if you can; after is fine too, and step 11 covers fixing what’s already there.

1. Start from questions

Every event should answer a question someone will actually ask. Write the questions first, then find the smallest set of events that answers them. Acme’s four:

  1. What share of sign-ups create a deal within 7 days?
  2. Do new accounts keep creating deals week after week?
  3. Which campaigns bring accounts that activate and stay?
  4. How much revenue comes from upgrades, by plan?

An event that answers none of them can wait. Tens of well-named events beat hundreds of near-duplicates.

2. Know what you get for free

The script tag already records a lot, so don’t plan events for it (see Autocapture):

  • $pageview for every page, including route changes in single-page apps.
  • $click on any click, recording the nearest link or button, with up to 40 characters of its text.
  • $scroll depth.
  • On every event: the page, the anonymous and session IDs, and browser, device and country details. In reports, every event also carries its visit’s referrer and UTM tags.

So “Viewed pricing page” is a $pageview filtered to /pricing, not a new event. Plan custom events for outcomes that pages and clicks can’t show: an account created, an import finished, a payment made.

3. Agree the naming rules

Write these at the top of the plan, and hold every new event to them:

  • Events: object and past-tense verb, in sentence case. Deal created, Invoice paid. Names are 1 to 128 characters and can’t start with $.
  • Name the outcome, not the widget. Plan upgraded, not Upgrade button clicked; clicks are already captured.
  • Variants and IDs go in properties. Signed up with method: 'google'; never invoice_paid_4821.
  • Properties in snake_case, one type each. value_cents is always a number, stage always text. Send numbers as numbers so they can be summed and averaged.
  • No personal data in names or properties. Use your user ID.

ClickClacks’ own examples use sentence case. If your team already uses snake_case event names, that works too; what matters is one style, everywhere. The full rules are in Naming conventions.

4. Browser or server?

Send fromWhenAcme examples
The browserSomething happens in the interface, and you want it in the visit, with its page, referrer and UTM tags.Signed up, Demo requested
The serverMoney, plan changes, background jobs, webhooks, or anything that must never be blocked or faked.Contacts imported, Plan upgraded, Invoice paid

Each event is sent from one place only; from both, it counts twice. Server events join the same people as long as they use the same user ID as the browser’s identify (One person across browser and backend).

5. Decide person traits

Properties describe an event at the moment it happened. Traits describe the person now, and are sent with identify. A person’s traits come from their newest identify, and identify calls are free.

  • Good traits: plan, seats, role, signed_up_at.
  • They power Person property breakdowns in Insights, and has person property conditions in segments.
  • Email or name only if your team needs them in People, and never a password or payment detail.

6. What to track: SaaS

For a product people sign in to, the lifecycle is what matters. A starting set:

  • Acquisition: Signed up (browser, for attribution).
  • Activation: the one or two actions that mean someone got value. For Acme, Contacts imported and Deal created.
  • Engagement: the repeated core action you’ll use as the returning event in retention. For Acme, Deal created again.
  • Collaboration: Teammate invited, if your product grows through teams.
  • Revenue: Trial started, Plan upgraded, Invoice paid with $revenue and $currency, Subscription cancelled, all from the server.

Plus an identify at sign-in, and reset at sign-out. Ten to fifteen events cover most SaaS products well.

7. What to track: marketing site

A marketing site needs far fewer custom events, because pageviews, clicks, scroll depth and campaigns are captured already. Add events only for outcomes:

  • Forms: Demo requested, Newsletter joined with a placement property, Contact form submitted.
  • Nothing for page views, section views or button clicks. Use Heatmaps and the $click event for those.

Put the site and the app in the same project, as separate sources, and list both domains. That is what lets a funnel run from the pricing page to Deal created.

8. Write the hand-off

One document, owned by the analyst, reviewed by the developer. Copy this template into your wiki or repository and fill it in:

tracking-plan.md
# Acme CRM tracking plan
Owner: Priya (analytics) · Engineering contact: Sam · Last reviewed: 2026-09-26

## Questions
Q1  What share of sign-ups create a deal within 7 days?
Q2  Do new accounts keep creating deals week after week?
Q3  Which campaigns bring accounts that activate and stay?
Q4  How much revenue comes from upgrades, by plan?

## Rules
- Events: object + past-tense verb, sentence case ("Deal created").
- Properties: snake_case, one type each, numbers as numbers, money in cents.
- User ID: users.id as a string, identical in browser and server.
- No email, name or other personal data in events or properties.
- UTM tags: lower case; utm_medium is one of cpc, email, social, referral.

## Events
| Event             | Fires when                     | Sent from                   | Properties                                   | Answers | Status  |
|-------------------|--------------------------------|-----------------------------|----------------------------------------------|---------|---------|
| Signed up         | The account is created         | Browser, app.acme.com       | method: text (email, google)                 | Q1 Q3   | Live    |
| Contacts imported | An import job finishes         | Server, API                 | count: number; source: text (csv, manual)    | Q1      | Live    |
| Deal created      | A deal is saved                | Server, pipeline service    | value_cents: number; stage: text             | Q1 Q2   | Review  |
| Plan upgraded     | The new plan is saved          | Server, billing             | from: text; to: text; seats: number          | Q4      | Planned |
| Invoice paid      | A payment succeeds             | Server, billing webhook     | amount_cents: number; $revenue; $currency    | Q4      | Planned |
| Demo requested    | The demo form is accepted      | Browser, acme.com           | team_size: number                            | Q3      | Planned |

## Person traits (identify)
| Trait         | Type   | Set from          | Example    |
|---------------|--------|-------------------|------------|
| plan          | text   | Server, billing   | growth     |
| seats         | number | Server, billing   | 5          |
| role          | text   | Browser, app      | owner      |
| signed_up_at  | date   | Server, API       | 2026-09-01 |

## Sign-off
- [ ] Analyst: every question maps to events above
- [ ] Developer: every event has one place it is sent from
- [ ] Verified in Events with real properties, then marked Verified in Lexicon
  • Fires when is the most important column. “When the button is clicked” and “when the account is created” are different numbers.
  • Sent from settles browser or server, and which system, so nobody sends it twice.
  • Answers links each event to a question, which is how you decide what to drop later.
  • Keep Status current: Planned, Review, Live.

9. Verify together

When the developer ships an event, check it against the plan before anyone builds a report on it:

  1. Trigger the event yourself, then open Realtime and narrow the feed to its exact name.
  2. Open it in Event details and compare the properties, names and types with the plan.
  3. In Events, pick it over Today and expand a few rows from real users.
  4. For server events, the developer can check payloads before shipping with a dry run.

10. Record it in Lexicon

Lexicon is where the plan lives inside ClickClacks. Every picker reads from it, so descriptions show up wherever someone builds a report. Editing needs the Manage Organization Settings permission, which Owners have.

  1. Open Lexicon (under Data). It has Events, Event properties and Person properties tabs.
  2. For each live event, add the plan’s Fires when as its Description (up to 500 characters).
  3. Add Tags such as activation or billing (up to 10, lower case).
  4. Turn on Verified once step 9 has passed.
  5. On the property tabs, check each type. Set value_cents to Number or signed_up_at to Date if the inferred type is wrong; the type decides which filter operators are offered.
  6. Turn on Hidden for test events, so they leave the pickers.

11. Change it safely

  • Renaming. Lexicon doesn’t rename events. Change what the code sends, then open the new event in Lexicon and, under Merged names, use Merge another event into this one for the old name. Reports count both as one, and Unmerge undoes it.
  • Before removing an event, check its Used in list: every funnel, insight, retention definition, board, experiment, alert and segment that names it.
  • Review the plan each quarter: drop events that answer no question, and describe any that crept in unplanned.

Common mistakes

  • Tracking everything “just in case”. Pickers fill with noise and nobody trusts them. Start from questions.
  • A vague “fires when”. Two developers implement it two ways, and the numbers never match.
  • Events for things autocapture records. A custom “Page viewed” doubles every pageview.
  • The same event from browser and server. It counts twice. One sender per event.
  • Properties with changing types. seats as 5 on one event and '5' on another. Set the type in Lexicon and fix the sender.
  • A plan nobody updates. Keep it next to the code, and make “update the tracking plan” part of shipping an event.

Next

With the events agreed and verified, put the answers where people will see them every week: Weekly reporting.