# Collect player data

Pushwoosh needs to know who your players are and what they do in the game. Events record the actions, and tags keep the current state of each player.

<Aside type="tip" title="Let AI Assistant create the events and tags">
[AI Assistant](/product/pushwoosh-ai/ai-assistant/) can create the events and tags from this page in your application. Send it the link to the page and ask it to make the tags User-specific. Check each change before you approve it.
</Aside>

## Identify players by User ID

<Aside type="caution" icon="setting" title="Developer assistance needed">
Setting the User ID is a change in your game's code. Please share [Collecting User IDs](/developer/first-steps/collecting-user-ids/#setuserid-sdk-method) with your developers.
</Aside>

Pushwoosh links all of a player's devices to one User ID, so your segments and journeys treat one person as one player. Events and User-specific tags that a device recorded before the player logged in stay with its earlier ID. Device-specific tags move with the device.

By default, Pushwoosh uses each device's hardware ID (HWID) as its User ID. A player who plays on two devices then counts as two players. To avoid this for players who log in, set your own account ID as their User ID:

1. Choose the ID to use. Take the account ID that your game already assigns to each player.
2. Ask your developers to call `setUserId` with this ID when the player logs in.
3. Check the result. Log in to the game on a test device, then open **Audience** → **User Explorer**, search by **User Id**, and enter the account ID. The player's profile opens.

Players who never log in keep the HWID as their User ID, and Pushwoosh sets it for them.

To learn more about how User IDs relate to devices, see [Users and User IDs](/product/pushwoosh-knowledge-hub/users-userids/).

## Set up the events to track

Events are how Pushwoosh learns what a player does. Create them first: [segments](/tutorials/industry-playbooks/mobile-games/build-the-segments/) group players by the events they triggered, and [journeys](/tutorials/industry-playbooks/mobile-games/build-the-journeys/) start when an event happens.

Plan both the events and their attributes before you build anything. [Recommended events for gaming apps](/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/) lists ready-made gaming events with attributes, and [Custom events](/product/audience-data-and-segmentation/events/custom-events/) explains how to define your own.

There are two kinds of events:

