# Design a tracking plan

> A step-by-step walkthrough for designing a ClickClacks tracking plan: start from questions, use what autocapture already records, name events and properties, choose browser or server, decide person traits, what to track for a SaaS product and for a marketing site, a hand-off template, and keeping Lexicon in sync.

- Canonical URL: https://clickclacks.io/docs/walkthroughs/tracking-plan
- Section: Walkthroughs
- Last updated: 2026-09-26

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.

## The goal {#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](https://clickclacks.io/docs/walkthroughs/first-events.md) if you can; after is fine too, and step 11 covers fixing what’s already there.

## 1. Start from questions {#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 {#free}

The script tag already records a lot, so don’t plan events for it (see [Autocapture](https://clickclacks.io/docs/autocapture.md)):

- `$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 {#names}

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](https://clickclacks.io/docs/events.md#naming).

## 4. Browser or server? {#where}

| Send from | When | Acme examples |
| --- | --- | --- |
| The browser | Something happens in the interface, and you want it in the visit, with its page, referrer and UTM tags. | `Signed up`, `Demo requested` |
| The server | Money, 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](https://clickclacks.io/docs/walkthroughs/one-person.md)).

## 5. Decide person traits {#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 {#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 {#marketing}

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](https://clickclacks.io/docs/guides/heatmaps.md) 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 {#handoff}

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

```title="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 {#verify}

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](https://clickclacks.io/docs/api.md#validate).

> **What you should see**
>
> The event under exactly the planned name, with every planned property, numbers as numbers, and on the right person. Then set its status to Live.

## 10. Record it in Lexicon {#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.

> **What you should see**
>
> A check next to each verified event in every picker, with your description underneath. Filtering Lexicon by **Not verified** shows what still needs checking.

## 11. Change it safely {#change}

- **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 {#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 {#next}

With the events agreed and verified, put the answers where people will see them every week: [Weekly reporting](https://clickclacks.io/docs/walkthroughs/weekly-reporting.md).

## Related {#related}

- [Custom events](https://clickclacks.io/docs/events.md): Rules, naming and examples.
- [Lexicon](https://clickclacks.io/docs/guides/lexicon.md): Describe, verify, tag, hide and merge.
- [Autocapture](https://clickclacks.io/docs/autocapture.md): What is recorded without any code.
- [Events and properties](https://clickclacks.io/docs/properties.md): Every $ event and property.
