Overview

Dynamic Content lets you personalize messages at scale—without creating separate versions for each audience. Whether you’re sending multi-language messages or tailoring content by region or campaign, Dynamic Content allows you to define one message that automatically adjusts for each user.

You do this by uploading a CSV file or programatically using our Template APIs with data containing content variations and referencing those values in your message using Liquid Syntax. When a message is sent, OneSignal pulls the appropriate content for each user based on their Data Tags.

Dynamic Content works across Push, Email, and SMS, making it ideal for:

  • Multi-language onboarding or marketing flows
  • Region or segment-specific promotions
  • Personalization driven by external campaign data

Key benefits:

  • Personalization at scale – Deliver custom experiences without creating and managing dozens of message variants.
  • Multi-language support – Write one message that automatically displays in each user’s preferred language.
  • Dynamic segmentation – Change message content based on user properties like language, region, plan, or custom tags such as campaign_id.
  • Collaboration without friction – Let translators or non-technical teams create and edit message content directly in CSV files—no OneSignal login needed.
  • Cross-channel compatibility – Use the same CSV logic across Push, Email, and SMS campaigns.

Common use cases:

  • Event announcements per country or city
  • Personalized push notifications based on campaign ID or purchase history
  • Testing multiple subject lines or messages across user groups

See Message Personalization for more options on personalizing your messages.


Dynamic Content setup

Create a new message or template.

From the dashboard: Select Dynamic Content.

  • Upload a CSV file that maps message content to Data Tags.

From the Templates APIs: Use the dynamic_content property.

  • Great solution if your dynamic content changes frequently.

Dynamic Content button found in the push create editor.

Format your content

You can start with a blank file or choose one of the provided templates:

  • Multi-language
  • Content personalization

CSV template options provided by OneSignal.

For API details, see:

CSV requirements

  • File size must be under 200 KB
  • Column headers:
    • Alphanumeric only
    • Use underscores (_) instead of spaces
    • Avoid special characters
  • Ensure UTF-8 encoding in your spreadsheet editor

Map the subscription.language property to different translations per section.

Similar to a VLOOKUP formula in Excel, we match user attributes with the corresponding content from the CSV.

Reference Dynamic Content in messages

To display personalized content, use this Liquid syntax based on if you used a CSV or the dynamic_content property.

{{dynamic_content.file_name.section_name[user_property]}}

Replace:

  • file_name: CSV file name (without .csv), omit if using the dynamic_content property
  • section_name: Value in the first column of the row or property
  • user_property: Column header or property matching a Data Tag

Notation examples

Multi-language:

{{dynamic_content.translations.section_1[subscription.language]}}

Campaign ID:

In this example, campaign_id is a Data Tag.

{{dynamic_content.content_personalization_template[campaign_id].title}}

Add fallback content

To ensure your message renders even if a user lacks a tag or property, you can use:

Fallback column in CSV

Include a default value in the liquid syntax to display data in a column if the user doesn’t have the property or tag.

{{ dynamic_content.translations.section_1[discount_amount] | default: dynamic_content.translations.section_1.default_discount }}
{% assign campaign_id = campaign_id | default: "21234086613" %}
{{ dynamic_content.content_personalization_template[campaign_id].title }}

Hard-coded fallback (if/else)

{{ dynamic_content.translations.section_1[subscription.language] | default: "Tu descuento" }}
{% assign campaign_name = dynamic_content.translations.campaign_name[subscription.language] %}

{% if campaign_name %}
{{ campaign_name }}
{% else %}
Save the Whales
{% endif %}

Examples

Multi-language email

Use translations.csv to localize your email:

  • {{dynamic_content.translations.section_1[user.language]}}
  • {{dynamic_content.translations.section_2[user.language]}}

Example shows how to add the liquid syntax for the multi-language example.

Update subject lines, preheaders, button labels, and URLs using Liquid.

Personalized push message

Use content_personalization_template.csv for user-specific push messages:

  • {{dynamic_content.content_personalization_template[campaign_id].title}}
  • {{dynamic_content.content_personalization_template[campaign_id].message}}
  • {{dynamic_content.content_personalization_template[campaign_id].url}}

Example shows how to add the liquid syntax for the content personalization example.


Test and preview

1

Create a test CSV

Multi-language test:

  • Columns: email, language

Personalization test:

  • Columns: external_id, campaign_id

Example CSV to upload and create segment of email testers.


Use + addressing in emails to test multiple variations: username+test@example.com

Example CSV to upload and create segment of testers with external_id and the tag campaign_id.

2

Upload your test segment

Go to Subscriptions or Segments > Upload/Import Users.

Set the first column as the identifier. All other columns are treated as user properties or Data Tags.

Create a segment of your testers to repeat as needed.

See Import for more on uploading user data.

3

View the message

After sending your test messages, check your inboxes or devices to verify display.

Example shows the email sent with Dynamic Content.

Example shows the push sent with Dynamic Content.

You can now scale personalized messaging using these templates and CSVs.


Usage considerations

When to use Dynamic Content vs. Custom Data

  • Use Dynamic Content for diverse languages or personalized message blocks.
  • Use Custom Data for simple, inline personalization in a single language.

Editing templates

Re-upload CSVs or use the dynamic_content property of the Update Template API to update.

Special characters in keys

Use hash notation if keys contain non-alphanumeric characters:

{{ dynamic_content.file_name["!the_row!"]["&the_column&"] }}

Use dot notation for standard alphanumeric keys:

{{ dynamic_content.file_name.the_row.the_column }}