Skip to main content
Custom Events let you personalize Journey messages using event properties (product names, prices, URLs, arrays, etc.). Event properties become available in templates only if the event:
  • Triggers Journey entry, or
  • Matches a Wait Until condition inside the Journey
Stored event properties can be accessed using Liquid syntax.

How Custom Event personalization works

Add event properties to your Journey messages following these steps:
1

Send a Custom Event with properties

Example Custom Events payload:
JSON
2

Reference event properties in your Journey message templates

Use Liquid syntax to access event properties.

Common Liquid access patterns

Nested Liquid access patterns

You can also access nested properties using dot and bracket notation:
Liquid
Invalid Liquid or missing properties render as empty strings. Messages still send. Use default filters to set a fallback.
3

Create a Journey

Setup the Journey to use the Custom Event as the entry rule and/or Wait Until condition.Add message nodes with the templates.

Event property storage rules

  • You can use multiple events in your Journey by combining entry rules and Wait Until steps.
  • Maximum: 100 stored event properties per user per Journey instance (oldest dropped).
  • Event properties are stored per user, per Journey instance.
  • Events sent before entry are not accessible.
  • Event properties are cleared when the user exits the Journey.
Events stored in one Journey cannot be accessed in another Journey.

Custom Event Liquid reference

Use these objects to access stored events inside the Journey.
The first stored event for this Journey instance.
  • If using a Custom Event Entry Rule, than this is the event that caused Journey entry.
  • If not using a Custom Event Entry Rule, then this is the first event stored by matching a Wait Until condition.
Liquid
The most recent stored event for this Journey instance.
  • If only one event is stored, first_event and last_event return the same thing.
Liquid
The most recent stored event with a specific name.
  • Replace EVENT_NAME with your event name (e.g., purchase).
  • If the same event name is used multiple times, this returns the most recent instance.
Liquid
If your event name includes spaces or special characters, use bracket notation.Example Event: "name": "order status"
Liquid
array
All stored events for this Journey instance, in the order they were stored.
Liquid
  • journey.first_event is shorthand for journey.all_events[0].
  • journey.last_event is shorthand for the most recent event in the array.

Example: Abandoned cart templates using Custom Events

This example shows how to personalize abandoned cart messages using Custom Events. It builds on the Abandoned Cart tutorial. Example Custom Event set:
JSON

Email template

This example shows how to build an email template that displays:
  • The cart item count
  • Each product with image, name, quantity, and price using a for-loop
  • A button that links to the customer’s unique cart URL

Example Abandoned Cart Email Template

1

Create the email template

Navigate to Messages > Templates > New Email Template and open the Drag & Drop Editor.
2

Add the layout structure

Create five rows:
  • Rows 1, 2, and 4: one column with a Paragraph block
  • Row 3: four columns with HTML | Paragraph | Paragraph | Paragraph
  • Row 5: one column with a Button block

Starter email template for abandoned cart

3

Display the item count

In row 1, add:
Liquid
For better grammar, you could use a conditional to say “1 item” vs “2 items”, but for abandoned cart emails, plural is usually acceptable.
Liquid
4

Start the for-loop

Use a for-loop to repeat the product display row for each cart item.In row 2 (loop start), add:
Liquid
What this does:
  • Begins a loop that iterates over each object in the cart array
  • Creates a temporary variable product representing the current item
  • Everything between {% for %} and {% endfor %} repeats once per cart item
  • You can name product anything (e.g., item, cartItem)—just stay consistent
For-loop placement: Make sure the {% for %} syntax is in its own Text block row. Don’t put it inside a multi-column row with other content, as this can break email rendering in some clients.
5

Display product details

This 4-column row shows image, name, quantity, and price. Because it’s inside the loop, it repeats for every cart item.In row 3 (product details), configure:Column 1 - HTML block (product image):
Columns 2–4 - Text blocks (product name, quantity, price):
  • Column 2: {{product.product_name}}
  • Column 3: {{product.product_quantity}}
  • Column 4: {{product.product_price}}
How the loop works:
  • On first iteration, product = first object in cart array
  • {{product.product_image}} gets the first item’s image
  • On second iteration, product = second object
  • Row repeats automatically for all cart items
Field name matching: Keys like product_image must exactly match your event payload (case-sensitive). Mismatches render as empty strings.
6

End the for-loop

Close the loop to mark where repetition stops.In row 4 (loop end), add:
Liquid
Every {% for %} must have a matching {% endfor %}. Missing this will break email rendering.
7

Add the cart URL button

In the row 5 Button block, set the Action URL to:
Liquid

Finished base email template for abandoned cart without styling

8

Test the template

  • Add the template to a blank Journey and set the entry rule to a Custom Event.
  • Enabling the Journey and entering yourself into it via the Custom Event API.
  • Verify the data displays correctly.
Success! Now you can apply your own styling to the template. See Design emails with drag-and-drop.

Push template

Push notifications have limited space, so display one item and mention the total count. Message field: Display item and count with correct grammar using conditional statements.
Liquid
Image field:
Liquid
Launch URL field:
Liquid
Send yourself a test push notification by adding it to the test Journey and entering yourself into it via another Custom Event API call. You should see a notification similar to this appear:

Abandoned cart push template example

Success! You can now create more templates and use them in the Abandoned Cart Journey.

Troubleshooting & best practices

Common mistakes: Best practices:
  • Always test templates before going live
  • Use default filters for optional properties
  • Validate event schema matches template expectations

Message personalization

Overview of all personalization options in OneSignal, including when to use Custom Events vs other methods.

Custom Events

Complete guide to implementing and sending Custom Events via SDK or API.

Journeys overview

Learn how to build automated messaging workflows with triggers, conditions, and actions.

Journey settings

Configure event-triggered entry rules and Journey behavior.

Wait Until actions

Use Wait Until nodes to store additional events during Journey progression.

Using Liquid syntax

Complete Liquid reference with filters, conditionals, loops, and string manipulation.

Templates

Create and manage reusable message templates for use in Journeys.
Need help?Chat with our Support team or email support@onesignal.comPlease include:
  • Details of the issue you’re experiencing and steps to reproduce if available
  • Your OneSignal App ID
  • The External ID or Subscription ID if applicable
  • The URL to the message you tested in the OneSignal Dashboard if applicable
  • Any relevant logs or error messages
We’re happy to help!