Tracking actions in context
The single biggest instrumentation decision is how you name events and where you put context. Get it right and one event answers a dozen questions; get it wrong and your catalog explodes into hundreds of near-duplicate names that no funnel can line up. This guide is the canonical Kixo pattern — the same design the AI instrumentation advisor (suggest_instrumentation in chat) recommends.
The pattern: one generalized event + rich properties
Track one event per user action and describe the context with properties — never fork the event name per context. A post opened from search and a post opened from the feed are the same action (post_opened) with a different source property.
Anti-pattern
Do not create post_opened_from_search, post_opened_from_feed, post_opened_from_profile. Three event names mean three things to maintain, no single funnel can span them, and adding a fourth surface means a code change everywhere. One post_opened with source scales to any number of surfaces for free.
| Don't (event-per-context) | Do (one event + property) |
|---|---|
post_opened_from_searchpost_opened_from_feed | post_opened with { source: "search" | "feed" } |
video_played_mobilevideo_played_web | video_played — platform is already on every event automatically |
checkout_from_cartcheckout_from_buy_now | cart_checked_out with { source: "cart" | "buy_now" } |
Naming: snake_case (object)(verb), past tense
Name every event (object)_(verb) in snake_case, past tense — post_opened, video_played, cart_checked_out. The same name must be used verbatim across Web, iOS, and Android so cross-platform funnels line up, and so Kixo's standard-event detector can match canonical names.
- Object first, verb second — groups related events together when you scan the catalog (
post_opened,post_liked,post_shared). - Past tense — an event records something that happened.
- snake_case — not
postOpened, notPostOpened. The detector and vocabulary matcher are case-insensitive but converging on one form keeps funnels clean.
Event properties vs user properties
The other half of the decision is where a value lives.
| Event property | User property | |
|---|---|---|
| Describes | This one occurrence of the action | The person, durably across all their events |
| Set with | Kixo.track(name, { ... }) | Kixo.setUserProperty(key, value) |
| Examples | source, screen, post_id, amount_cents | plan, vip, signup_cohort, lifetime_orders |
| Powers | Per-step funnel filters, breakdowns, distributions | Segmentation, audience targeting, campaign eligibility |
Tip
Rule of thumb: if the value can differ between two events from the same user (which post, whichsource), it's an event property. If it's a fact about the user that holds regardless of the action (their plan, whether they're a VIP), it's a user property. Put source on the event; put plan on the user.
Always carry a stable entity id
Pass a stable id for the object the action touched — post_id, video_id, order_id. Without it you can count how many posts were opened, but you can't group, dedupe, or chain events about the same post. A funnel like search → open-from-search → like only resolves to a coherent user journey when each step carries the property that ties the steps together.
Worked example: search → open → like
Say you want to know how many users search, then open a post from the search results, then like it. Three events — but the "from search" context is a source property on post_opened, not a separate event:
search_performed—{ query }post_opened—{ source: "search", screen, post_id }post_liked—{ source: "search", screen, post_id }
Then your funnel filters step 2 (and step 3) on source = "search" — one property predicate, no event-name juggling. The exact SDK calls for each platform:
// User runs a search
Kixo.track('search_performed', {
query: 'running shoes',
});
// User taps a result — the post was opened FROM search
Kixo.track('post_opened', {
source: 'search', // ← context as a property, not the event name
screen: 'search_results',
post_id: 'post_8f3a1c',
});
// User likes the post they opened from search
Kixo.track('post_liked', {
source: 'search',
screen: 'post_detail',
post_id: 'post_8f3a1c',
});Tagging the user instead
If "has liked a post from search" is a durable trait you want to segment on (not just a per-event signal), set a user property after the action: Kixo.setUserProperty("liked_from_search", true). Now the audience explorer and campaign eligibility can target those users directly.
Let the AI design it for you
In the Kixo dashboard chat, describe what you want to measure in plain English — "track posts opened from search and then liked" — and the assistant returns the recommended events, properties, and copy-paste snippets for all three platforms, and tells you which events are already flowing for your project versus what still needs SDK work. It applies exactly the pattern on this page.
See also: the Events Reference for the eleven canonical event names Kixo recognises automatically, and the per-platform SDK guides for Web, iOS, and Android.