Skip to main content

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.

This tutorial walks through a complete push campaign for a flash sale — a short, high-pressure promotion (often 24 hours or less) where conversions happen inside a narrow window. Black Friday (40% off sitewide, midnight to midnight) is the running example, but the pattern applies to any time-limited promotion. The campaign progresses through three phases: Anticipation (teasing and wishlist building), Launch (VIP-first rollout with personalization), and Urgency (re-engaging non-openers and non-purchasers). You can build this campaign two ways:
  • Case 1: Basic time-based flow — uses tags you likely already have (vip_customer, wishlist_count, last_purchase_date). No new instrumentation. Good for your first flash sale or low-effort campaigns.
  • Case 2: Advanced event-driven flow — uses Custom Events (sale_page_viewed, purchase_completed) to gate messages on real user actions. Cleanly exits buyers from the Journey and unlocks event-property personalization. Recommended once you have basic event tracking in your app.
Both paths share the same Journey backbone, segments, and timing — see Campaign overview for the unified plan. Pick the path that matches your tech stack and skip ahead.

At a glance

You will set one Journey live 2 days before the sale opens. From entry to exit, it:
  • Sends 2 pre-sale teasers (D-2 and D-1) to build anticipation
  • Splits at sale launch — VIPs get a 1-hour head start, then everyone else
  • Sends a personalized “your saved items are on sale” push to wishlist users
  • Nudges non-engagers mid-sale, then sends two urgency pushes (4 hours and 30 minutes before close)
  • Suppresses purchasers from urgency messages so they do not get “last chance” pings after they have bought
  • Optionally pairs with an in-app banner for users who open the app during the sale window

Case 1 vs Case 2 at a glance

Case 1: Basic (time-based)Case 2: Advanced (event-driven)
Data requiredExisting tags only — vip_customer, wishlist_count, last_purchase_dateCustom Events — sale_page_viewed, purchase_completed
Engineering effortNone — uses tags your stack likely already maintainsApp-side Custom Event instrumentation required
Buyer suppressionTag filter on last_purchase_date; accuracy depends on how fresh your backend keeps the tagWait Until + exit on purchase_completed event; precise and instant
Non-engager filteringNot possible — every user in tier receives the mid-day reminderFilters on absence of sale_page_viewed event
Personalization depthLiquid tags (first name, wishlist_count)Liquid tags plus event properties (cart_total, item names)
Best forFirst flash sale, low engineering effort, quick launchRecurring sales, precise targeting, mature programs
If you are not sure which path to pick, start with Case 1 — you can upgrade to Case 2 for the next sale once events are wired in.

Flash sale campaign goals

GoalHow this campaign achieves it
Build pre-sale anticipationTwo teaser pushes (D-2, D-1) drive wishlist adds before the sale opens
Reward loyalty with VIP early accessVIPs get a 1-hour head start before the general announcement
Personalize by user intentWishlist users see a “your saved items are on sale” push with their saved-item count
Recover non-engagers mid-saleMid-day nudge sent only to users who have not visited the sale page
Convert clickers in the final hoursTwo urgency pushes targeted at users who clicked but have not purchased
Protect trust by suppressing buyersA last_purchase_date filter (Case 1) or purchase_completed event exit (Case 2) keeps “last chance” pings off purchasers
The campaign sends 6 pushes total, capped at ~3 per user per day. The most important guardrail: always suppress purchasers from urgency messages — “last chance” pings to buyers are the #1 unsubscribe driver. Respect quiet hours (22:00–07:00) and pause the Journey if the sale ends early.

Prerequisites

Before you start, make sure you have:
  • A OneSignal account with push notifications set up for your app or site
  • Push permission enabled for your users
  • Familiarity with OneSignal Journeys (entry rules, Wait nodes, Branch nodes)
  • Existing user tags maintained by your backend: vip_customer, wishlist_count, and last_purchase_date (or your stack’s equivalent)
  • For Case 2 only: Custom Events sale_page_viewed (with a cart_value property for personalization) and purchase_completed wired in your app
  • (Optional) In-app messaging enabled if you want to show a banner during the sale window

Campaign overview

Both cases share the same timing and node structure. The audience for Node 5 is the only place they diverge.
NodePushTiming (from Journey entry)Audience
1D-2 teaserOn entryAll subscribers
2D-1 reminder+1 dayAll subscribers
3AVIP early access+2 days (sale opens)VIP customers
3BSale launch+2 days + 1 hourNon-VIP subscribers
4AWishlist personalizationRight after Node 3 on that pathWishlist users
5Mid-day reminder+10 hours after Node 4All non-buyers (Case 1) / Non-visitors only (Case 2)
64-hours-left urgency+20 hours after Node 4Non-buyers
730-minutes-left urgency+23.5 hours after Node 4Non-buyers

