Event-driven Journeys turn user behavior into automated messaging. Instead of waiting on the clock or relying on broad segment membership, the Journey reacts the moment a user performs a specific action. This guide shows you how to wire Custom Events into Journeys cleanly so your automation stays accurate as you scale. What you’ll learn: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.
- Where Custom Events fit inside a Journey (entry rules, Wait Until steps, and exit rules)
- How to design event names, properties, and types that hold up in Journey logic
- Three end-to-end patterns for translating real behavior into automation
Prerequisites
- A OneSignal app sending Custom Events via SDK or the Create Custom Events API
- Familiarity with Journey settings and Journey actions
- One or more messaging channels configured (push, email, SMS, or in-app)
Where Custom Events fit in a Journey
A single Custom Event can drive a Journey in three different places. Picking the right slot is the first design decision.| If the event represents… | Use it as… | Reference |
|---|---|---|
| The starting moment of an experience (signup, purchase, completion) | Entry rule | Custom Event entry rules |
| The expected next step inside an in-progress Journey | Wait Until condition | Wait Until |
| A signal the user is no longer eligible (cancelled, refunded, opted out) | Exit rule | Exit when Custom Event condition occurs |
| A repeating triggering action (cart updated, plan changed) | Both entry and exit, with the same event name | Same-event entry and exit |
Each Journey instance stores the events that triggered entry and matched any Wait Until step. Those events become available to message templates as
journey.first_event, journey.last_event, and journey.event.EVENT_NAME. Each instance can store up to 100 event properties per user (oldest dropped). See Event property storage rules.Design events for Journey use
The shape of your events determines how clean your Journey logic stays. A few rules of thumb pay off as automation grows.Naming
- Use snake_case for event names and property keys so they read predictably.
- Use a past-tense verb phrase for the action:
lesson_completed,cart_updated,payment_failed. Past tense is unambiguous; it describes something that already happened. - Keep event names stable. Renaming an event after Journeys depend on it silently breaks every reference. Add new event names; don’t rename old ones.
- Prefer specific names over umbrella names.
subscription_cancelledandsubscription_pausedproduce cleaner Journey logic than a singlesubscription_changedevent with astatusproperty.
Property design
Include properties when you’ll actually use them, for personalization, branching, or filtering. Skip properties just because they’re available. Useful categories:- Identifiers for matching across events:
order_id,workspace_id,course_id. These are what Wait Until’s Event Matching keys on. - Display values for templates:
product_name,price,image_url. These render directly inside messages. - Branching values for filters:
plan_tier,is_first_purchase,payment_method. - Numeric values for entry rule property filters:
amount,quantity,score.
Data types
Pick the right type up front. Journey filters and Liquid behave differently across types.| Type | Use when | Example |
|---|---|---|
| String | Categorical or identifier values | "premium", "prod_3342" |
| Number | Values you’ll filter or compare | 49.99, 12 |
| Boolean | Binary flags | true, false |
| Datetime (ISO 8601) | Time-of-event values used in Liquid date filters | "2025-04-30T14:22:00Z" |
| Array | Repeating items rendered with for-loops | ["push", "email"] |
What to avoid
- PII in properties. Names, emails, phone numbers, payment details, and sensitive health or auth data should not travel inside event properties. Use anonymized internal identifiers instead.
- Properties you don’t reference. They eat into the 2024-byte event payload limit and clutter analytics. See Custom Event structure.
- Inconsistent naming across events.
productIdin one event andproduct_idin another will silently break Wait Until’s Event Matching.
Pattern 1: React in real time with an entry trigger
Use this pattern when the event itself is the moment you want to message the user. The Journey enters, sends one or two personalized messages immediately, and exits. Use it for:- Order or appointment confirmations
- Course or task completions
- Account creation, plan upgrades
Example: Course completion celebration
A learning app firescourse_completed whenever a user finishes a course. The Journey congratulates them and surfaces what to take next.
Event payload:
JSON
Set the Journey entry rule
In Journey settings, under Entry rules, choose Custom events and set the event name to
course_completed. Leave Filter by property unset so every completion enters the Journey. See Custom event entry rules.Pattern 2: Wait for a follow-up event with Event Matching
Use this pattern when the entry event sets the stage but the actual decision depends on whether a second event fires within a window. Event Matching ties the wait event to the entry event so concurrent Journey instances stay scoped to the same workflow (the same order, the same workspace, the same conversation). Use it for:- Order tracking with a delivery SLA
- Trial-to-feature-activation funnels
- Quote-to-acceptance windows
- Any “did the expected next step happen?” question
Example: Food delivery order tracking
A food delivery app enters users into an order Journey whenorder_placed fires. If order_delivered fires within 90 minutes for the same order_id, the Journey sends a review prompt. Otherwise, it sends a delay-handling support message.
Entry event (sent when the user places the order):
JSON
JSON
Set the entry rule
Custom events entry rule on
order_placed. No property filter, since every order should enter the Journey.Add a Wait Until on the delivery event
Add a Wait Until step:
- Condition: Custom Event
order_delivered - Event Matching:
- Trigger Event Property:
order_id - Wait Event Property:
order_id
- Trigger Event Property:
- Expiration: 90 minutes
Configure the event branch
On the event branch (delivery happened in time), send a review prompt:You can use
Liquid
journey.last_event here if you want to reference properties on the matched order_delivered event. The full reference is in Custom Event Liquid reference.order_delivered event for that user. If the user has two open orders, the first delivery would prematurely complete both Journey instances. Binding to a shared order_id keeps each instance scoped to its own context.
Pattern 3: Wait for a threshold or milestone event
Use this pattern when the question is “did the user accumulate enough behavior?” Total spend, total sessions, total invitations sent. Wait Until matches discrete events, not aggregates, so the right way to model thresholds is to fire a discrete event from your backend the moment a threshold is crossed. Use it for:- Lifetime value tiers (silver, gold, platinum)
- Engagement milestones (10 workouts, 50 lessons, 100 transactions)
- Funded-account thresholds
- Streak or consistency achievements
Example: 30-day VIP loyalty Journey
An ecommerce app enters new users into a 30-day loyalty Journey onsignup_completed. When cumulative spending crosses a threshold, the backend fires a derived loyalty_milestone_reached event. The Journey waits up to 30 days for that event to land and routes accordingly.
Backend-fired milestone event (sent the first time the user crosses the threshold):
JSON
Send the milestone event from your backend
Track cumulative spend in your own data store. Each time a
purchase succeeds, recompute the running total. The first time it crosses the threshold, fire loyalty_milestone_reached to OneSignal.Fire this event once per user per milestone. Use the idempotency_key field to deduplicate if your retry logic could re-fire it.Set the entry rule
Custom events entry rule on
signup_completed. Every new user enters the Journey at signup; the wait then determines what they receive.Add a Wait Until on the milestone
Wait Until step:
- Condition: Custom Event
loyalty_milestone_reached - Expiration: 30 days
- No Event Matching needed. The milestone fires once per user per signup, so there is no instance ambiguity to resolve.
Configure the event branch
On the event branch, send a “Welcome to VIP” email and a celebratory push using the milestone event’s properties:
Liquid
Configure the expiration branch
On the expiration branch (30 days elapsed without the milestone), send a motivating nudge that does not commit to a specific dollar amount, since the cumulative spend lives in your backend rather than the Journey instance:If you want the message to show exactly how much further the user has to go, fire a second derived event like
Liquid
loyalty_progress_check with a cents_to_next_tier property and add another Wait Until earlier in the Journey to capture it.Personalize messages with event properties
Every Journey instance stores the events that drove its entry and any Wait Until matches. Reference them in templates with Liquid:Liquid
journey.first_eventis the entry-triggering event.journey.last_eventis the most recently matched event (entry or any Wait Until).journey.event.EVENT_NAMEreturns the most recent matched event with a specific name.
Personalize with Custom Events
Full Liquid reference, nested property access, for-loops over event arrays, and worked examples.
Optimization checklist
Work through this list as you ship more event-driven Journeys.- Use Event Matching wherever the same user can have concurrent instances. Orders, conversations, surveys, support tickets. Bind Wait Until events to a shared identifier so each instance advances independently. See Event Matching.
- Always pair Wait Until with a meaningful expiration. A wait without a thoughtful expiration leaves users in limbo. Decide what message, or no message, should fire if the expected event never arrives.
- Reuse the same event for entry and exit on repeating signals. Cart updates, address changes, plan changes. When a triggering event repeats, set the same event name on the entry rule and the exit rule. The user exits the current instance and re-enters with the latest properties. See Exit when Custom Event condition occurs.
- Watch the 100-property storage cap. Each Journey instance stores at most 100 event properties across all matched events. Long-running Journeys with many Wait Until matches can hit the cap.
- Stay under the 2024-byte event payload limit. Strip properties you won’t reference. Move large content like HTML, base64 images, or full carts behind a deep link or a backend lookup.
- Verify events with the Custom Events Activity feed before activating. The dashboard’s Event Activity tab shows events as they land. Use it to confirm property names, types, and values match your Journey configuration before turning the Journey live.
FAQ
When should I use a Custom Event entry rule vs. a segment-based entry rule?
Use a Custom Event entry rule when the trigger is a discrete user action (the user just placed an order). Use a segment-based entry rule when the trigger is a state (the user has been inactive for 7 days). You cannot combine the two on the same Journey, but you can still reference Custom Events inside a segment-based Journey via Wait Until steps.Can a single user be in the same Journey multiple times?
Yes. Custom Event entry rules allow concurrent instances by default. Eachorder_placed for the same user starts its own instance with its own stored event. To prevent overlap, either filter the entry rule by a property or set the same event name as both the entry rule and the exit rule, which exits the current instance before the new one begins. See the Custom Event entry rule warning.
Why is my Wait Until step matching the wrong event?
A Wait Until without Event Matching advances on any event of the configured name for that user. If the user has multiple concurrent Journey instances or fires the event from an unrelated workflow, the Wait Until completes prematurely. Bind it to a shared property likeorder_id or workspace_id so each instance scopes to its own context.
How do I model thresholds like “spent more than $100”?
For per-event thresholds (this single purchase was above 100), compute the cumulative state in your backend and fire a derived event the moment the threshold is crossed. Wait Until matches discrete events, not aggregates.What happens to the stored event when a user exits a Journey?
The stored event is cleared. If the user re-enters the same Journey, a freshjourney.first_event is captured from the new entry trigger. See Event property storage rules.
Do I need to send Custom Events from a mobile or web SDK?
No. You can send Custom Events from any source: SDKs, the Create Custom Events API, or one of OneSignal’s data integrations. All sources are treated identically inside Journeys.Related pages
Custom Events
Send events from your SDK or REST API, configure retention, and verify events in the dashboard.
Personalize with Custom Events
Full Liquid reference for
journey.first_event, journey.last_event, and journey.event.EVENT_NAME.Journey actions
Wait, Wait Until, Time Window, Yes/No branch, Split Branch, and Tag User reference.
Journey settings
Configure entry rules, exit rules, re-entry, and scheduling.
Welcome Journey: Mobile gaming
Worked example of a 7-day onboarding Journey using a
daily_session Wait Until.Abandoned cart tutorial
End-to-end ecommerce Journey using cart events for entry, exit, and personalization.