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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.