Skip to content
Launching Oct 20 — OpenPush is free forever to 100K MAU per app, every push feature included. Get early access →
ProductJourneysLive ActivitiesPricingCompareDocsChangelogMigrate from OneSignalSign in

Data tags for games.

A tag is one key and one value on a user. Segments filter on them, journeys write them, and the OneSignal importer brings yours across unchanged. This page is the schema we use ourselves: what to tag, what not to, and what each key is for.

How tags work here

Tags are key–value strings on a user, shared by every subscription that user has. Set them from the SDK with addTag / addTags (the same call a OneSignal integration already makes), from the REST API on the user resource, or by a tag node inside a journey. A value that parses as a number compares numerically in segment filters, so game_level > 10 means what it says. There is no plan gate on the number of keys. Tags are included in every export and are deleted with the user.

Before you add a tag: is it already a field?

Several things teams tag out of habit are first-class on OpenPush and filter without a tag. Tagging them again only gives you two copies that drift.

What you want to filter onUse this instead of a tag
Signup date, account agefirst_session — set when the device first registers
Last seen, lapsed playerslast_session and session_count, updated by the SDK
Languagelanguage, read from the device and overridable
Which build they are on, "updated the app"app_version, plus device_type and country
Username, player idThe external id (an alias) — this is how you address one person from your server; it is an identity, not a tag
Clicked the last notificationRecorded per message already; branch on it inside a journey with a yes/no node on the notification action

The schema

These keys cover almost every free-to-play game we have run. Numbers are numbers; flags are 1 / 0; dates are ISO days. "Set by" says who writes it — the app after the matching event, or the server (a journey tag node, or your backend through the REST key).

KeyTypeSet byFeeds
Progression
game_levelnumberapp, on level up (only ever upwards)Veterans (≥ 10) → the level-up reward journey; level-gated store pushes
high_score · points · powernumberapp, on the run or match endingLeaderboard and tournament targeting; "you are 200 points from the next tier"
characterstringapp, on selectionCopy and image per character in one template with a {{tag}} substitution
playstylestringapp, from one onboarding questionCompetitive / Explorers — different welcome journeys from day one
Economy and spend
number_of_purchasesnumberapp or server, on purchaseSpenders (≥ 1); first-purchase journey exits when it changes
ltvnumberserver, on purchase (sum of amounts)Whales (≥ 50) — never a real currency figure on a device you do not trust; use tiers if that worries you
spenderflagjourney tag node after the first purchase, or the appThe cheapest segment filter you will ever write: spender = 1
last_purchase_atISO dayapp or server, on purchaseReporting and templates ("your last pack was …")
days_since_purchasenumberapp, on each session (today − last_purchase_at)Lapsed spenders (spender = 1 and days_since_purchase > 30) → the win-back journey. See the elapsed-time note below.
gem_balancenumberapp, when the balance changes"You can afford the hero now" pushes; a gem_balance < 50 segment for the refill offer
vip_pass_purchasedflagapp or server, from the purchase SKUExcluded segment on every pass upsell; included on pass-holder perks
Daily loops
daily_gift_eligibleflagapp, at the daily reset (1) and on claim (0)Gift unclaimed (= 1 and last_session older than 20 h) → one nudge, quiet hours respected
daily_gift_claimed · daily_mission_achievedISO dayapp, on the actionStreak copy; the retention journey's yes/no checks whether today's value is set
store_visitsnumberapp, on opening the storeBrowsers who never buy: store_visits ≥ 3 and spender = 0
Onboarding
completed_onboardingflagapp, on finishing the tutorial — or the welcome journey's tag nodeExcluded segment for every welcome message; the day-1 rescue journey targets = 0
onboarding_statusstringapp, per step (started, step_3, done)Where exactly they dropped out, for the rescue copy

Numbers, days, and the elapsed-time trick

Segment filters compare a tag against a constant. That covers "level above 10" but not "purchased more than thirty days ago" — a constant cannot know today's date. Two ways round it, and both are cheap: keep a days_since_… number that the app recomputes on every session (one line of code, and a player who is not opening the app is exactly the one you filter on with last_session anyway), or hold the date in a …_at key for copy and reporting and let last_session carry the recency logic. Store dates as ISO days, not epoch seconds — a person reading the user page should not need a calculator.

What stays out

Anything that identifies a person outside your game — first name, birthday, email, phone, a real-world location — is linkable information even when it is not sensitive on its own, and belongs in a tag only if your privacy policy says you store it and why. OpenPush does not need any of it to send: the external id is the join key to your own systems, and templates can substitute a tag value without you ever storing a name. Never tag a secret, a session token, or a payment identifier. Tags leave with every export and show on the user page to everyone on your team.

Tags → segments → journeys

A tag earns its place by feeding a segment that feeds a journey. The pairs we run:

  • New players (first_session within 24 h, completed_onboarding ≠ 1) → the welcome journey, which writes completed_onboarding = 1 when they tap through.
  • Lapsed 7d (last_session older than 7 d) → the comeback journey with a 10% holdout, exiting the moment a session is recorded.
  • Lapsed spenders (spender = 1, days_since_purchase > 30) → the win-back offer.
  • Veterans (game_level ≥ 10) → the level-up reward, with a yes/no on spender deciding which follow-up they get.
  • Gift unclaimed (daily_gift_eligible = 1, last_session older than 20 h) → a single nudge, sent at each device's own learned hour.

Coming from OneSignal

Your existing keys import unchanged and keep working in segments the same day — including the common ones (game_level, gem_balance, number_of_purchases, vip_pass_purchased, completed_onboarding, daily_gift_claimed, plan_type, subscription_status, trial_expiring, reward_redeemed, referral_invite, survey_response). Nothing needs renaming to match this page; the schema above is a recommendation, not a requirement. Two things are worth tidying after the import: drop tags that duplicate first-class fields (language, signup_date, updated_app), and check whether any key holds linkable information you would rather not carry across. The migration steps are on Migrate from OneSignal.

See it in the sample

The OpenPush.ai App has a "Try it" screen: level up, buy, open the store, finish the tutorial, claim the gift, complete a mission. Each tap records an event and writes the keys above on your own device, the sample segments update, and the demo journeys run against you with the waits compressed to seconds. Open the user page in the console while you tap and you can watch the tags change.