View as Markdown

How to win back churned players with a targeted offer

In this guide, you build a campaign that finds players who reached a meaningful point in your game and then stopped playing, and offers them a reward to return.

What Satori does and what your game does #

Satori is the remote configuration and targeting layer for your game. It determines which players qualify for an offer and what that offer contains.

  • Satori handles the player data you send it, audience membership, the live event schedule and its JSON payload, and the metrics that report engagement.
  • Your game handles the source of truth for player state, how the offer looks, your item catalog and currencies, and granting the reward.

You don’t need any other Heroic Labs product, and Satori makes no assumptions about your game server or your engine.

In this guide #

You’ll start with the configuration in the Satori Console. Then you’ll connect your game so it sends player data to Satori and fetches the offer back, and finish by testing, launching, and measuring the campaign.

PartWhat you doWhere
OnePlan the campaignSatori Console
TwoTell Satori what your game will sendSatori Console
ThreeBuild the campaignSatori Console
FourConnect your gameYour client or server
FiveTest, launch, and measureSatori Console

Plan the campaign #

Decide when the campaign runs and what data it depends on, before you configure anything.

Pick a window that avoids event overlap #

Use the Timeline view to find the right window for your campaign. It shows all active and scheduled events in a consolidated calendar, for up to a four-week span, so you can check for overlap before you commit to dates.

Timeline view showing active and scheduled events across a four-week calendar span
Timeline view showing active and scheduled events across a four-week calendar span

Assign category labels while you plan, to group related events. Labels apply across live events, feature flags, and player messages, which gives you a consistent way to filter the calendar by campaign or season.

For the full operational picture, see Manage live events.


Tell Satori what your game will send #

Satori can only target players it has data about. This campaign runs on two analytic events, levelCompleted and playerReturned, both custom events you define here. Your game sends both. Every event Satori receives also produces computed properties, and you use those to build the player segments you target.

Define the level progression event #

levelCompleted reports player progression. Follow these steps to define it in Satori:

  1. Navigate to the Taxonomy -> Events screen.
  2. Click the Create New Event button.
  3. For Event Name enter: levelCompleted.
  4. For Value set Number, and for Metadata set Object.
Creating the levelCompleted event with a Number value and Object metadata
Define an analytic event to report level progression

Your game sends this event in Report each level a player completes. Satori derives the levelCompletedValueHigh computed property from it, which you target on in the next step.

Define the return event #

playerReturned signals that a player has come back. Follow these steps to define it in Satori:

  1. Navigate to the Taxonomy -> Events screen.
  2. Click the Create New Event button.
  3. For Event Name enter: playerReturned.
  4. For Value set Number, and for Metadata set Object.
Creating an event
Define an analytic event to signal a player returned

Your game sends this event in Report when a player comes back. The name you enter here is reused twice: the metric in Decide how you’ll measure success counts how many times the event fires, and every event Satori receives also produces computed properties you can target on later.


Build the campaign #

The following steps define who you target, what they receive, and how you measure the result. You can change any of the configurations after the campaign goes live.

Describe the players you want to win back #

In Satori, a segment of players is called an audience. In this step, define your target players for the campaign by filtering for the relevant player properties.

  1. Navigate to the Audiences screen.
  2. Click Create New Audience.
  3. For Audience Name enter ChurnedProgressedPlayers and give it an appropriate description.
  4. Click Create.
  5. On the next screen, enter the appropriate filter. This filter checks that the player’s levelCompletedValueHigh computed property is 5 or higher and that they haven’t been seen within the last 7 days.

Here we’re making use of Satori’s computed properties. Satori computes levelCompletedValueHigh from the levelCompleted event you defined earlier. _sessionStartSeenLast comes from _sessionStart event: Satori emits a _sessionStart event every time an authentication creates a session, and every event automatically produces a SeenLast computed property. So _sessionStartSeenLast the timestamp of the player’s most recent session.

Completed audience filter with two conditions joined by And: levelCompletedValueHigh greater or equal than 5, and _sessionStartSeenLast not within last 7 days
The completed filter: level 5 or higher, and no session in the last 7 days

Decide how you’ll measure success #

This metric counts how many players returned to the game, which is how you tell whether the campaign worked. Your game sends the playerReturned analytic event when a player logs back in, and the metric tallies each one. The run reports in Launch, then tune while it runs read those totals.

  1. Navigate to the Metrics screen.
  2. Click Create New Metric.
  3. For Name enter the name of the event from Define the analytic event: playerReturned.
  4. For Type change it to Count.
  5. For Order leave it as High.
