> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Loyalty Journey

> Build automated loyalty messaging that keeps customers informed about their points balance, notifies them when rewards unlock and drives repeat purchases with measurable ROI.

A Loyalty Journey is an automated email flow that keeps customers informed about their points balance and engaged with your rewards program. It connects your existing loyalty system to OneSignal so personalized, data-driven messages go out automatically without manual campaign sends each week.

This tutorial covers two implementation cases:

* **Case 1 – Basic:** Sync three loyalty tags to OneSignal via API and send weekly point reminder emails and reward-unlock notifications. No purchase event tracking required.
* **Case 2 – Advanced:** Same tag sync as Case 1. Adds post-purchase points confirmation emails, a multi-step lapsed customer winback series and a webhook to confirm redemptions with your loyalty backend.

Start with Case 1 to prove ROI quickly. Upgrade to Case 2 when you're ready to connect messaging directly to purchase behavior.

***

## Overview

Your loyalty system is the source of truth for points and rewards. OneSignal stores a copy of each customer's loyalty state as data tags and uses those tags to personalize and trigger messages automatically.

1. Your loyalty system updates OneSignal tags via the [Update user API endpoint](/reference/update-user) whenever a customer earns points, redeems a reward or crosses a threshold
2. OneSignal segments update in real time as tag values change
3. Journeys are triggered by segment entry or custom event, using Liquid to pull each customer's points data into the message
4. You track email opens, clicks and downstream purchases to measure ROI

***

## Prerequisites

**For both cases:**

