> ## 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 six 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                    |
| [**Brand Kit assets**](#brand-kit-assets)       | Colors, fonts, and logos configured in your app's [Brand Kit](./brand-kit) | Keep templates and reusable content aligned with your brand         | Yes (per app) | Yes                   |

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

***

### Brand Kit assets

[Brand Kit](./brand-kit) stores your colors, fonts, and logos per app. Reference them with Liquid to keep templates and reusable content aligned with your brand at delivery time.

Unlike the other data sources on this page, Brand Kit values are the same for every recipient. They personalize the *message* to your brand rather than to the individual user.

**Use Brand Kit assets when:**

* You want templates or saved rows to update automatically after a rebrand.
* Multiple channels or templates share the same colors, fonts, or logos.
* You need consistent branding without hardcoding hex values or asset URLs in every message.

```liquid Liquid theme={null}
{{ brand_assets.colors.primary.hex }}
{{ brand_assets.colors.secondary.hex }}
{{ brand_assets.fonts.primary.family }}
{{ brand_assets.logos.primary.url }}
```

<Card title="Brand Kit" icon="palette" href="./brand-kit">
  Configure brand voice, colors, fonts, and logos once and reference them anywhere Liquid is supported.
</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>