Case 1: Basic time-based Journey

This Journey runs entirely on time-based Wait nodes and existing tags. No in-sale instrumentation required. Set it live 2 days before the sale opens; it walks every subscriber through the full pre-sale → launch → urgency arc.

Set up segments and Journey settings

Create these segments in Audience > Segments (used for QA and reporting; the Journey itself uses tag filters on individual nodes):
SegmentFilter
Total SubscriptionsBuilt-in — no setup needed
VIP CustomersTag vip_customer = true
Wishlist UsersTag wishlist_count > 0
In Journeys > New Journey > Journey Settings:
SettingValue
Entry ruleTotal Subscriptions
Entry windowRestrict to D-2 (your launch day)
Re-entryDisabled
Exit ruleUser reaches the end of the flow

Build the Journey

Set the Journey live on D-2 (Wednesday for a Friday sale). The first Wait lands users on D-1; the second lands them on sale day.

Node 1: D-2 teaser (sent on entry)

Add a Push Notification node as the first step.
FieldValue
AudienceAll users in the Journey
TitleBlack Friday is almost here 🛍️
BodyGet ready — 40% off everything drops this Friday at midnight. Start your wishlist now.
Launch URLYour sale preview or wishlist page

Node 2: D-1 reminder

Add a Wait node set to 1 day, then a Push Notification node.
FieldValue
AudienceAll users in the Journey
TitleTomorrow only. 40% off sitewide.
BodyYour Black Friday sale starts at midnight tonight. Don't miss it.
Launch URLYour sale landing page

Node 3: Sale launch (VIP / general split)

Add a Wait node set to 1 day (lands on sale day at your go-live clock time), then a Branch node:
  • Branch A (VIP): Tag vip_customer = true → push immediately
  • Branch B (general): all other users → Wait 1 hour, then push
Branch A — VIP early access:
FieldValue
TitleYou're in early, {{user.first_name}} 🎉
BodyVIP early access is live. 40% off everything — shop before everyone else.
Launch URLYour sale page
Branch B — Sale launch (after the 1-hour Wait):
FieldValue
TitleBlack Friday is LIVE 🛍️
Body40% off sitewide — ends tonight at midnight. Shop now.
Launch URLYour sale page

Node 4: Wishlist personalization

In the canvas, connect both branches of Node 3 into the same next node. Add a Branch node:
  • Branch A (wishlist users): Tag wishlist_count > 0 → push immediately
  • Branch B (no wishlist): continue with no message
Branch A — Wishlist push:
FieldValue
TitleYour saved items are 40% off
BodyYou have {{user.tags.wishlist_count}} items on your wishlist. They're all on sale right now.
Launch URLYour wishlist page

Nodes 5–7: Mid-day reminder and urgency pushes

Each of these three pushes uses a Yes/No Branch node to exclude buyers before sending. Build each as a Wait → Branch → Push sequence: Node 5: Mid-day reminder
  1. Add a Wait node set to 10 hours after Node 4.
  2. Add a Yes/No Branch node with the condition: Tag last_purchase_date is earlier than the sale start date (or tag does not exist).
  3. Yes branch (non-buyers) → Add a Push Notification node:
FieldValue
TitleStill going — 40% off ends tonight
BodyThe Black Friday sale is still live. Don't leave your items behind.
Launch URLYour sale page
  1. No branch (buyers) → Route directly to Node 6 (skip the push).
Node 6: 4-hours-left urgency
  1. Connect both branches from Node 5 into a Wait node set to 10 hours.
  2. Add a Yes/No Branch node with the same condition: Tag last_purchase_date is earlier than the sale start date.
  3. Yes branch → Push:
FieldValue
Title4 hours left ⏰
BodyThe sale ends at midnight. Grab your items before they're gone.
Launch URLYour cart page
  1. No branch → Route to Node 7.
Node 7: 30-minutes-left urgency
  1. Connect both branches from Node 6 into a Wait node set to 3.5 hours.
  2. Add a Yes/No Branch node with the same buyer-exclusion condition.
  3. Yes branch → Push:
FieldValue
Title30 minutes left — last chance
BodyThe Black Friday sale closes at midnight. This is your last chance.
Launch URLYour sale page or cart
  1. No branch → End of Journey.
