• iOS
  • Android
  • Windows
  • Cordova
  • Web
  • Unity
  • Adobe Air
  • Dashboard
  • API
  • Guides
  • FAQ
  • Guides > Message personalization

    Basics

    Introduction

    You can personalize your push notifications from the campaign editor using the data you have collected on your users. Batch provides a system of dynamic contents and a templating engine, allowing you to make dynamic messages for your campaigns.

    Using the templating engine, you can reference and use user data inside your message. You can also add conditions to a message so that the content changes if some condition is fullfilled.

    Here's a quick example to see how a dynamic content with a user attribute looks.

    DynamicContentPreview

    Dynamic Content

    Before diving deep let's review the dynamic contents.

    We have implemented a dedicated interface to allow you to use user attributes in dynamic content and display custom data easily. When editing your message, just click on the {...} button, choose the custom attribute you want to personalize your message with and the formatting.

    That's it!

    As some of the targetted users might not possess the attribute you are using, you can add a default value. The preview will let you see how the notification will look for 10 random installs.

    Referencing attributes in your messages

    If you are using APIs to send your notifications, here is the syntax to use dynamic contents in a message. All attributes usable in a query are available in dynamic contents.

    They are:

    • attribute or c.attribute will refer to an installation attribute, collected from the SDK.
    • u.attribute will refer to a user attribute, collected via the Custom Data API.
    You're only on level {{ c.current_level }}, come back and play !
    

    If the user's c.current_level attribute is 6 this evaluates to:

    You're only on level 6, come back and play !
    

    Default values

    Note that with the previous dynamic content, if the user doesn't have the attribute the resulting message will be:

    You're only on level, come back and play !
    

    This is probably not what you want, so you can add a default value. The default value is used when the attribute doesn't exist.

    Here is how to use them:

    Special offer: Get {{ u.special_offer|default('-5%') }} by subscribing today !
    

    If the user's u.special_offer attribute is -15% then it evaluates to:

    Special offer: Get -15% by subscribing today !
    

    Otherwise it evaluates to:

    Special offer: Get -5% by subscribing today !
    

    Referencing tag collections

    All tag collections usable in a query are available in a dynamic content.

    They are:

    • t.tag will refer to an installation tag collection.
    • ut.tag will refer to a user tag collection.

    However, there's a catch: since a tag is a collection of values and we can only output a single value using a dynamic content, we have to introduce the concept of filters. A filter is a function you can apply to a value to transform it.

    In the case of a tag collection you can use the filter join to concatenate all values into a single string.

    For example:

    You've already beaten those levels: {{ c.levels_done|join(',') }}
    

    We will see more examples later, but you can check out the reference on filters to learn all about them.

    Note that using a filter on a tag collection that doesn't exist always produces an empty string. Using the previous example, if the tag collection c.levels_done doesn't exist the dynamic content evaluates to:

    You've already beaten those levels:
    

    Formatting

    Using raw data like this is great but you might want to format the attributes yourself.

    Formatting is done by using the following filters:

    The two filters are explained in details in the reference but here is a small example:

    You have accumulated {{ u.points|formatNumber(decimals=2) }} points, make sure to use them before {{ u.points_expiration_date|formatDate('yyyy-MM-dd') }}
    

    This evaluates to:

    You have accumulated 20.68 points, make sure to use them before 2017-02-30
    

    Be sure to check out the reference documentation to learn about all options !

    Templating

    Dynamic contents are already really powerful but they're not always enough.

    For example, what if you want to display a completely different message based on which level a user is on, or how much fidely points he has ?

    This is a job for templates.

    Templates allow you to do conditional statements, define variables and even do some light arithmetic.

    Conditions

    Suppose you want to congratulate a user based on how far he is into your game.

    You could write something like this:

    {% if current_level > 3 %}
    Good job on beating level 3 ! You're now halfway through the game, keep pushing !
    {% else if current_level > 5 %}
    Almost there ! One more level and you beat the game !
    {% else if current_level == 6 %}
    Amazing ! You've beat the game ! Go take a look at the amazing perks you unlocked !
    {% endif %}
    

    Now depending on the value of the user's current_level attribute, the message will be one of the 3 possibilities.

    Variables and arithmetic

    Sometimes it might be handy to compute some value and keep a reference to it so you can reuse it multiple times inside your template.

    It is mainly a quality of life improvement but still useful.

    For example, suppose you want to compute an expiration date based on multiple user attributes and remind the user when their subscription will expire.

    Given the following rules:

    • premium users have a subscription for 90 days
    • users subscribed to the newsletter have a subscribed for 75 days
    • users younger than 25 and not premium have a subscription for 60 days
    • all other have a subscription for 50 days

    You could write something like this:

    {% if premium %}
    {% set $expiration_date = c.subscription_date + 90d %}
    {% else if c.has_newsletter_subscription %}
    {% set $expiration_date = c.subscription_date + 75d %}
    {% else if c.age < 25 %}
    {% set $expiration_date = c.subscription_date + 60d %}
    {% else %}
    {% set $expiration_date = c.subscription_date + 50d %}
    {% endif %}
    Hey {{ c.first_name ~ c.last_name }}, friendly reminder that your subscription will expire on {{ $expiration_date|formatDate('yyyy-MM-dd') }}
    

    This evaluates to:

    Hey John Smith, friendly reminder that your subscription will expire on 2018-01-03
    

    Obviously the date will change based on what kind of subscription the user has.

    There are a couple of new features here:

    • Defining a variable with set $variableName = <expression>. A valid expression has to return a single value of any type. More about here
    • Arithmetic. You can do simple math inside a template. Conveniently, you can add also do arithmetic on a date by using a duration
    • Concatenation. You can concatenate multiple values into a single one using the ~ operator.

    Referencing custom app data

    Dynamic contents and templating also allow you to reference custom application data to use in your messages.

    These are tables of key/value pairs that you can upload using our dashboard.

    For example, given a table with the name population_by_city with the following content:

    2988507,2244000
    2996944,484344
    2995469,850726
    2973783,271782
    3031582,239157
    

    These are the population of Paris, Lyon, Marseille, Strasbourg and Bordeaux respectively.

    You can now use them like this.

    You live in a city of {{ lookup('population_by_city', b.city_code) }}
    

    For someone in Paris, this evaluates to:

    You live in a city of 2244000
    

    You can use any attribute or literal value as the lookup key. This works too:

    You live in a city of {{ lookup('population_by_city', 2988507) }}
    

    However by doing this you lose the benefit of the custom app data table. This also works:

    You live in a city of {{ lookup('population_by_city', c.my_custom_city_code) }}
    

    Use cases

    After looking into how it works, let's look at some use cases for dynamic contents and templates that would otherwise be hard or even impossible to do.

    Electoral results

    Suppose you want to report on electoral results for every city in France. There are currently 35416 cities in France and you want each user to have the result from their city when receiving the notification.

    Without dynamic contents you could only do this by creating one campaign per city with a query matching the city. In that case you'd need more than 35k campaigns, this is obviously not good.

    Instead you can do the following:

    • create a table named electoral_results_201710 for example.
    • each row in the table should be $city_code => $result.

    Example:

    2988507,Yes 20.3% - No 79.7%
    2996944,Yes 48.5% - No 51.5%
    2995469,Yes 74% - No 26%
    2973783,Yes 38% - No 62%
    3031582,Yes 93.2 - No 6.8%
    

    These are the results of Paris, Lyon, Marseille, Strasbourg and Bordeaux respectively.

    • create a message containing a lookup on the table and city code.

    Example:

    Election day result: {{ lookup('electoral_results_201710', b.city_code) }}
    

    For someone in Paris this will evaluate to:

    Election day result: Yes 20.3% - No 79.7%
    

    Each user will have a customised message based on where he is.

    Loyalty program goals

    Suppose you have a loyalty program for your application where the user can gain points and at some threshold you gain a loyalty level. Here is an example of a points scale:

    • 100 points - regular
    • 500 points - silver
    • 1000 points - gold
    • 5000 points - platinum

    Suppose also that you attach special one time discounts every time the user gains a level.

    Finally, suppose you want to remind a user that they're about to reach the next level with the following message:

    Gain 55 more points to reach Gold and get a one time discount of 15%!
    

    Let's break down what we need:

    • the number of points to reach to get to a level
    • the number of points that a user has to gain
    • the discount for a level

    Prerequisites

    For this to work you need to feed us the data we'll be working with; you can do so using custom attributes or the Custom Data API.

    We imagine the following user attributes:

    u.loyalty_points an integer value representing the current number of points a user has

    We also need two custom app data tables:

    loyalty_thresholds containing this:

    regular,100
    silver,500
    gold,1000
    platinum,5000
    

    The query

    Before diving into the template let's look at what query we should use:

    {
      "$or": [
        "$and": [
          "u.loyalty_points": {
            "$gte": 85
          },
          "u.loyalty_points": {
            "$lt": 100
          }
        ],
        "$and": [
          "u.loyalty_points": {
            "$gte": 475
          },
          "u.loyalty_points": {
            "$lt": 500
          }
        ],
        "$and": [
          "u.loyalty_points": {
            "$gte": 850
          },
          "u.loyalty_points": {
            "$lt": 1000
          }
        ],
        "$and": [
          "u.loyalty_points": {
            "$gte": 4950
          },
          "u.loyalty_points": {
            "$lt": 5000
          }
        ]
      ]
    }
    

    This will match any user that is just about to reach the next level.

    The template

    Here is how the template could look like:

    {% if u.loyalty_points >= 85 and u.loyalty_points < 100 %}
    {% set $nextLevel = 'regular' %}
    {% set $discount = '5%' %}
    {% else if u.loyalty_points >= 475 and u.loyalty_points < 500 %}
    {% set $nextLevel = 'silver' %}
    {% set $discount = '10%' %}
    {% else if u.loyalty_points >= 850 and u.loyalty_points < 1000 %}
    {% set $nextLevel = 'gold' %}
    {% set $discount = '15%' %}
    {% else if u.loyalty_points >= 4950 and u.loyalty_points < 5000 %}
    {% set $nextLevel = 'platinum' %}
    {% set $discount = '35%' %}
    {% endif %}
    {% set $remaining = lookup('loyalty_thresholds', $nextLevel)|int - u.loyalty_points %}
    Gain {{ $remaining }} more points to reach {{ $nextLevel|upper }} and get a one time discount of {{ $discount }}
    

    Special discounts after a purchase

    Suppose you want to give a user a special discount if they didn't buy anything after a month, and at the same time you want to remind them what they bought.

    Suppose also that the discount changes based on how much time has passed since the purchase:

    • 20% after a month
    • 40% after more than two months

    For example you could have the following message:

    Did you like your Nike Air Max ? Get a 20% discount on all purchase today!
    

    Let's break down what we need:

    • the product name of the last purchase
    • the date of the last purchase

    Prerequisites

    Like before, for this to work you need to feed us the data we'll be working with; you can do so using custom attributes or the Custom Data API.

    In this use case we'll use custom events so you need to have that working too.

    We image the following user attributes:

    u.last_purchase_product_name a string containing the product name of the last purchase. It should be a properly formatted string.

    We also need one custom event:

    e.purchase which tracks every time a user has purchased something.

    The query

    The query needs to filter users that haven't purchased anything since at least a month:

    {
      "age(e.purchase)": {
        "$gte": "30d"
      }
    }
    

    The template

    Here is how the template could look like:

    {% set $timeSinceLastPurchase = age(e.purchase) %}
    {% if $timeSinceLastPurchase > 30d and $timeSinceLastPurchase < 60d %}
    {% set $discount = '20%' %}
    {% else %}
    {% set $discount = '40%' %}
    {% endif %}
    Did you like your {{ u.last_purchase_product_name }} ? Get a {{ $discount }} discount on all purchase today!