> ## 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.

# Flash sale Journey

> Build an automated flash sale campaign that drives conversions during a time-limited promotion, from a simple time-based flow to a fully personalized Journey with VIP early access and buyer suppression.

This tutorial walks through a complete flash sale Journey using **Black Friday** as the running example (40% off sitewide, midnight to midnight). The pattern applies to any time-limited promotion.

* **Case 1 – Basic:** Pure time-based, no tags. Everyone receives the same messages at scheduled times. Good for your first flash sale or teams without tag infrastructure.
* **Case 2 – Advanced:** Uses tags (`vip_customer`, `wishlist_count`, `last_purchase_date`) for VIP early access, wishlist personalization, and buyer suppression. Start with Case 1 and layer in Case 2 targeting as your data matures.

***

## At a glance

Setup is the same for both cases. The only difference is how targeting and branching are applied.

1. Define the [entry segment](#segments).
2. Build the [push, email, and in-app message templates](#templates).
3. Configure [Journey entry, exit, and re-entry rules](#journey-settings).
4. Wire up the Journey steps:
   * **Case 1:** [Linear flow with Wait nodes](#case-1-basic-time-based-journey), everyone gets the same messages.
   * **Case 2:** [Add VIP branch, wishlist branch, and buyer suppression](#case-2-advanced-tag-based-journey).

***

## Campaign goals

| Goal                                       | How this campaign achieves it                                   |
| ------------------------------------------ | --------------------------------------------------------------- |
| **Build pre-sale anticipation**            | Email + in-app message before the sale                          |
| **Remind users the day before**            | Push the day before to keep the sale top of mind                |
| **Announce the sale launch**               | Email + in-app message when the sale goes live                  |
| **Re-engage mid-sale**                     | Push a few hours after launch for non-visitors                  |
| **Final email reminder**                   | Email halfway through to recover non-purchasers                 |
| **Last-chance urgency**                    | Push 2 hours before the sale ends                               |
| **Personalize and protect trust** (Case 2) | VIP early access, wishlist push, and buyer suppression via tags |

***

## Prerequisites

* A OneSignal account with push notifications set up for your app or site
* Push permission enabled for your users
* Familiarity with [Journeys](./journeys-overview) concepts (entry/exit rules, message and action steps, etc.)
* Email channel set up if you want to send emails (see [Email setup](./email-setup))
* In-app messaging enabled if you want to show banners during the sale window (see [In-app messages setup](./in-app-messages-setup))

Case 2 also requires tags maintained by your backend. See [Case 2: Advanced tag-based Journey](#case-2-advanced-tag-based-journey).

## Segments

**Entry segment (both cases):**

* Name: "Total Subscriptions"
* Built-in, no setup needed. All push-subscribed users enter the Journey.

**VIP segment (Case 2 only):**

* Name: "VIP Customers"
* Filter: **User Tag** `vip_customer` · operator **is** · value `true`

**Wishlist segment (Case 2 only):**

* Name: "Wishlist Users"
* Filter: **User Tag** `wishlist_count` · operator **greater than** · value `0`

## Templates

These templates are starting points: clone, edit, and re-use them in your own Journey. They use [Liquid syntax](./using-liquid-syntax) for personalization.

<Tabs>
  <Tab title="Push Notification templates">
    | When                                 | Name             | Title                                 | Message                                                                                         |
    | ------------------------------------ | ---------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
    | Day before sale                      | Sale tomorrow    | 🛍️ Tomorrow only. 40% off sitewide.  | Your Black Friday sale starts at midnight tonight. Don't miss it.                               |
    | Mid-sale                             | Mid-sale nudge   | ⏰ Still going, 40% off ends tonight   | The Black Friday sale is still live. Don't leave your items behind.                             |
    | 2 hours before close                 | Last chance      | 🔥 2 hours left, last chance          | The Black Friday sale closes at midnight. This is your last chance.                             |
    | Sale launch, VIPs (Case 2)           | VIP early access | 🎉 You're in early, \{\{first\_name}} | VIP early access is live. 40% off everything, shop before everyone else.                        |
    | Sale launch + 1 hour (Case 2)        | Sale launch      | 🛍️ Black Friday is LIVE              | 40% off sitewide, ends tonight at midnight. Shop now.                                           |
    | Sale launch, wishlist users (Case 2) | Wishlist on sale | 💜 Your saved items are 40% off       | You have \{\{user.tags.wishlist\_count}} items on your wishlist. They're all on sale right now. |

    <Columns cols={2}>
      <Card title="Templates" icon="clone" href="./templates">
        Create and manage reusable message templates.
      </Card>

      <Card title="Message personalization" icon="wand-magic-sparkles" href="./message-personalization">
        Overview of all personalization options available in OneSignal.
      </Card>
    </Columns>
  </Tab>

  <Tab title="Email templates">
    Emails carry the long-form moments of the Journey. Three emails fire: pre-sale excitement, sale launch, and a halfway reminder.

    **Pre-sale excitement (2 days before)**

    *Purpose:* Build anticipation and drive wishlist adds before the sale opens.

    *Suggested subject:* Black Friday is almost here 🛍️

    *Content outline:*

    * A teaser of what's coming (40% off everything)
    * Preview of featured products or categories
    * CTA to browse and add items to wishlist
    * Countdown to sale start

    **Sale is live (sale day)**

    *Purpose:* Announce the sale launch and drive immediate traffic.

    *Suggested subject:* Black Friday is LIVE, 40% off everything

    *Content outline:*

    * Bold announcement that the sale has started
    * Featured deals and product highlights
    * Clear CTA to shop now
    * Sale end time prominently displayed

    **Final reminder (halfway through sale)**

    *Purpose:* Recover users who haven't purchased yet.

    *Suggested subject:* Don't miss out, sale ends tonight

    *Content outline:*

    * Reminder that time is running out
    * Highlight top-selling or low-stock items
    * Urgency messaging with countdown
    * CTA to complete purchase

    <Columns cols={2}>
      <Card title="Email messaging" icon="envelope" href="./email-messaging">
        Create and send email campaigns.
      </Card>

      <Card title="Message personalization" icon="wand-magic-sparkles" href="./message-personalization">
        Overview of all personalization options available in OneSignal.
      </Card>
    </Columns>
  </Tab>

  <Tab title="In-App Message templates">
    In-app messages fire when users open your app. Two are included: a pre-sale teaser and a sale-day banner.

    **Pre-sale excitement (2 days before)**

    Use a banner or modal to build anticipation:

    * Headline: "Black Friday is coming!"
    * Body: "40% off everything starts Friday at midnight. Add items to your wishlist now."
    * CTA: Deep link to wishlist or sale preview page

    **Sale is live banner (sale day)**

    Show a banner when users open the app during the sale:

    * Headline: "Black Friday is LIVE"
    * Body: "40% off sitewide, ends at midnight"
    * CTA: Deep link to sale page

    <Columns cols={2}>
      <Card title="Design your in-app message" icon="pen-ruler" href="./design-your-in-app-message">
        Full reference for the in-app message editor.
      </Card>

      <Card title="Deep linking" icon="arrow-up-right-from-square" href="./deep-linking">
        Route users to the correct page.
      </Card>
    </Columns>
  </Tab>
</Tabs>

## Journey configuration

Both cases share the same Journey settings. Define them once and use them for either case.

### Journey settings

| Setting      | Value                                             |
| ------------ | ------------------------------------------------- |
| **Entry**    | Audience Segment: "Total Subscriptions"           |
| **Exit**     | User reaches the end of the flow                  |
| **Re-entry** | No, users receive this Journey only once per sale |

***

## Case 1: Basic time-based Journey

This Journey runs entirely on time-based **Wait** nodes. No tags, no targeting. Everyone receives the same messages at scheduled times. Set it live 2 days before the sale opens.

### Journey timing

The campaign progresses through anticipation (2 days before through the day before), launch (sale day), and urgency (afternoon through close):

| Step | When                          | Channel     | Purpose              |
| ---- | ----------------------------- | ----------- | -------------------- |
| 1    | 2 days before sale (on entry) | Email + IAM | Build excitement     |
| 2    | Day before sale               | Push        | Sale starts tomorrow |
| 3    | Sale launch                   | Email + IAM | Sale is live         |
| 4    | A few hours after launch      | Push        | Mid-sale nudge       |
| 5    | Halfway through sale          | Email       | Final email reminder |
| 6    | 2 hours before close          | Push        | Last chance          |

### Basic Journey steps

<Steps>
  <Step title="Email + In-App Message: Pre-sale excitement (2 days before)">
    **Goal:** Build anticipation a few days before the sale.

    Add an **Email** node as the first step, then an **In-App Message** node immediately after.

    The email lands when the user enters the Journey. The IAM is queued and displays the next time they open your app.
  </Step>

  <Step title="Wait: 1 day">
    **Goal:** Advance to the day before the sale.

    Add a **Wait** node set to **1 day**.
  </Step>

  <Step title="Push Notification: Sale starts tomorrow (day before)">
    **Goal:** Reminder the day before.

    Add a **Push Notification** node.

    | Field          | Value                                                               |
    | -------------- | ------------------------------------------------------------------- |
    | **Title**      | `Tomorrow only. 40% off sitewide.`                                  |
    | **Body**       | `Your Black Friday sale starts at midnight tonight. Don't miss it.` |
    | **Launch URL** | Your sale landing page                                              |
  </Step>

  <Step title="Wait: 1 day">
    **Goal:** Advance to sale day.

    Add a **Wait** node set to **1 day**.
  </Step>

  <Step title="Email + In-App Message: Sale is live (sale day)">
    **Goal:** Announce the sale launch.

    Add an **Email** node, then an **In-App Message** node.

    The email announces the sale is live. The IAM displays a sale banner when users open the app.
  </Step>

  <Step title="Wait: a few hours">
    **Goal:** Give users time to see the launch messages before the mid-sale nudge.

    Add a **Wait** node set to **3–4 hours** (adjust based on your sale duration).
  </Step>

  <Step title="Push Notification: Mid-sale nudge">
    **Goal:** Re-engage users who haven't visited yet.

    Add a **Push Notification** node.

    | Field          | Value                                                                 |
    | -------------- | --------------------------------------------------------------------- |
    | **Title**      | `Still going, 40% off ends tonight`                                   |
    | **Body**       | `The Black Friday sale is still live. Don't leave your items behind.` |
    | **Launch URL** | Your sale page                                                        |
  </Step>

  <Step title="Wait: until halfway through sale">
    **Goal:** Advance to the halfway point.

    Add a **Wait** node. For a 24-hour sale that started at midnight, set this to **6–8 hours** to land around midday/afternoon.
  </Step>

  <Step title="Email: Final reminder">
    **Goal:** Last email push to recover non-purchasers.

    Add an **Email** node with urgency messaging and countdown.
  </Step>

  <Step title="Wait: until 2 hours before end">
    **Goal:** Advance to the final urgency window.

    Add a **Wait** node to land **2 hours before the sale ends**. For a midnight-to-midnight sale, this is around 10 PM.
  </Step>

  <Step title="Push Notification: Last chance">
    **Goal:** Final urgency push.

    Add a **Push Notification** node.

    | Field          | Value                                                                 |
    | -------------- | --------------------------------------------------------------------- |
    | **Title**      | `2 hours left, last chance`                                           |
    | **Body**       | `The Black Friday sale closes at midnight. This is your last chance.` |
    | **Launch URL** | Your sale page or cart                                                |
  </Step>
</Steps>

### Best practices for Case 1

* **Set the Journey live 2 days before the sale.** The pre-sale email fires immediately for users who enter.
* **Respect quiet hours.** Avoid sending pushes between 10 PM and 7 AM in the user's timezone. Adjust Wait nodes if needed.
* **Pause the Journey if the sale ends early.** From the dashboard, pause stops remaining Wait nodes and prevents any remaining messages from firing.
* **[Deep-link](./deep-linking) every message to the right page.** Tapping a push should land users on the sale page, not the app home.

***

## Case 2: Advanced tag-based Journey

Case 2 builds on Case 1 by adding targeting with tags. The timing is the same, but you add:

* **VIP early access:** VIPs get a 1-hour head start at sale launch
* **Wishlist personalization:** Users with wishlist items get a personalized push
* **Buyer suppression:** Filter out buyers from urgency messages

### Required tags

| Tag                  | Type                     | Description                                                                                |
| -------------------- | ------------------------ | ------------------------------------------------------------------------------------------ |
| `vip_customer`       | Boolean                  | `true` if the user is a VIP customer                                                       |
| `wishlist_count`     | Number                   | Count of items in the user's wishlist                                                      |
| `last_purchase_date` | Unix timestamp (seconds) | Time of the user's most recent purchase, stored as a Unix timestamp in seconds (10 digits) |

These tags must be maintained by your backend and synced to OneSignal. See [Tags](./add-user-data-tags) for setup.

<Note>
  The `time elapsed greater than` operator compares the current time to a Unix timestamp stored on the tag. Store `last_purchase_date` as a **Unix timestamp in seconds** (10 digits), not milliseconds or a formatted date. See [Tags: Time Operators](./time-operators) for details.
</Note>

### Advanced Journey steps

Case 2 follows the same overall timing as Case 1, but adds a VIP branch at launch, a wishlist branch right after, and a buyer-suppression filter on every urgency message.

<Steps>
  <Step title="Email + In-App Message: Pre-sale excitement (2 days before)">
    **Goal:** Build anticipation a few days before the sale.

    Add an **Email** node as the first step, then an **In-App Message** node immediately after.

    The email lands when the user enters the Journey. The IAM is queued and displays the next time they open your app.
  </Step>

  <Step title="Wait: 1 day">
    **Goal:** Advance to the day before the sale.

    Add a **Wait** node set to **1 day**.
  </Step>

  <Step title="Push Notification: Sale starts tomorrow (day before)">
    **Goal:** Reminder the day before.

    Add a **Push Notification** node.

    | Field          | Value                                                               |
    | -------------- | ------------------------------------------------------------------- |
    | **Title**      | `Tomorrow only. 40% off sitewide.`                                  |
    | **Body**       | `Your Black Friday sale starts at midnight tonight. Don't miss it.` |
    | **Launch URL** | Your sale landing page                                              |
  </Step>

  <Step title="Wait: 1 day">
    **Goal:** Advance to sale day.

    Add a **Wait** node set to **1 day**.
  </Step>

  <Step title="Branch: VIP / General split (sale launch)">
    **Goal:** Give VIPs a 1-hour head start.

    Add a **Yes/No Branch** node:

    * **Condition:** **User Tag** `vip_customer` · operator **is** · value `true`

    **Yes branch (VIP):** Add a **Push Notification** node immediately:

    | Field          | Value                                                                      |
    | -------------- | -------------------------------------------------------------------------- |
    | **Title**      | `You're in early, {{first_name}} 🎉`                                       |
    | **Body**       | `VIP early access is live. 40% off everything, shop before everyone else.` |
    | **Launch URL** | Your sale page                                                             |

    **No branch (general):** Add a **Wait** node set to **1 hour**, then a **Push Notification** node:

    | Field          | Value                                                   |
    | -------------- | ------------------------------------------------------- |
    | **Title**      | `Black Friday is LIVE 🛍️`                              |
    | **Body**       | `40% off sitewide, ends tonight at midnight. Shop now.` |
    | **Launch URL** | Your sale page                                          |
  </Step>

  <Step title="Email + In-App Message: Sale is live">
    **Goal:** Announce the sale to everyone.

    Connect both branches into the same next node. Add an **Email** node, then an **In-App Message** node. The email announces the sale is live and the IAM displays a sale banner when users open the app.
  </Step>

  <Step title="Branch: Wishlist personalization">
    **Goal:** Send a personalized push to wishlist users.

    Add a **Yes/No Branch** node:

    * **Condition:** **User Tag** `wishlist_count` · operator **greater than** · value `0`

    **Yes branch (wishlist users):** Add a **Push Notification** node:

    | Field          | Value                                                                                          |
    | -------------- | ---------------------------------------------------------------------------------------------- |
    | **Title**      | `Your saved items are 40% off`                                                                 |
    | **Body**       | `You have {{user.tags.wishlist_count}} items on your wishlist. They're all on sale right now.` |
    | **Launch URL** | Your wishlist page                                                                             |

    **No branch (no wishlist):** Continue with no message.

    Connect both branches into the next step.
  </Step>

  <Step title="Wait: a few hours">
    **Goal:** Give users time before the mid-sale nudge.

    Add a **Wait** node set to **3–4 hours**.
  </Step>

  <Step title="Branch + Push: Mid-sale nudge (buyer suppression)">
    **Goal:** Nudge non-buyers only.

    Add a **Yes/No Branch** node. Match **any** of these filters:

    * **User Tag** `last_purchase_date` · operator **time elapsed greater than** · value `4 hours`
    * **User Tag** `last_purchase_date` · operator **does not exist**

    Adjust the `4 hours` value based on how long after sale launch this step fires in your Journey.

    **Yes branch (non-buyers):** Add a **Push Notification** node:

    | Field          | Value                                                                 |
    | -------------- | --------------------------------------------------------------------- |
    | **Title**      | `Still going, 40% off ends tonight`                                   |
    | **Body**       | `The Black Friday sale is still live. Don't leave your items behind.` |
    | **Launch URL** | Your sale page                                                        |

    **No branch (buyers):** Skip the push, route directly to the next step.
  </Step>

  <Step title="Wait: until halfway through sale">
    Add a **Wait** node to reach the halfway point.
  </Step>

  <Step title="Branch + Email: Final reminder (buyer suppression)">
    **Goal:** Email non-buyers only.

    Add a **Yes/No Branch** node. Match **any** of these filters:

    * **User Tag** `last_purchase_date` · operator **time elapsed greater than** · value `12 hours` (halfway through a 24-hour sale)
    * **User Tag** `last_purchase_date` · operator **does not exist**

    **Yes branch (non-buyers):** Add an **Email** node with urgency messaging.

    **No branch (buyers):** Skip the email.
  </Step>

  <Step title="Wait: until 2 hours before end">
    Add a **Wait** node to reach the final urgency window.
  </Step>

  <Step title="Branch + Push: Last chance (buyer suppression)">
    **Goal:** Final push to non-buyers only.

    Add a **Yes/No Branch** node. Match **any** of these filters:

    * **User Tag** `last_purchase_date` · operator **time elapsed greater than** · value `22 hours` (two hours before a midnight close)
    * **User Tag** `last_purchase_date` · operator **does not exist**

    **Yes branch (non-buyers):** Add a **Push Notification** node:

    | Field          | Value                                                                 |
    | -------------- | --------------------------------------------------------------------- |
    | **Title**      | `2 hours left, last chance`                                           |
    | **Body**       | `The Black Friday sale closes at midnight. This is your last chance.` |
    | **Launch URL** | Your sale page or cart                                                |

    **No branch (buyers):** End of Journey.
  </Step>
</Steps>

<Warning>
  The `last_purchase_date` filter only suppresses buyers if your backend updates the tag in **near-real-time**. If your stack updates the tag nightly, buyers in the early hours of the sale will still receive urgency messages later that day. Verify the sync cadence before launch.
</Warning>

### Best practices for Case 2

* **Apply buyer suppression to every urgency message.** Without it, users who bought early still receive "last chance" pushes, which is the most common cause of unsubscribes after a flash sale.
* **Verify tag freshness** before launch by simulating a purchase and confirming the `last_purchase_date` tag updates within minutes.
* **All Case 1 best practices still apply.** Quiet hours, deep linking, and pausing the Journey if the sale ends early.
* **Be aware of message volume.** A VIP wishlist user who never buys can receive up to 4 pushes and 3 emails across the full sequence. Make sure that volume feels appropriate for your audience before launch.

***

## Compare Case 1 and Case 2

Both cases share the same entry segment, Journey settings, and templates. Case 2 adds targeting.

|                              | Case 1: Basic                              | Case 2: Advanced                                             |
| ---------------------------- | ------------------------------------------ | ------------------------------------------------------------ |
| **Data required**            | None                                       | Tags: `vip_customer`, `wishlist_count`, `last_purchase_date` |
| **Setup effort**             | Low, no tag infrastructure needed          | Medium, requires backend tag sync                            |
| **VIP early access**         | No, everyone gets sale launch at same time | Yes, VIPs get 1-hour head start                              |
| **Wishlist personalization** | No                                         | Yes, "your saved items are on sale" push                     |
| **Buyer suppression**        | No, everyone gets urgency messages         | Yes, filter out buyers via `last_purchase_date`              |
| **Best for**                 | First flash sale, no tag infrastructure    | Teams with existing tags, better UX                          |

***

## Where to go from here

Start with Case 1 if you're running your first flash sale or don't have tag infrastructure in place. As you add tags to your data layer, upgrade to Case 2 by adding the VIP branch, wishlist branch, and buyer-suppression filters. The VIP early access alone is worth the effort: it rewards your best customers and creates social proof when they share their finds.

After this Journey is live, consider adding SMS for the final urgency push (see [SMS messaging](./sms-messaging)) or extending the pattern to other time-limited promotions like summer sales, anniversary events, or new product launches.

***

## Related pages

<Columns cols={2}>
  <Card title="Journeys overview" icon="route" href="./journeys-overview">
    Learn how to build, branch, and manage automated message flows.
  </Card>

  <Card title="Message personalization" icon="user" href="./message-personalization">
    Use Liquid tags to personalize message copy with user data.
  </Card>

  <Card title="Segments" icon="filter" href="./segmentation">
    Build reusable audiences based on tags, behavior, and more.
  </Card>

  <Card title="In-app messages" icon="mobile" href="./in-app-messages-setup">
    Create banners and modals to reach users inside your app.
  </Card>

  <Card title="Email setup" icon="envelope" href="./email-setup">
    Configure email messaging for your campaigns.
  </Card>

  <Card title="Tags" icon="tag" href="./add-user-data-tags">
    Add custom data to user profiles for targeting and personalization.
  </Card>
</Columns>