Case 1 cannot distinguish engaged users from non-engaged users without event tracking — both groups receive all three pushes (minus those who bought).
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 pushes later that day. Verify the sync cadence before launch — if you cannot guarantee it, jump to Case 2, which uses an event-based exit instead.

Best practices

  • Apply the last_purchase_date filter to every urgency-arc message (Nodes 5, 6, and 7). Without it, users who bought at hour 2 still get “last chance” pings — the #1 unsubscribe driver.
  • Verify tag freshness before launch by simulating a purchase and confirming the tag updates within minutes.
  • Accept the engagement trade-off. Case 1 cannot distinguish “clicked-but-not-purchased” from “ignored everything” — both groups receive the urgency arc. If post-sale unsubscribe trends climb, upgrade to Case 2.
  • Review frequency caps before launch. A VIP wishlist user who never buys can receive up to 6 pushes across the sequence.
  • Pause the Journey if the sale ends early. From the dashboard, pause stops remaining Wait nodes and prevents urgency pushes from firing.

Case 2: Advanced event-driven Journey

This Journey replaces Case 1’s tag filters with Wait Until nodes that listen for Custom Events. Buyers exit cleanly the moment they purchase, non-engagers are filtered without tag staleness, and event properties drive personalization in the urgency arc. Recommended once you have basic event tracking wired in your app. The pre-sale and launch nodes (1–4) are identical to Case 1. Case 2 only changes how the mid-day reminder and urgency arc are gated.

Set up segments and Journey settings

Reuse the segments from Case 1:
SegmentFilter
Total SubscriptionsBuilt-in — no setup needed
VIP CustomersTag vip_customer = true
Wishlist UsersTag wishlist_count > 0
In Journeys > New Journey > Journey Settings:
SettingValue
Entry ruleTotal Subscriptions
Entry windowRestrict to D-2 (your launch day)
Re-entryDisabled
Exit ruleUser reaches the end of the flow OR fires the purchase_completed event
The journey-level exit on purchase_completed removes any user from the Journey the moment they buy — no per-node buyer filter needed.

Required Custom Events

EventWhen firedProperties used
sale_page_viewedUser views the sale pagecart_value (current cart total in dollars)
purchase_completedUser completes a purchaseNone — used only by the journey-level exit
Verify both events arrive in OneSignal within seconds before setting the Journey live.

Build the Journey

Set the Journey live on D-2. Nodes 1–4 are identical to Case 1; Nodes 5–7 use Wait Until nodes that listen for events.

Nodes 1–4: Pre-sale and launch (same as Case 1)

These nodes are identical to Case 1. Expand for the full setup:
Add a Push Notification node as the first step.
FieldValue
AudienceAll users in the Journey
TitleBlack Friday is almost here 🛍️
BodyGet ready — 40% off everything drops this Friday at midnight. Start your wishlist now.
Launch URLYour sale preview or wishlist page
Add a Wait node set to 1 day, then a Push Notification node.
FieldValue
AudienceAll users in the Journey
TitleTomorrow only. 40% off sitewide.
BodyYour Black Friday sale starts at midnight tonight. Don't miss it.
Launch URLYour sale landing page
Add a Wait node set to 1 day, then a Branch node:
  • Branch A (VIP): Tag vip_customer = true → push immediately
  • Branch B (general): all other users → Wait 1 hour, then push
Branch A — VIP early access:
FieldValue
TitleYou're in early, {{user.first_name}} 🎉
BodyVIP early access is live. 40% off everything — shop before everyone else.
Launch URLYour sale page
Branch B — Sale launch (after the 1-hour Wait):
FieldValue
TitleBlack Friday is LIVE 🛍️
Body40% off sitewide — ends tonight at midnight. Shop now.
Launch URLYour sale page
Connect both branches of Node 3 into the same next node. Add a Branch node:
  • Branch A (wishlist users): Tag wishlist_count > 0 → push immediately
  • Branch B (no wishlist): continue with no message
Branch A — Wishlist push:
FieldValue
TitleYour saved items are 40% off
BodyYou have {{user.tags.wishlist_count}} items on your wishlist. They're all on sale right now.
Launch URLYour wishlist page

Node 5: Mid-day reminder (event-gated)

Replace Case 1’s Wait + Push with a Wait Until node listening for sale_page_viewed:
SettingValue
Wait for eventsale_page_viewed
Timeout10 hours
Two outputs:
  • Event happened — user viewed the sale page; route directly to Node 6 (no mid-day reminder needed).
  • Timeout — user did not visit; send the mid-day reminder push, then route to Node 6.
