Skip to main content

How personalization works

You can add custom data into messages, templates, Journey Webhooks, and Event Streams using Liquid syntax. Data can be sourced from:

Key considerations

Personalization helps users connect deeper with your app, potentially increasing engagement and revenue. Common examples are putting the person’s name or specific information (like abandoned cart items) in a message but the use cases are endless. What is the goal? What do you want to send in the message? Examples:
  • Add a user property like name or ID
  • Show invoice or OTP
  • Send dynamic content from a CSV
Where is the data located?
  • Is the data stored in OneSignal like a property or a tag?
  • Is the data stored in your own database?
  • Do you need to pass data from your backend to OneSignal?
How do you want to send the message?
  • One-time messages: Send the message from your server or through the OneSignal dashboard?
  • Recurring messages: Using Journeys or other automated workflows?
Example:
  • Goal: Send a one-time-passcode to help a user login.
  • Data:
    • OTP stored on your backend.
    • User name store in OneSignal as a tag.
    • External ID stored in OneSignal as a property.
  • Send:
    • From your server using our REST API.
    • From an automated workflow using Journeys.
A details walkthrough of this example is available in the Verification, Magic Link, & OTP example tutorial.See more Tutorial examples below.

Data sources

Available options for personalization.

Dynamic Content

This current guide on personalization explains how to add dynamic content to your messages, templates, Journeys, and Event Streams. OneSignal also provides a way of uploading a CSV of data to customize the messages based on data from your own database. See Dynamic Content for more details.

Data feeds

Data Feeds are a way to pull real-time data from your APIs directly into messages at send time. Just connect a template to your data source and we will pull the data from your server and inject it into the message.

Custom Events

Reference Custom Events within Templates used in Journeys. Depending on the Journey configuration, it may store one or more Custom Events on behalf of the user. You can use Liquid syntax to display properties of these stored events, or to conditionally display parts of your message based on the events.
  • Custom Event Properties
  • Example Custom Event Structure
  • Using Events in Templates
journey.first_event
For Event-triggered Journeys, this will always be the event that caused the user to enter the Journey.If the Journey’s entry rule is not event-triggered, then this will be the first event matched by a Wait Until condition.
{% assign event = journey.first_event %}
journey.last_event
The most recent Custom Event stored. If there is only one stored custom event, then first_event and last_event will return the same thing.
{% assign event = journey.last_event %}
journey.event.most_recent_event_name
The most recent event with a given name (replace most_recent_event_name with the name of the event you want to reference). If the same event is used multiple times, this will return the most recent instance. Example: purchase.
{% assign event = journey.event.purchase %}
For special characters (e.g. spaces), use hash notation if the event name contains non-alphanumeric characters. For example, if the event name is “order status”, you can reference it with journey.event["order status"].
{% assign event = journey.event["order status"] %}
journey.all_events
Provides an array of all events stored for this Journey for the user, in the order they were received. You can use for loops to iterate over them.
{% for event in journey.all_events %}
  {{ event.name }}
{% endfor %}
first_event and last_event are shorthand for all_events[0] and all_events[-1], respectively.

Properties

Predefined fields saved in OneSignal.
Properties are not available to substitute within In-App Messages or Live Activities.

User & Subscription properties

Follow the provided steps based on where you need to access the data.
  • Properties available in messages & templates
  • Properties available in Journey Webhooks & Event Streams
Use the subscription object to access properties at the Subscription level in messages and templates. Tags can also be accessed directly via the key name. For example:
Hi {{ first_name | default: "friend" }}!
Congrats on reaching level {{ level | default: "1" }}!
If you have tags first_name: Jon and level: 5 for User A and first_name: Jeff and level: 100 for User B, each will see their name and level in the message. Otherwise, they’ll see the default values.
subscription.external_id
The External ID associated with the Subscription.
Your user ID is {{ subscription.external_id }}.
subscription.email
The email address of the email Subscription being sent the message.
Thanks for subscribing with email {{ subscription.email }}.
subscription.phone_number
The phone number of the SMS Subscription being sent the message.
Thanks for subscribing with phone number {{ subscription.phone_number }}.
subscription.language
The language code of the user.
Preferred language: {{ subscription.language }}
subscription.unsubscribe_token
The token used with the Unsubscribe email with token API.
liquid
Unsubscribe: https://your-domain.com/unsubscribe?token={{ subscription.unsubscribe_token }}

