Pageviews, custom events, properties, identity, and what each one costs you later.
Use a small, fixed vocabulary in snake_case, past tense, describing what happened rather than where it happened. checkout_started, not clicked_blue_button.
The single most damaging mistake is putting a variable in the event name. An event called signup_user_8842 creates one event type per user, and a project with 50,000 event names is a project where no report can be built. Metricward warns at 500 distinct names and refuses new ones at 2,000, which is deliberately annoying because the alternative is a dataset nobody can query.
mw.track("checkout_started", { plan: "pro", seats: 12 });Properties are scalars: strings, numbers, booleans or null. Nested objects are rejected because they are a cardinality trap dressed as convenience.
Numbers are stored in a separate map from strings, so a property that arrives as 1200 in one event and "1200" in the next shows up as a tracking-plan violation rather than silently breaking every sum built on it.
| Maximum properties per event | 100 |
| Maximum property name length | 64 characters |
| Maximum value length | 1024 characters, truncated beyond that |
| Reserved names | None, but avoid anything starting with mw_ |
Call identify() when somebody signs in, and reset() when they sign out. Everything before the identify call in the same session is attributed backwards for up to 30 days.
If a device already carries a different user, Metricward does not merge the two. It issues a fresh device id instead and records a conflict, because a shared laptop or a support agent impersonating a customer would otherwise fuse two real people's histories permanently.
mw.identify("usr_882", { plan: "pro" });
mw.group("account", "acct_9", { seats: 12 });
// on sign out
mw.reset();Worth knowing. Turning on identified analytics changes what you are processing. Anonymous aggregates become personal data, with everything that follows for your legal basis and your privacy notice. The project setting says so before you enable it.
Send anything that must be correct from your backend. Ad blockers remove somewhere between 10 and 35 percent of European browser traffic, and a signup you undercount by a third is worse than no signup metric at all.
Pass the upstream event id as the idempotency key. Webhooks retry, and revenue counted twice is a worse error than revenue counted late.
await mw.track({
event: "subscription_created",
userId: "usr_882",
revenue: { amountCents: 118800, currency: "EUR" },
idempotencyKey: stripeEvent.id,
});