* [Default events](#default-events): device registration, app open, purchase, and a few more. The SDK sends them, and you do not create them in the Control Panel.
* [Custom events](#custom-events): level completed, reward claimed, store opened, and others. Your developers [send them from the game](#send-the-events-from-the-game), and Pushwoosh creates each one the first time it arrives. To choose the attribute types yourself, create the event in the Control Panel first. See [Custom events](/product/audience-data-and-segmentation/events/custom-events/) and [Events created automatically](/product/audience-data-and-segmentation/events/#events-created-automatically).

### Default events

The SDK sends these events for you. You need them for most of the journeys in this playbook: onboarding, win-back, and purchase offers.

* [`PW_DeviceRegistered`](/product/audience-data-and-segmentation/events/default-events/#pw_deviceregistered): a device received its first push token. The SDK requests the token only when the game calls `registerForPushNotifications`, which on iOS also shows the permission prompt, and the token arrives whatever the player answers. If the game asks for permission later than the first launch, the event fires at that moment, not at install. To catch new players, use the `Tutorial started` event instead.
* [`PW_ApplicationOpen`](/product/audience-data-and-segmentation/events/default-events/#pw_applicationopen): a player opened the game. You need it to find players who still play and players who stopped playing, so you can win them back.
* [`PW_InAppPurchase`](/product/audience-data-and-segmentation/events/default-events/#pw_inapppurchase): a player bought something in the game. You need it to tell payers from non-payers, for example to offer a first purchase only to players who have never paid. Your developers turn on purchase tracking in the iOS SDK or send purchases from code, and on Android they always send them from code. On iOS, it fires only for products configured in App Store Connect.
* Optional: [`PW_UserIdle`](/product/audience-data-and-segmentation/events/default-events/#pw_useridle): fires when a player stops touching the screen for a time you set, at least 30 seconds. Starts a journey that shows an in-app hint when a player stalls. Your developers enable it in the SDK configuration.
* Optional: [`PW_ApplicationExit`](/product/audience-data-and-segmentation/events/default-events/#pw_applicationexit): fires when a player leaves the game and does not return within 10 to 30 seconds. Starts a win-back journey for players who leave mid-flow. Your developers enable it in the SDK configuration.

An event appears in **Audience** → **Events** after the SDK sends it for the first time.

To add any other default event to the list earlier, for example to build a segment before the game update is live:

1. Open **Audience** → **Events** and click **Create event**.
2. Choose **Default Event**.
3. Select the event and click **Activate**.

The list in step 3 does not include `PW_UserIdle` and `PW_ApplicationExit`. They appear in **Audience** → **Events** only after the SDK sends them for the first time.


For details on these and other default events, see [Default events](/product/audience-data-and-segmentation/events/default-events/).

### Custom events

Custom events record what happens in your game, such as completing a level. You choose which ones the game needs, your developers send them, and you check that they arrive.

#### Choose the events to track

Choose the events your game needs and the attributes each one carries, for example `level_id` for `Level completed`. The table gives examples: use them as they are, rename them, or add your own. Track only the events that the journeys you launch need.

| Event | When it fires | Key attributes | What it drives |
| --- | --- | --- | --- |
| [New level](/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#new-level) | Player reaches a new level | `level_id` (String) | Optional: your own milestone segments |
| [Level completed](/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#level-completed) | Player finishes a level | `level_id` (String) | Engaged non-payers and stuck players segments, Help for stuck players journey |
| Level failed | Player fails a level | `level_id` (String) | Stuck players segment and journey |
| Tutorial started | Player starts the in-game tutorial | `tutorial_name` (String) | Onboarding journey, tutorial drop-off segment |
| [Tutorial completed](/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#tutorial-completed) | Player finishes the in-game tutorial | `tutorial_name` (String), `completion` (Boolean) | Onboarding journey, push permission flow |
| [Virtual currency earned](/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#virtual-currency-earned) | Player's in-game currency balance tops up | `currency_name` (String), `quantity` (Integer) | Optional: your own currency-based segments |
| [Achievement unlocked](/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#achievement-unlocked) | Player unlocks an achievement | `achievement_name` (String), `level` (Integer) | Achievement hunters segment |
| Daily reward claimed | Player claims the daily reward | `day_number` (Integer) | Daily reward reminder journey |
| Live event joined | Player joins a live event | `event_id` (String) | Conversion goal of the live event journey |
| Store opened | Player opens the in-game store | `section` (String) | Store abandonment journey |

<Aside type="tip" title="No tutorial in your game?">
Use your own events for the first two actions every new player takes, such as starting and finishing the first level, instead of `Tutorial started` and `Tutorial completed` wherever this playbook mentions them.

* The first event starts the onboarding journey, and the second one tells it the player is done. Skip the `completion` condition that goes with `Tutorial completed`.
* The SDKs send no first-launch event of their own, so both have to be custom events your game sends.
</Aside>

#### Send the events from the game

<Aside type="caution" icon="setting" title="Developer assistance needed">
Sending events is a change in your game's code. Please share [Events for developers](/developer/guides/audience-and-segmentation/events/) and the [gaming event examples](/developer/guides/audience-and-segmentation/events/custom-events-examples#gaming-apps) with your developers. They send each event with `postEvent` at the moment the action happens. Purchases and reward grants should come from the game server with the player's `userId`.
</Aside>

Agree with your developers which events the game sends and which attributes each one carries. The names of the events and attributes must match exactly.

For example, this is how the game sends `Level completed` with its `level_id` attribute:

<Tabs>
<TabItem label="Android">
```java
TagsBundle attributes = new TagsBundle.Builder()
    .putString("level_id", "12")
    .build();

InAppManager.getInstance().postEvent("Level completed", attributes);
```
</TabItem>
<TabItem label="iOS">
```swift
PWInAppManager.shared().postEvent("Level completed", withAttributes: ["level_id": "12"])
```
</TabItem>
</Tabs>

If an event happens on a server that already sends its own webhooks, such as your game backend or a tournament service, it can reach Pushwoosh through an [inbound webhook](/product/integrations/inbound-webhook/) instead of `postEvent`. You map the fields it sends to a Pushwoosh event in the Control Panel, and the event can start a journey like any other.

To check that an event arrived, open **Audience** → **User Explorer**, find a test player, and open the **Events history** tab.


### Track revenue as PW_Conversion

`PW_Conversion` records the amount and currency of every purchase in one format. You need it for a revenue goal in journeys and for spend tiers in [RFM segmentation](/product/audience-data-and-segmentation/segmentation/rfm-segmentation/), which groups players by how recently, how often, and how much they buy.

Purchases that the iOS SDK tracks reach `PW_Conversion` on their own, because they carry the `__amount` and `__currency` attributes. Purchases from Android need one of these:

* If your game runs on both iOS and Android, ask your developers to send Android purchases as `PW_InAppPurchase` with numeric `__amount` and `__currency` attributes through `postEvent`, instead of the SDK's purchase method. Then the same automatic mapping covers both platforms.
* If your game runs on Android only, map the purchases yourself, without new code. An event keeps one mapping, so in a game with iOS purchases a manual mapping would replace the automatic one.

To map purchases yourself:

1. Open **Audience** → **Events** and open the conversion events settings.
2. Add `PW_InAppPurchase` as a conversion event.
3. Select the attributes that hold its **Price** and **Currency**.
4. Click **Apply**.

<Aside type="caution">
Use only one path per transaction. If your code also sends `PW_Conversion` for the same purchase, Pushwoosh records two separate conversion events.
</Aside>

For all the setup options, see [Conversion events](/product/audience-data-and-segmentation/events/conversion-events/).

### Track subscriptions from the stores

If your game sells subscriptions, such as a VIP pass or a battle pass, Pushwoosh receives their status changes from the App Store and Google Play. Each change arrives as a `PW_Subscription*` event, for example:

* `PW_SubscriptionStart`: a player subscribes.
* `PW_SubscriptionCancel`: a player turns off auto-renewal.
* `PW_SubscriptionBillingIssue`: a renewal payment fails.

Use these events to start journeys, for example a reminder before a cancelled pass expires.

<Aside type="caution" icon="setting" title="Developer assistance needed">
The game must pass the player's Pushwoosh HWID with each purchase:

* On iOS, as `appAccountToken`.
* On Android, as `obfuscatedAccountId`. The game passes the value of the SDK's `getSubscriptionAccountId()`, which is the Application Code and the HWID together, not the HWID alone.

Without it, Pushwoosh receives the notification but records no event, and subscriptions bought earlier can't be matched later. Please share [App Store subscription tracking](/product/integrations/app-store-subscription-tracking/) and [Google Play subscription tracking](/product/integrations/google-play-subscription-tracking/) with your developers.
</Aside>

The events cover subscriptions only. One-time purchases, such as a coin pack, still arrive as `PW_InAppPurchase`.

## Store player data in tags

A [tag](/product/audience-data-and-segmentation/user-data-tags/) is a value that Pushwoosh stores on a player's profile, such as the player's level or whether the player has paid. An event records what happened, and a tag keeps the current state. Tags help you to:

* Send a message only to players with a certain value, for example an offer only to paying players.
* Put the value into the message text, for example the player's level. See [Build the journeys](/tutorials/industry-playbooks/mobile-games/build-the-journeys/).

Tags are optional. The journeys in this playbook run on events and on default tags that Pushwoosh sets itself, so you can skip your own tags at first. Add them when you want more ways to group players. See [Segments by tags](/tutorials/industry-playbooks/mobile-games/build-the-segments/#segments-by-tags).

### Your own tags

Your developers send tag values from the game, or you can [import them from a CSV file](/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/). Pushwoosh creates a tag the first time your developers send it, as a User-specific tag. Create the tag in the Control Panel first if you want to choose its type yourself. See [Tags](/product/audience-data-and-segmentation/user-data-tags/tags/).

These tags are examples. Rename them to match your game's vocabulary, and add your own:

| Tag | Type | Example | What it drives |
| --- | --- | --- | --- |
| `player_level` | Integer | `12` | Filter messages to a progression tier |
| `is_paying_user` | Boolean | `true` | Split payer and non-payer messaging |
| `soft_currency_balance` | Integer | `1500` | Offer a currency pack to players whose balance is low |
| `vip_tier` | String | `gold` | Give VIP players their own offers and early access |
| `favorite_mode` | String | `pvp` | Announce live events for the mode the player likes |

When you create a tag in the Control Panel, keep [**User-specific**](/product/audience-data-and-segmentation/user-data-tags/tags/#tag-scope-general-vs-user-specific) turned on, as it is by default. Pushwoosh then keeps one value per User ID, so the value belongs to the player and not to one of their devices. With **User-specific** turned off, each device keeps its own value. See [Identify players by User ID](/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id).

### Default tags

Pushwoosh stores these [default tags](/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) for every device, so you do not create them:

* **Language**: the language of the device. Pushwoosh uses it to send each player the message in their own language.
* **Last Application Open**: when the game was last opened on the device. Use it to find players who stopped playing.
* **Push Alerts Enabled**: whether notifications are allowed in the device settings. Use it to show an in-app message only to players who have not allowed them yet.
* **First Install**: when the game was first installed on the device. Use it for install anniversaries.
* **Country**: the country of the device. Use it to send an offer or a live event announcement only to players in one region.