Journey properties

The journey object is helpful for tracking the Journey name or referencing Custom Events within Templates used in Journeys.
journey.name
The name of the Journey.
JSON
{
  "journey.name": "{{ journey.name }}"
}

Message properties

The message object is helpful for accessing custom_data sent from your backend or for tracking properties of the message with Event Streams.
message.id
The OneSignal message ID.
JSON
{
  "message.id": "{{ message.id }}"
}
message.name
The name of the message.
JSON
{
  "message.name": "{{ message.name }}"
}
message.template_id
The OneSignal template ID.
JSON
{
  "message.template_id": "{{ message.template_id }}"
}

Template properties

The template object is helpful for accessing details about the Template used to send the message.
template.id
The OneSignal template ID.
JSON
{
  "template.id": "{{ template.id }}"
}
template.name
The name of the template.
JSON
{
  "template.name": "{{ template.name }}"
}

OneSignal properties

The app and org objects are helpful for accessing details about the App and Organization that sent the message.
app.id
The OneSignal App ID.
JSON
{
  "app.id": "{{ app.id }}"
}
app.name
The name of the OneSignal App.
JSON
{
  "app.name": "{{ app.name }}"
}
org.id
The OneSignal Organization ID.
JSON
{
  "org.id": "{{ org.id }}"
}
org.name
The name of the OneSignal Organization.
JSON
{
  "org.name": "{{ org.name }}"
}

API custom_data

Add personalization directly from your backend using custom_data and our Create message API. Steps:
  1. Create a template
  2. Use Liquid Syntax with format {{ message.custom_data.key }}
  3. Send API request with the custom_data object and template_id

Example: Flat JSON

Template
Your invoice for {{message.custom_data.product_name}} is ready.
URL: https://your-domain.com/invoice={{message.custom_data.invoice_id}}
API Request
{
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_email_tokens": ["THE_USER_EMAIL"],
  "custom_data": {
    "invoice_id": "463246732",
    "product_name": "Widget"
  }
}
Customer sees:
  • “Your invoice for Widget is ready.”
  • The final URL: https://your-domain.com/invoice=463246732

Example: Array data

Template
Your {{message.custom_data.cart_items[0].item_name}} is waiting for you!
Image: {{message.custom_data.cart_items[0].image_url}}
API Request
{
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_email_tokens": ["THE_USER_EMAIL"],
  "custom_data": {
    "cart_items": [
      {
        "item_name": "sweater",
        "img_url": "https://.../sweater.png"
      },{
        "item_name": "socks",
        "img_url": "https://.../socks.png"
      }
    ]
  }
}
Customer sees:
  • “Your sweater is waiting for you!”
  • The image: https://.../sweater.png

Example: Bulk personalization

To personalize a single message for many users in one request:
Template
{% assign eid = message.custom_data.users[subscription.external_id] %}
Hi {{ eid.first_name }}, you have {{ eid.points }} points. Your level is {{ eid.level }}.
API Request
{
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_aliases": {
    "external_id": ["user123", "user456"]
  },
  "custom_data": {
    "users": {
      "user123": { "first_name": "John", "points": "150", "level": "Gold" },
      "user456": { "first_name": "Sarah", "points": "200", "level": "Platinum" }
    }
  }
}
Customer sees:
  • “Hi John, you have 150 points. Your level is Gold.”
  • “Hi Sarah, you have 200 points. Your level is Platinum.”

Tutorials