Creating a metric
Creating a metric to measure the success of the campaign

Build the offer and schedule it #

You deliver the offer as a live event: a time-bound player experience that carries remote configuration to the players you target. The same mechanism covers holiday one-offs, repeating season passes, and promotional bundles, so the pattern in this step transfers to most campaigns you run later.

On the Live Events screen, click Create Live Event. The wizard walks you through six stages in this order.

  1. Details. For Name enter ReturnedPlayerOffer and give it a Description.
  2. Metrics. For Metrics to monitor select the playerReturned metric.
Metrics stage of the wizard with the playerReturned metric selected for monitoring
Attach the metric from Decide how you'll measure success so the run reports have data to show
  1. Target Audience. For Audience(s) select ChurnedProgressedPlayers, and turn on Sticky Membership.
Target Audience stage of the wizard with the ChurnedProgressedPlayers audience selected and Sticky Membership toggled on
Target the audience you built in Describe the players you want to win back, with sticky membership on
Turn on Sticky Membership for a win-back campaign
This audience selects players with no session in the last 7 days. The moment a churned player logs back in, they stop matching that filter. Sticky membership keeps a player enrolled until the run ends, even once they no longer qualify.
  1. Feature Flags. A live event carries its content in one of two ways: by overriding feature flags, or by holding a one-off JSON Value. For this example you can simply use Value so skip this step.
  2. Live Event Value. This campaign carries its configuration in a one-off JSON Value. The structure of the configuration is entirely up to you. Use it to carry whatever your game needs to build the offer, such as reward amounts, a headline, or an art asset key.

Live Event Value suits data that doesn’t need to persist after the event ends. For configuration that outlives a single run, such as the reward tiers of a repeatable season pass, use a feature flag instead.

Leave Value - Schema Validator set to Object, then enter the JSON that defines the reward and its presentation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "reward": {
    "currency": "soft_currency",
    "amount": 500,
    "itemId": "welcome_back_bundle_01"
  },
  "presentation": {
    "title": "Welcome Back!",
    "description": "We saved this for you. Come back and claim your reward.",
    "artAssetKey": "welcome_back_banner",
    "buttonStyle": "gold_cta"
  }
}
Live Event Value stage of the wizard, with the schema validator set to Object and a JSON payload containing reward and presentation fields in the value editor
Enter the reward and presentation payload as the Live Event Value

The Schema Validator checks the value against a JSON Schema before saving it. Object accepts any JSON object, which is what this campaign needs. Pick a narrower validator, or define your own, when you want the Console to reject a malformed payload before it reaches your players.

  1. Scheduling. Set the Start Time and End Time for the run.
Scheduling stage of the wizard showing start and end time fields with a date picker open, and a Repeat toggle
Set when the run starts and ends

Start and end times default to UTC
By default, start and end times are in UTC. If Show dates in UTC timezone is toggled off in Settings > General, timestamps use local browser time.
The campaign is now fully configured. You can also use native push notifications or email marketing to tell these players to come back and take part in the event.

Invite churned players back with a message #

Reach players before they ever open the game again, using the same audience you already built.

  1. Navigate to the Messages screen.
  2. Click Create Message Template and write the notification, for example, inviting the player back for a reward. See Create message templates for the full field reference.
Create Message Template dialog with a title and plain text content inviting a churned player back for a reward
Write the notification copy in a message template
  1. Click Schedule Message.
  2. Under Integrations, select your configured push or email integration. This is required for a win-back message: leaving it empty delivers to the in-game inbox only, which the player won’t see until they’re already back in the game. See Set up message integrations for supported providers and setup.
  3. Turn on Connect to Live Event and select ReturnedPlayerOffer.
  4. For Reference Point, select On Event Start so the message sends the moment the offer goes live.
Reach players who uninstalled with email
Churned players usually include players who deleted the app, and a push notification can’t reach a device once the app is gone. If you have a player’s email address, you can use Satori’s OneSignal’s integration to reach these players directly.
Create Message Schedule dialog with Connect to a Live Event enabled, targeting the ReturnedPlayerOffer live event and the Churned-Player-Offer-Template
Connect the message schedule to the ReturnedPlayerOffer live event

See Attach player messages to live events for the full messaging reference.


Connect your game #

Satori knows what the campaign is, but it can only target players using data you send it. These four steps supply that data and deliver the offer. Send each call from wherever the data lives: your game client, or your game server if it holds the authoritative player state.

Identify the player to Satori #