* OneSignal app with [email](./email-messaging) configured
* An external loyalty system that stores points balances, reward thresholds and redemption status
* API access to sync loyalty data to OneSignal as tags (see [Update user API endpoint](/reference/update-user))
* A customer list with email addresses matched to OneSignal profiles via [External ID](./users#external-id)
* Familiarity with [Journeys overview](./journeys-overview) concepts

**For Case 1, you also need:**

* Tags `points_balance`, `points_to_next_reward` and `rewards_available` synced to each customer's profile

**For Case 2, you also need:**

* Tags `last_purchase_date` and `lifetime_spend` synced to each customer's profile
* [Custom events](./custom-events) firing from your loyalty system when a purchase completes
* [Journey webhooks](./journeys-webhook) enabled on your account (contact OneSignal sales for access)

***

## Loyalty data setup

Your loyalty system syncs each customer's state to OneSignal as data tags. The journey reads these tags to decide what to send and what to say.

### Tags to sync

| Tag                     | Type   | Description                                         | When to update                                  |
| ----------------------- | ------ | --------------------------------------------------- | ----------------------------------------------- |
| `points_balance`        | Number | Customer's current point total                      | After every point-earning or redemption event   |
| `points_to_next_reward` | Number | Points needed to unlock the next reward             | After every point-earning event                 |
| `rewards_available`     | Number | Number of rewards the customer can currently redeem | After points are earned or rewards are redeemed |
| `last_purchase_date`    | Date   | Unix timestamp of the most recent purchase          | After each purchase                             |
| `lifetime_spend`        | Number | Total spend in cents                                | After each purchase                             |

Tags are synced from your loyalty backend via the [Update user API endpoint](/reference/update-user) because points and rewards are calculated server-side after each transaction. Match customers to OneSignal profiles using [External ID](./users#external-id) so tags land on the correct profile regardless of device or channel. If your app receives updated loyalty data directly, you can also set tags via the [mobile SDK `addTags` method](./mobile-sdk-reference#addtag-addtags).

<Tip>
  Your loyalty system is the source of truth. OneSignal stores a copy of each customer's loyalty state used only for targeting and personalization. Never calculate points or rewards inside OneSignal.
</Tip>

***

## Case 1: Basic loyalty journey

Send weekly point reminders and reward-unlock notifications using three synced tags. No purchase event tracking required.

### Overview

Two journeys run in parallel for each enrolled customer:

1. **Weekly points reminder:** Re-enters every 7 days and sends a personalized email with the customer's current balance and progress toward their next reward.
2. **Reward ready notification:** Triggers when a customer newly qualifies for a reward and sends an email prompting redemption before it goes unused.

### Journey 1: Weekly points reminder

#### Journey settings

Configure these settings on the **Settings** tab before building the steps. See [Journey settings](./journeys-settings) for full reference.

| Setting                                | Value                                                                                                                                             |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Entry rules: Audience segment**      | `points_balance` exists AND has email subscription                                                                                                |
| **Entry rules: Future additions only** | Unchecked. All enrolled loyalty members enter when the journey launches.                                                                          |
| **Re-entry rules**                     | Enabled. Allow re-entry after **7 days**. Each customer exits after the email send, re-enters 7 days later and receives the next week's reminder. |
| **Exit rules**                         | None.                                                                                                                                             |

#### Required segments

| Segment name              | Filters                                            |
| ------------------------- | -------------------------------------------------- |
| Loyalty email subscribers | `points_balance` exists AND has email subscription |

#### Journey steps

<Steps>
  <Step title="Time Window">
    **Tuesday through Thursday, 10 AM to 2 PM** in the user's timezone. This limits delivery to mid-week, mid-morning windows when retail loyalty emails tend to see higher open and click rates.
  </Step>

  <Step title="Email: Weekly points summary">
    Use [Liquid syntax](./using-liquid-syntax) to pull each customer's loyalty data into the email. Include the current balance, points needed for the next reward and a clear call to action to visit or purchase.

    Subject: *"You have \{\{ points\_balance }} points. Your next reward is \{\{ points\_to\_next\_reward }} points away."*

    Body highlights:

    * Current balance: **\{\{ points\_balance }} points**
    * Points to next reward: **\{\{ points\_to\_next\_reward }} to go**
    * Progress bar visual (built in your email template)
    * CTA: "Order now to earn more points"
  </Step>
</Steps>

The journey ends here. Re-entry (enabled in Journey settings) brings the customer back through this same flow 7 days later.

### Journey 2: Reward ready notification

This journey fires once each time a customer newly qualifies for a reward so they know to redeem it before it goes unused.

#### Journey settings

| Setting                                | Value                                                                                                                                                                                                                                        |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Entry rules: Audience segment**      | `rewards_available` > 0                                                                                                                                                                                                                      |
| **Entry rules: Future additions only** | Checked. Only customers who newly enter this segment receive the notification. Customers who already qualify at launch are excluded permanently. Even if they leave the segment and re-enter later, they will not receive this notification. |
| **Re-entry rules**                     | Enabled. Allow re-entry after **30 days**. This lets the journey fire again the next time a customer earns a new reward after redeeming a previous one.                                                                                      |
| **Exit rules**                         | None.                                                                                                                                                                                                                                        |

#### Required segments

| Segment name          | Filters                 |
| --------------------- | ----------------------- |
| Has redeemable reward | `rewards_available` > 0 |

#### Journey steps

<Steps>
  <Step title="Time Window">
    **Any day, 10 AM to 6 PM** in the user's timezone.
  </Step>

  <Step title="Email: Reward unlocked">
    Subject: *"You've earned a reward. Ready to redeem?"*

    Body highlights:

    * Rewards available: **\{\{ rewards\_available }}**
    * What the reward covers (pulled from a tag or hardcoded based on your program's structure)
    * CTA: "Redeem now"
    * Secondary message: current balance and points to the next reward threshold
  </Step>

  <Step title="Wait: 3 days">
    Give your loyalty system time to calculate and sync updated tag values to OneSignal before the email sends.
  </Step>

  <Step title="Yes/No Branch">
    Condition: user is in **Has redeemable reward** segment.

    * **Yes (reward still unused):** Continue to the reminder email below.
    * **No (redeemed):** Exit the journey. No reminder needed.
  </Step>

  <Step title="Time Window">
    **Any day, 10 AM to 6 PM** in the user's timezone.
  </Step>

  <Step title="Email: Reward reminder">
    Subject: *"Your reward is still waiting. \{\{ rewards\_available }} available."*

    Body highlights:

    * Reminder of the unredeemed reward
    * CTA: "Redeem before it expires"
  </Step>
</Steps>

### Success metrics to track

* Weekly email open rate and click-to-open rate
* Reward redemption rate within 7 days of the unlock notification
* Purchase frequency lift for customers in the journey vs. a holdout group
* Revenue per email sent

### Best practices for Case 1

* **Let Liquid do the personalization.** A subject line that shows the customer's point balance outperforms a generic "Check your rewards" subject. Every customer sees their own number, which makes the email feel relevant rather than bulk.
* **Keep the weekly email focused.** One primary message: here is your balance and here is how close you are to a reward. Avoid adding promotions or unrelated offers. The loyalty summary email performs better when it stays on topic.
* **Sync tags frequently.** The weekly email is only as accurate as the tag data behind it. Sync `points_balance` and `points_to_next_reward` after every point-earning event so the email reflects the customer's real state at send time.

***

## Case 2: Advanced loyalty journey

Same two journeys as Case 1, plus a post-purchase points confirmation email and a lapsed customer winback series.

### Overview

Case 2 adds two journeys to the two in Case 1:

3. **Post-purchase follow-up:** Triggered by a `purchase_complete` event. Sends a points credit confirmation shortly after the transaction and shows the customer how close they are to their next reward.
4. **Lapsed customer winback:** Triggered when `last_purchase_date` indicates inactivity. Sends a two-step email series with escalating incentives to bring the customer back.

Case 2 also adds a Webhook step to Journey 2 so your loyalty backend is notified each time a reward-unlock notification is sent.

### Required data

#### Additional tags for Case 2

Tags `last_purchase_date` and `lifetime_spend` are required in addition to the Case 1 tags. See [Loyalty data setup](#loyalty-data-setup) for sync instructions.

#### Events to track

| Event               | Description                   | How to trigger                                                                        |
| ------------------- | ----------------------------- | ------------------------------------------------------------------------------------- |
| `purchase_complete` | Customer completes a purchase | Fire from your loyalty system backend with `amount` and `points_earned` as properties |

#### Required segments

In addition to Case 1 segments, create:

| Segment name       | Filters                             |
| ------------------ | ----------------------------------- |
| Lapsed 30 days     | `last_purchase_date` > 29 days ago  |
| Lapsed 60 days     | `last_purchase_date` > 59 days ago  |
| Purchased recently | `last_purchase_date` \< 30 days ago |

### Journey 2 update: Add a Webhook step

In Case 2, add a Webhook step to Journey 2 immediately after the "Email: Reward unlocked" step. This notifies your loyalty backend each time a reward-unlock notification sends so it can track delivery and flag unredeemed rewards.

<Steps>
  <Step title="Webhook">
    Send the customer's `external_id` and `rewards_available` count to your loyalty backend. Place this step directly after the "Email: Reward unlocked" step. The updated step sequence for Journey 2 becomes: Time Window → Email: Reward unlocked → Webhook → Wait 3 days → Yes/No Branch → ...
  </Step>
</Steps>

### Journey 3: Post-purchase points confirmation

#### Journey settings

| Setting            | Value                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| **Entry trigger**  | Custom event: `purchase_complete`                                                                                               |
| **Re-entry rules** | Automatic. Custom event journeys always allow re-entry. Each `purchase_complete` event triggers a new run through this journey. |
| **Exit rules**     | None.                                                                                                                           |

#### Journey steps

<Steps>
  <Step title="Wait: 15 minutes">
    Give your loyalty system time to calculate and sync updated tag values to OneSignal before the email sends.
  </Step>

  <Step title="Email: Points credited">
    Subject: *"Your points are in. \{\{ points\_balance }} total."*

    Body highlights:

    * Points earned on this purchase
    * Updated balance: **\{\{ points\_balance }} points**
    * Points to next reward: **\{\{ points\_to\_next\_reward }} to go**
    * Progress bar visual
    * CTA: "View your rewards"
  </Step>
</Steps>

### Journey 4: Lapsed customer winback

#### Journey settings

| Setting                                | Value                                                                                                                                               |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Entry rules: Audience segment**      | `Lapsed 30 days`                                                                                                                                    |
| **Entry rules: Future additions only** | Checked. Only customers who newly cross 30 days inactive enter.                                                                                     |
| **Re-entry rules**                     | Not enabled. A customer receives one winback series per lapse period.                                                                               |
| **Exit rules**                         | Exit when user enters **Purchased recently** segment. This removes them from the series if they make a purchase before the 60-day escalation sends. |

#### Journey steps

<Steps>
  <Step title="Time Window">
    **Tuesday through Thursday, 10 AM to 2 PM** in the user's timezone.
  </Step>

  <Step title="Email: We miss you">
    Subject: *"You have \{\{ points\_balance }} points waiting. Come back and use them."*

    Body highlights:

    * Points balance and how close the customer is to a reward
    * A reminder of what their rewards can get them
    * CTA: "Order now"
  </Step>

  <Step title="Wait: 14 days">
    Give your loyalty system time to calculate and sync updated tag values to OneSignal before the email sends.
  </Step>

  <Step title="Yes/No Branch">
    Condition: user is in **Lapsed 60 days** segment.

    * **No:** Exit the journey. The customer hasn't reached 60 days inactive and doesn't need the escalation email. Any customer who made a purchase during the journey would have already exited via the exit rule.
    * **Yes (still lapsed):** Continue to the escalation email below.
  </Step>

  <Step title="Time Window">
    **Tuesday through Thursday, 10 AM to 2 PM** in the user's timezone.
  </Step>

  <Step title="Email: Escalation with incentive">
    Subject: *"Still \{\{ points\_balance }} points waiting. Here's a reason to come back."*

    Body highlights:

    * Points balance (still theirs, not expiring)
    * A limited time bonus offer or discount to incentivize the first purchase back
    * Clear expiry on the offer to create urgency
    * CTA: "Claim your offer"
  </Step>
</Steps>

### Success metrics to track

In addition to Case 1 metrics:

* Post-purchase email open rate and influence on next-visit return rate
* Winback email conversion rate at 30-day and 60-day lapse
* Revenue recovered from lapsed customers per 1,000 emails sent
* Redemption webhook delivery rate (confirms your backend is receiving events)

### Best practices for Case 2

* **Send the post-purchase email fast.** A points credit confirmation sent within minutes of a transaction reinforces the earning behavior and shows the customer their loyalty is being tracked. The 15-minute Wait accounts for tag sync time while keeping the email timely.
* **Make the winback offer specific.** A generic "we miss you" email without an offer underperforms one with a concrete, time-limited incentive. Tie the offer value to the customer's points balance or spend history so high-value lapsed customers get a stronger reason to return.
* **Exit customers who return mid-series.** The exit rule on the winback journey removes customers who make a purchase before the 60-day escalation. Avoid sending a discount to someone who already returned without one.

***

## Case 1 vs. Case 2 at a glance

|                     | Case 1: Basic                                                      | Case 2: Advanced                                                                                      |
| ------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| **Tags required**   | 3 (`points_balance`, `points_to_next_reward`, `rewards_available`) | 5 (adds `last_purchase_date`, `lifetime_spend`)                                                       |
| **Events required** | None                                                               | `purchase_complete`                                                                                   |
| **Journeys**        | 2 (weekly reminder, reward unlock)                                 | 4 (adds post-purchase confirmation, lapsed winback)                                                   |
| **Primary channel** | Email                                                              | Email                                                                                                 |
| **Personalization** | Points balance, reward proximity                                   | All Case 1 fields, plus points earned per transaction                                                 |
| **Winback**         | None                                                               | Two-step email series at 30 and 60 days inactive                                                      |
| **Backend sync**    | One way. Loyalty system writes tags to OneSignal.                  | Adds webhook step in Journey 2 to notify your loyalty backend when a reward-unlock notification sends |
| **Best for**        | Teams proving loyalty email ROI for the first time                 | Teams ready to connect messaging directly to purchase behavior                                        |

***

## Getting started

Start with Case 1. Set up the three core tags, build the two journeys and let them run for four weeks before evaluating. The weekly reminder and reward-unlock notification together give you enough data to measure whether loyalty messaging is driving incremental purchases.

Once you have baseline data, add Case 2 journeys one at a time. The post-purchase points confirmation is the highest-ROI addition since it arrives immediately after a transaction and reinforces the earning behavior at exactly the right moment.

***

## Industry-specific versions

This tutorial covers a general loyalty program structure. For a version tailored to mobile gaming, including in-app tier celebrations, session-based entry and push as the primary channel, see the mobile gaming edition.

<Card title="Loyalty Journey: Mobile Gaming" icon="gamepad" href="./loyalty-journey-mobile-gaming">
  Reward continued play, move players through loyalty tiers and turn retained players into advocates.
</Card>

***

## Track performance

<Card title="Goals" icon="bullseye" href="./goals">
  Set a target metric on your journey and track progress in real time on the Journey report.
</Card>