Mid-day reminder push (timeout branch only):
FieldValue
TitleStill going — 40% off ends tonight
BodyThe Black Friday sale is still live. Don't leave your items behind.
Launch URLYour sale page
Unlike Case 1, this filters out engaged users — only true non-visitors receive the nudge.

Nodes 6 and 7: Urgency pushes (event-gated)

Both urgency nodes use Wait Until with purchase_completed. Because the journey-level exit handles buyers, the event branch silently exits while the timeout branch sends the urgency push. Node 6: 4-hours-left urgency Add a Wait Until node:
SettingValue
Wait for eventpurchase_completed
Timeout10 hours
On timeout, send a push that personalizes with the user’s most recent sale_page_viewed.cart_value:
FieldValue
Title4 hours left ⏰
BodyYour ${{event.cart_value}} cart is waiting. The sale ends at midnight — grab your items.
Launch URLYour cart page
{{event.cart_value}} references the cart_value property from the user’s most recent sale_page_viewed event. Always set a fallback in the message editor so the push still renders for users without a matching event. See OneSignal’s Liquid event property reference for exact syntax.
Node 7: 30-minutes-left urgency Add another Wait Until node:
SettingValue
Wait for eventpurchase_completed
Timeout3.5 hours
On timeout, send the final push:
FieldValue
Title30 minutes left — last chance
BodyThe Black Friday sale closes at midnight. This is your last chance.
Launch URLYour sale page or cart
Buyers who fire purchase_completed at any point exit the Journey via the journey-level exit rule — they never receive the urgency pushes.

Best practices

  • Verify events fire reliably before going live. Test sale_page_viewed and purchase_completed in a staging app and confirm they arrive within seconds, not minutes.
  • Use the journey-level exit on purchase_completed rather than per-node filters — one rule covers every node and prevents accidental sends.
  • Set a fallback for event-property personalization so pushes still render correctly for users without a matching event.
  • Pause the Journey if the sale ends early.

When ready to launch, click Set Live on D-2 — the D-2 teaser fires immediately for every user who enters the Journey.

Multi-channel: Adding email, in-app, and SMS

Want to reach more users? Add Email, In-App Message, or SMS nodes to the Journey you already built.

Where to add each channel

NodeEmailIn-appSMS
D-2 teaser✅ More room for product images
D-1 reminder✅ Reinforce the push
Sale launch✅ Banner for users in the app
4-hours-left✅ Show cart contents
30-minutes-left✅ High-urgency final reminder

How to add each channel

Email: After any Push node, add an Email node. Insert a Wait node (1–2 hours) between push and email to avoid overwhelming users. In-app: After Node 3 (sale launch), add an In-App Message node. Users see the banner the next time they open your app. SMS: At Node 7 (30-minutes-left), add an SMS node after the push. SMS is high-interrupt — use it only for the final urgency message.

Tips

  • Space channels 1–2 hours apart
  • Cap at 3–4 total messages per user per day
  • Save SMS for final urgency only
  • Test email deliverability before the sale

FAQ

What if my last_purchase_date tag isn’t synced in real-time?

If your backend updates tags nightly instead of in real-time, buyers from the early hours of the sale will still receive urgency pushes later that day. Either speed up your tag sync or use Case 2, which exits buyers instantly via the purchase_completed event.

How many pushes will each user actually receive?

A maximum of 6 pushes across the full sequence. VIP wishlist users who never purchase receive all 6; non-VIP users without a wishlist receive 5. Buyers exit early and receive fewer.

How do I handle users in different time zones?

OneSignal’s Intelligent Delivery can optimize send times per user, but for a time-bound sale with a hard deadline, send at clock time instead. If your audience spans multiple time zones, consider running separate Journeys per region with adjusted timing.

What if the sale sells out or ends early?

Pause the Journey immediately from the dashboard. This stops all pending Wait nodes and prevents any remaining pushes from sending.

Can I add email or SMS to this Journey?

Yes. Add Email or SMS nodes alongside or instead of Push nodes. The same timing, branching, and suppression logic applies. For multi-channel campaigns, stagger channels to avoid overwhelming users — for example, push first, then email 2 hours later to non-openers.

Next steps

Journeys Overview

Learn how to build, branch, and manage automated message flows in OneSignal Journeys.

Message Personalization

Use Liquid tags to personalize message copy with user tag values and event properties.

Segments

Build reusable audiences based on tags, behavior, location, and more.

In-App Messages

Create and schedule in-app banners and modals to reach users already inside your app.