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

# Personalization

> Choose the right personalization method in OneSignal. Compare Properties, Custom Events, API custom_data, Data Feeds, and CSV uploads to send dynamic messages using Liquid syntax.

<Frame>
  <iframe width="560" height="315" src="https://www.youtube.com/embed/71gtwxKWlN8?si=mlgmP6eE3652r_cX" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />
</Frame>

Personalization lets you send messages that include dynamic data — such as a user's name, cart items, account balance, booking details, or a one-time password.

This guide helps you choose the right personalization method based on:

* Where your data lives
* Whether it should persist
* How the message is triggered

***

## How personalization works

Personalization in OneSignal has two parts:

1. **Liquid syntax** – defines how values render in your message
2. **A data source** – determines where the value comes from

At send time, OneSignal resolves your Liquid variables using the selected data source.

<Info>
  Liquid controls formatting and logic (variables, loops, conditionals). The data source determines what values are available.
</Info>

**Example:**

```liquid Liquid theme={null}
Hi {{ user.tags.first_name }},

Your verification code is {{ message.custom_data.otp }}.
```

* `user.tags.first_name` is a stored property
* `message.custom_data.otp` is passed via the API `custom_data` field

***

## Supported fields by message type

<Tabs>
  <Tab title="Email">
    * Subject, Reply-to, and Pre-header
    * Message Body
    * Image substitution in HTML blocks. Example: `<img src="{{image_url}}"/>`
    * Button block actions like URLs, Mail to, and other fields.
  </Tab>

  <Tab title="Push">
    * Title (`headings`), Subtitle, Body (`contents`)
    * Image URL
    * Launch URL. Example: `https://example.com/{{last_category_viewed}}`
    * Additional `data` doesn't support Liquid syntax.
  </Tab>

  <Tab title="SMS">
    * Message Body (`contents`)
  </Tab>

  <Tab title="In-App Messages">
    <Warning>
      Only Tag substitution is supported at this time.

      The tag must be set before the user opens the app to start a new session. The tags available are in the [`getTags` method](./mobile-sdk-reference#gettags).
    </Warning>

    **Block editor:**

    * Tag substitution works in Text, Button, Image Blocks.

    **HTML editor:**

    * Tag substitution works in:
      * Header `<h*>` and `<p>` tags. Example: `<h2>Hello {{first_name}}!</h2>`
      * Element attributes: `["src", "href", "action", "data"]`. Example: `<img src="{{image_url}}"/>`

    <Note>
      See [In-app message JavaScript APIs](./in-app-message-api) for more details and examples.
    </Note>
  </Tab>

  <Tab title="Live Activities">
    * Within the `event_updates`, `contents` and `headings` properties.
  </Tab>
</Tabs>

***

## Data sources

OneSignal supports five data sources for personalization. Use the table below to identify which source fits your use case, then read the detailed section for implementation guidance.

| Data source                                     | What it is                                               | When to use                                                         | Persisted | Available in Journeys |
| ----------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------- | --------- | --------------------- |
| [**Properties**](#properties)                   | Tags, External ID, subscription data, app fields         | Reusable values stored in OneSignal (name, plan, preferences)       | Yes       | Yes                   |
| [**Custom Events**](#custom-events)             | Event properties captured at Journey entry or Wait Until | Behavioral personalization inside Journeys                          | Per event | Journey only          |
| [**API `custom_data`**](#api-custom_data)       | Key-value pairs passed in the Create Message API         | One-time or sensitive values (OTP, secure links, cart items)        | No        | No                    |
| [**Data Feeds**](#data-feeds)                   | Live API call made at send time                          | Values that change frequently (pricing, inventory, account balance) | No        | Yes                   |
| [**Dynamic Content CSV**](#dynamic-content-csv) | CSV uploaded in the dashboard                            | Bulk campaigns with per-recipient content                           | Per send  | No                    |

<Warning>
  **Common mistakes to avoid**

  * Using Properties (Tags) for one-time values like OTPs or verification codes — use `custom_data` instead
  * Expecting `custom_data` to be available in Journeys or future messages — it exists only for the current API request
  * Assuming Custom Event properties are available outside of the event-triggered Journey entry or a Wait Until step
  * Using Data Feeds for static data that rarely changes — use Properties instead
</Warning>

### Properties

Properties include Tags, External ID, subscription data, and app-level fields.

They are:

* Persistent
* Reusable
* Available across messages, templates, [Journey webhooks](./journeys-webhook), and [Event Streams](./event-streams).

**Use Properties when:**

* The value exists in OneSignal
* The value is persistent
* You reuse it across campaigns

<Card title="Personalize with Properties" icon="tag" href="./personalization-properties-and-tags">
  Learn how to reference stored persistent property data.
</Card>

***

### Custom Events

[Custom Events](./custom-events) can personalize messages inside [Journeys](./journeys-overview) using event properties.

When an event **starts a Journey** or **matches a Wait Until condition**, OneSignal stores that event so its properties can be referenced in message templates using Liquid.

**When to use Custom Events:**

* Event triggered messages with Journeys
* The message should reflect event-specific data

<Warning> Only events that trigger Journey entry or a Wait Until step are stored for personalization. Events sent outside those moments are not available to Journey messages. </Warning>

<Card title="Custom Event personalization" icon="bolt" href="./personalization-custom-event">
  Complete guide to using event properties to personalize Journeys.
</Card>

***

### API `custom_data`

The `custom_data` field in the [Create Message API](/reference/create-message) lets you send message-specific values from your backend.

This data:

* Exists only for the current request
* Is not stored in OneSignal
* Is not available in Journeys

**Use `custom_data` when:**

* Sending one-time or sensitive values (OTP, secure links)
* Passing arrays (cart items, order lines, leaderboard scores)
* Sending transactional or API-triggered messages

<Card title="Personalize with API custom_data" icon="code" href="./personalization-api-custom-data">
  Learn how to pass transient personalization data.
</Card>

***

### Data Feeds

[Data Feeds](./data-feeds) call your API at send time and inject the response into your message.

**When to use Data Feeds:**

* You need the latest value at delivery
* The data lives in your backend
* The value may change between sends

<Card title="Data Feeds" icon="cloud-arrow-up" href="./data-feeds">
  Pull real-time backend data into messages at send time.
</Card>

***

### Dynamic Content CSV

Upload a CSV file into the OneSignal dashboard and reference its values using Liquid.

**Use CSV when:**

* Customizing different sections of a bulk campaign for each recipient
* Translations or custom data for each recipient is exportable to a CSV file
* You do not want to use the API

<Card title="Dynamic Content CSV" icon="file-csv" href="./dynamic-content">
  Personalize dashboard campaigns using CSV uploads.
</Card>

***

## Detailed guides

Use the guides below for step-by-step implementation details and advanced examples.

<Columns cols={2}>
  <Card title="Using Liquid syntax" icon="droplet" href="./using-liquid-syntax">
    Learn how to insert dynamic data into messages using Liquid. Covers variables, conditionals, loops, filters, formatting, and common personalization patterns.
  </Card>

  <Card title="Data Feeds" icon="cloud-arrow-up" href="./data-feeds">
    Pull real-time data from your own APIs at send time. Use Data Feeds when message content depends on live backend values like balances, availability, or pricing.
  </Card>

  <Card title="Custom Events personalization" icon="bolt" href="./personalization-custom-event">
    Personalize Journey messages using event properties captured when users enter or progress through a Journey. Ideal for behavioral and event-driven workflows.
  </Card>

  <Card title="Properties & Tags" icon="tag" href="./personalization-properties-and-tags">
    Use stored user, subscription, message, and app properties to personalize content across messages, templates, Journey webhooks, and Event Streams.
  </Card>

  <Card title="API custom_data" icon="code" href="./personalization-api-custom-data">
    Pass per-message and transient data from your backend using the Create Message API. Best for OTPs, carts, arrays, and bulk transactional personalization.
  </Card>

  <Card title="Dynamic Content CSV" icon="file-csv" href="./dynamic-content">
    Upload CSV files in the dashboard to personalize campaigns at scale. Each row maps to a recipient and can be referenced using Liquid.
  </Card>
</Columns>

### Tutorials

These guides show how to implement personalization in practice.

<Columns cols={2}>
  <Card title="Verification, Magic Link, & OTP" icon="key" href="./example-verification-magic-link-otp">
    Send secure verification messages using one-time passwords, magic links, or custom URLs with API-driven personalization.
  </Card>

  <Card title="Abandoned cart Journey" icon="cart-shopping" href="./abandoned-cart">
    Build an automated Journey that detects cart activity, waits for inactivity, sends a personalized reminder, and exits users immediately after purchase.
  </Card>

  <Card title="Booking confirmations" icon="calendar" href="./booking-confirmations">
    Send booking confirmation and recovery messages using Custom Events, Journeys, and Data Feeds based on real-time reservation status.
  </Card>

  <Card title="Transactional messages" icon="receipt" href="./transactional-messages">
    Learn how to send receipts, alerts, confirmations, and other transactional messages across channels using APIs and automation.
  </Card>
</Columns>
