Event Tracking 101: How to Instrument Your App
Good analytics starts before the dashboard, at the moment you decide what to track. A practical guide to instrumenting an app without making a mess you regret later.
Everyone wants the dashboard. Almost nobody wants to talk about the part that makes the dashboard possible, which is instrumentation: deciding what to track, naming it well, and attaching the right context. Skip that work and no analytics tool can save you. Garbage events in, garbage charts out, and you find out months later when the numbers do not add up.
This is a practical guide to event tracking from the ground up. What an event is, what to track, how to name things so you do not hate yourself in a year, and the mistakes that quietly poison a dataset.
What an event actually is
An event is a record that something happened. At minimum it has a name, plus who did it, when, and any context worth keeping. Here is the actual shape Hintway puts on the wire when you track one:
{
"tenant_id": "mygame",
"tracking": {
"name": "level_complete",
"value": "{\"level\": 3, \"time\": 94.2}",
"identity": "550e8400-e29b-41d4-a716-446655440000",
"session_id": "b1e2f3a4-c5d6-7890-abcd-ef1234567890",
"platform": "Android",
"app_version": "1.4.2",
"timestamp": "2026-05-16T14:30:00.000Z"
}
}
Walk the fields, because they are the anatomy of every event everywhere, not just Hintway’s:
nameis the verb that happened:level_complete,checkout_completed,invite_sent. This is the part you name carefully, more on that below.identityis who did it: a persistent UUID tied to the device or user, generated once and reused across launches. Note what it is not: an email or a real name. Identity here is a stable anonymous key, not personal data.session_idis a fresh UUID per app launch, so the same person across two sessions is still one identity but two sessions. That split is what makes “actions per session” answerable.platformandapp_versionride on every event, which is what lets you later filter a whole dashboard by version or compare one platform against another.valueis the context: a JSON-encoded payload of whatever details matter for this event. One quirk worth knowing, in Hintway it is a JSON string, not a nested object, so you serialize it before sending.timestampis optional. Omit it and the server records arrival time.
Everything in analytics is built on top of these records. Retention, funnels, revenue, segmentation, all of it is just counting and grouping events by those fields.
In practice you almost never build that JSON by hand. An SDK does it for you and attaches identity, session_id, platform, and app_version automatically. The call collapses to one line:
TrackEvent("level_complete")
Your job is to decide which actions are worth a TrackEvent call and what context to attach. (No SDK for your stack? The same envelope goes straight to the /track endpoint over HTTP.)
Events vs page views vs sessions
Old-school web analytics counted page views: someone loaded a URL. That tells you traffic, not behavior. It cannot tell you whether a user finished onboarding, used the feature you shipped, or rage-quit at step three.
Event tracking is behavior-first. Instead of “they viewed the pricing page,” you record “they started a trial,” “they invited a teammate,” “they hit the usage limit.” Sessions and users are derived from those events, not the other way around. For any product with real interaction, a game, a SaaS app, a tool, behavior is what you actually want to measure, which is why event-based analytics has largely replaced pure page-view tracking.
What to track, and what to skip
The most common instrumentation mistake is tracking everything. Every click, every hover, every scroll. It feels thorough. It produces a swamp nobody can query and a bill that scales with noise.
Track the actions that map to value or to a question you will actually ask:
- Activation moments. Signup completed, onboarding finished, first real action taken.
- Core-loop actions. The thing your product exists to do. A purchase, a level started, a report generated, a message sent.
- Money events. Trial started, subscription upgraded, payment failed.
- Friction and churn signals. Error hit, limit reached, feature abandoned mid-flow.
A simple test: for each event, name the question it answers. “Did onboarding improve?” needs onboarding_completed. If you cannot name a question, you probably do not need the event yet. You can always add it later. You cannot easily clean a year of noise.
Naming is the part that makes or breaks it
This is where future pain is decided. Event names are a schema, and an inconsistent schema is a slow disaster, because renaming later breaks every historical query that referenced the old name.
Pick one convention and never deviate. A solid default:
- Lowercase, snake_case:
level_started, notLevel StartedorlevelStarted. object_actionshape, past tense:item_purchased,invite_sent,report_generated.- The same action gets the same name on every platform. iOS, Android, web, and backend all emit
checkout_completed, never three spellings.
| Bad | Good |
|---|---|
Click, click2, btnClick | checkout_started |
LevelDone vs level_complete | level_completed |
bought on web, purchase on iOS | item_purchased everywhere |
The good column reads like a sentence months later. The bad column is a forensic project.
Properties: the context that makes events useful
An event name tells you what happened. The payload, the value field from earlier, tells you the details you will want to slice by. Without it, item_purchased is a number you cannot break down. With it, the event answers a dozen questions. Most SDKs let you pass that payload as a structured object and serialize it for you:
TrackEvent("item_purchased", {
item_id: "sword_07",
price: 29,
currency: "USD",
source: "shop"
})
Attach the details you will filter or group by: source, item, amount, and anything specific to the event. Platform and app version you do not attach by hand, the SDK already rides them on every event, which is exactly what makes dashboard filtering possible later. You can only scope a dashboard by version or platform because every event carried them. The slice has to exist in the data before you can filter on it.
One rule on properties: keep personal data out. You almost never need names or emails to answer product questions, and putting them in your event stream turns a simple analytics setup into a privacy liability. Track behavior, not identity.
Initialize once, then track everywhere
Mechanically, instrumentation is two steps. Initialize the SDK once at startup, telling it which project the data belongs to, which platform it runs on, and which version it is:
Init(
tenantId = "your-tenant-id",
serverUrl = "https://in.hintway.app",
platform = "PC",
appVersion = "1.4.2"
)
On init the SDK loads or generates the persistent identity, opens a fresh session_id, and from then on stamps both, plus platform and appVersion, onto every event automatically.
Then call TrackEvent wherever an action you care about happens. Init runs once, before any event. After that, tracking is a single line at each interesting moment in your code. If your platform has no SDK, the same events go over plain HTTP through a REST API, so any language can participate.
The mistakes that poison a dataset
A short list of what to avoid, all of which are painful to fix after the fact:
- Instrumenting after building the UI. Decide your events as you build the feature, not as an afterthought. The best time to add
onboarding_completedis while you are writing onboarding. - Inconsistent names across platforms. The fastest way to get numbers that do not reconcile. Agree on names once, write them down, reuse them.
- PII in the payload. A privacy and compliance problem hiding in your analytics. The anonymous
identityUUID is fine, names and emails are not. Track behavior, not personal data. - No properties. Events with no context cannot be sliced. You will wish you had logged version and country the day you need to compare a release.
- Tracking everything. Noise drowns signal and inflates cost. Track questions, not clicks.
Where this leads
Get instrumentation right and the rest of analytics gets easy. Clean, well-named, well-propertied events are what let you build retention curves, funnels, and revenue views, and what let you scope a dashboard by version or segment with filters and presets instead of guessing. Get it wrong and the most expensive analytics tool in the world just renders your mess faster.
Start small. A handful of meaningful events, named consistently, with the properties you know you will slice by. Add more as real questions come up. That is the whole discipline.
Hintway gives you lightweight SDKs for Unity, Unreal, C#, Go, Rust, and a REST API for everything else, so tracking an event is one line and the session, platform, and version come attached automatically. The getting started guide walks from first event to first dashboard in a few minutes.
Ready to see analytics that actually answers your questions?
Get Started Free