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.


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 targeted 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: {{ t.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 t.levels_done doesn't exist the dynamic content evaluates to:

You've already beaten those levels:


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 !


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 fidelity points he has ?

This is a job for templates.

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


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 90-days subscription
  • users subscribed to the newsletter have a 75-days subscription
  • users younger than 25 and not premium have a 60-days subscription
  • all other have a 50-days subscription

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:


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

Referencing trigger event data

In the context of a trigger campaign, using data of the trigger event is also possible with 3 specific functions:

  • triggerEventLabel(): returns the label of the trigger event
  • triggerEventTags(): returns the tag collection attached to the trigger event. It can be used the same way as the tag collections, so the only way to use it is by using filters like count, first, last or join
  • triggerEventAttr(attribute_key): returns the attribute value corresponding to the given key

Here are a few examples of how you can use them.

Using the product name the user added to its cart:

Your {{ triggerEventAttr('product_name') }} is waiting for you !

Or if the products are listed in the tag collection:

{{ triggerEventTags() | count }} items are still waiting for you in your cart !

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.


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.

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


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


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:


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


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!