Satori targets individual players, so it must identify the player making each request. Requests that return targeted values, including properties and live events, require an authenticated session rather than an API key alone.

Authenticate the player once at startup and reuse the session:

1
var session = await client.AuthenticateAsync(deviceId);

See Sessions for session lifetime and refresh behavior. The examples in this part are C# for Unity. Satori has client libraries for JavaScript, Godot, Defold, Java, Swift, and Dart, and each exposes the same calls under that language’s naming conventions. Check your library’s reference page for the exact signature.

Report each level a player completes #

Your game sends the levelCompleted event each time a player finishes a level, with the level number as the event value:

1
2
var level = 5;
await client.EventAsync(session, new Satori.Event("levelCompleted", DateTime.UtcNow, level.ToString()));

Send this each time a player completes a level. Satori keeps the running maximum in levelCompletedValueHigh, so you don’t need to track the highest level reached yourself.

If your game is server authoritative and progression lives on your own backend rather than in the client, send this event from your server instead. See Server events.

Report when a player comes back #

Send the playerReturned event after the player returns to the game:

1
await client.EventAsync(session, new Satori.Event("playerReturned", DateTime.UtcNow));

This event serves two purposes. It feeds the metric from Decide how you’ll measure success, and like every event it automatically produces computed properties that you can target on later without any extra configuration.

Fetch the offer and show it to the player #

Ask Satori for the player’s active live events. Only players in the ChurnedProgressedPlayers audience receive ReturnedPlayerOffer, so its presence in the list is the signal that this player qualifies:

1
2
3
4
5
6
7
8
9
var liveEvents = await client.GetLiveEventsAsync(session, names: new[] { "ReturnedPlayerOffer" });

foreach (var liveEvent in liveEvents.LiveEvents)
{
    // Value is a JSON string. Deserialize it into a type your game defines.
    var offer = ParseOffer(liveEvent.Value);

    ShowWelcomeBackOffer(offer);
}

The rest happens in your game:

  1. Present the offer. Read presentation.title and presentation.description for the offer copy, and look up presentation.artAssetKey and presentation.buttonStyle in your game’s own asset and style tables to build the welcome back screen. Satori doesn’t constrain how it looks; it only carries the keys your game uses to decide that.
  2. Grant the reward. Read reward.currency, reward.amount, and reward.itemId, and pass them to your existing code for granting currency and items. The grant never passes through Satori.
  3. Report the outcome. Send the playerReturned event from Report when a player comes back so the metric records the engagement.
  4. Record that the offer was claimed. Use a custom property, such as hasClaimedReturnOffer. Your game client checks this property before showing the offer, and sets it once the reward is granted, to avoid a double grant.

One detail worth knowing: live event changes reach the player on their next fetch, not immediately. Refresh at moments where a stale offer would be visible, such as returning to the main menu or completing a purchase.

This guide uses an event that all qualifying players receive automatically. If you configure the live event to require explicit enrollment instead, your client must also call JoinLiveEventAsync and read from ExplicitJoinLiveEvents. See customize enrollment.

Test, launch, and measure #

The campaign is built and your game is connected. Verify it against a controlled set of identities first, then launch and tune it from the Console.

Verify the campaign on test accounts first #

An audience filter with a mistake in it matches the wrong players and raises no error. Pointing the event at a QA audience first catches that before it reaches your live player base.

  1. In the Audiences panel, create an audience that targets your internal test identities.
  2. Edit ReturnedPlayerOffer to target the QA audience only, and set the start time to the current date and time so the event goes live immediately.
  3. Using a test identity that belongs to the QA audience, verify that event values are delivered correctly, and monitor metrics are firing.
  4. When testing is complete, edit the event again: set the start time to your intended launch date, and change Target Audience back to ChurnedProgressedPlayers.

Launch, then tune while it runs #

The event starts on its own at the configured start time, and every field stays editable while it runs, so you can correct the campaign after it goes live.

All value fields are editable, with no code deploy. If a reward level needs adjusting or an asset reference has changed, edit the event value in the Console. The updated payload reaches qualifying players the next time your game requests its live events. To stop a problematic event immediately, edit its end time.

While the event runs, open its details page to see time-series data for each monitor metric you attached, and use it to track participation and conversion trends. When the event ends, review the monitor metrics for the completed run. Run Report compares performance across previous runs of the same event, and the filterable views let you change the date range and resolution of the data.

Live Event Run Reports showing run selector, date picker, resolution control, and time-series metric charts
Run Reports comparing metrics across runs of a live event

For more on reading the numbers, see Performance monitoring.