How personalization works

Personalization is powered by Liquid syntax, a templating language that lets you inject values into message fields.

You can personalize messages using:

  • Properties: Built-in user, message, app, or org values.
  • Data tags: Your own key: value strings saved per user.
  • Custom data: Dynamic content passed at send time via API.
  • Dynamic content: CSV-based personalization via dashboard uploads.

Key considerations

Personalizing messages 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 the message but the use cases are endless.

What is the goal?

  • 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?

  • Do you want to send the message from your server or through the OneSignal dashboard?

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.


Data sources

Properties

Predefined fields you can reference using Liquid syntax. Example:

Hi! 
Thanks for subscribing with email {{ subscription.email }}. 
Your user ID is {{ subscription.external_id }}.

Properties are not available in In-App Messages or Live activities.

  • subscription.external_id: The External ID associated with the Subscription.
  • subscription.email: The email address of the email Subscription being sent the message.
  • subscription.phone_number: The phone number of the SMS Subscription being sent the message.
  • subscription.push_token: The push token of the push Subscription being sent the message.
  • subscription.language: The language code of the user.
  • subscription.unsubscribe_token: The token used to identify a subscription for unsubscribe (when an email Subscription).

Data tags

Data Tags are key: value String data you can set on each user. Example:

Hi {{ first_name | default: "friend" }}!
Congrats on reaching level {{ level | default: "1" }}!

If you set 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.

custom_data (API Only)

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.”

Dynamic Content

Using a CSV of data, you can upload this into the OneSignal dashboard to customize the campaign based on per-user data. See Dynamic Content for more details.


Examples



How personalization works

Personalization is powered by Liquid syntax, a templating language that lets you inject values into message fields.

You can personalize messages using:

  • Properties: Built-in user, message, app, or org values.
  • Data tags: Your own key: value strings saved per user.
  • Custom data: Dynamic content passed at send time via API.
  • Dynamic content: CSV-based personalization via dashboard uploads.

Key considerations

Personalizing messages 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 the message but the use cases are endless.

What is the goal?

  • 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?

  • Do you want to send the message from your server or through the OneSignal dashboard?

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.


Data sources

Properties

Predefined fields you can reference using Liquid syntax. Example:

Hi! 
Thanks for subscribing with email {{ subscription.email }}. 
Your user ID is {{ subscription.external_id }}.

Properties are not available in In-App Messages or Live activities.

  • subscription.external_id: The External ID associated with the Subscription.
  • subscription.email: The email address of the email Subscription being sent the message.
  • subscription.phone_number: The phone number of the SMS Subscription being sent the message.
  • subscription.push_token: The push token of the push Subscription being sent the message.
  • subscription.language: The language code of the user.
  • subscription.unsubscribe_token: The token used to identify a subscription for unsubscribe (when an email Subscription).

Data tags

Data Tags are key: value String data you can set on each user. Example:

Hi {{ first_name | default: "friend" }}!
Congrats on reaching level {{ level | default: "1" }}!

If you set 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.

custom_data (API Only)

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.”

Dynamic Content

Using a CSV of data, you can upload this into the OneSignal dashboard to customize the campaign based on per-user data. See Dynamic Content for more details.


Examples