The Emarsys Scripting Language (ESL) is a scripting language for creating dynamic content in your campaigns, based on predefined rules.
Introduction
ESL is a genuine scripting language. It lets you personalize your content based on external factors, opposed to the standard personalization options, which can populate email content based only on contact database fields.
ESL is specifically for people with a knowledge of HTML.
As a quick reminder, here is the onboarding video about Emarsys Scripting Language:
Where can you use ESL?
You can use ESL snippets for dynamic content creation or further personalization in your omni-channel campaigns:
- Email campaigns (batch and transactional, external event emails: In email you can use ESL in the email body, header, preheader, subject, from name, basically anywhere in the email where personalization could work effectively.
- SMS and Push messages
- Personalizing content with Relational Data
- Web channel campaigns: You can use ESL for personalization only.
Personalization can only be applied for identified visitors. Therefore, ESL does not work for Web Channel campaigns with Recipient source: All visitors and Unidentified visitors.
ESL as being used for templates, it can also handle more complex algorithms. ESL also offers a universal solution for handling personalization, conditional text, and block targeting, and it is simpler and more flexible than other methods.
Why should you use ESL?
Since it is a scripting language, it provides numerous functions for you. First of all, the personalizations you can create with it can be more complex than the standard personalization options in Emarsys. Secondly, you can adjust any rule based on the values of your personalization fields for a specific campaign.
Emarsys accounts come with around 50 standard fields for storing contact data.
For a list of all the available values for the single-choice system fields, click here.
The value for a given ID may vary by language!
Examples for using ESL
You can use ESL in the following cases:
- Custom controls: You can display a button called Become a member to only those contacts who are not members of your loyalty scheme. Although this could be done via simple conditioning or section targeting as well, for those who know HTML, it makes campaign creation a lot easier.
- Counters: You can place a counter into your campaign without having to put a URL into your HTML code.
- Conversions: You can handle transformations from one contact field to another, for example, to display field values provided in kilometers in miles instead.
- Loops: You can create a loop, and do not have to refer to each and every array element separately. As ESL is a scripting language, it can automatically go through the array and use all elements one by one.
All in all, customization of your contacts becomes easy and fully automatic with the Emarsys Scripting Language.
How is Twig compared to ESL?
Twig does share similarities with the Emarsys Scripting Language, but there are huge differences as well. Twig is a language used solely for email templates and uses functions only to substitute variables in the template (e.g. make all first names uppercase).
ESL does not support all Twig tags and filters, so you are advised to use this documentation and not include elements from the Twig documentation referenced above.
Using ESL variables at template level
These ESL variables are created either in the template frame or within the HTML block if the variable needs to be used in a specific block only.
Example A
In this example you can use ESL variables at email template level to check a contact's registration language field and show a text accordingly.
{% if contact.35==1 %} Welcome dear customer
{% elseif contact.35 == 2 %} Willkommen, lieber Kunde!
{% else %} Bienvenido, querido cliente!
{% endif%}
Example B
For a contact's registration language field you can also create ESL variables at email template level with applying placeholder texts, instead of adding the whole ESL snippet in the email body.
Setting up a ctatext
variable at email template level:
<!--
{% if contact.35 == 1 %}
{% set ctatext = 'SHOP NOW' %}
{% elseif contact.35 == 2 %}
{% set ctatext = 'ZUM PRODUKT' %}
{% elseif contact.35 == 3 %}
{% set ctatext = 'ACHETER MAINTENANT' %}
{% elseif contact.35 == 4 %}
{% set ctatext = 'ACQUISTA ORA' %}
{% elseif contact.35 == 8 %}
{% set ctatext = 'COMPRAR AHORA' %}
{% else %}
{% set ctatext = 'SHOP NOW' %}
{% endif %}
-->

Editor-specific details
The Emarsys Scripting Language is supported only by custom HTML campaigns created in the Visual Content Editor. Old template-based campaigns do not support it.
Operators are case sensitive and they should always be in lower case: "and", "or".
Operators supported in ESL
The following operators are available for Twig and supported in any expression in the Emarsys Scripting Language:
-
Comparison operators: Compare values and return true or false state. The operators include:
==
,!=
,<
,>
,>=
,<=
-
Logical operators: Combine multiple boolean expressions or values and provide a single boolean output. The operators include:
and
,or
Commenting in ESL
Commenting out lines works in ESL similarly to Twig.
Commenting out a single line:
{# rds.rds_reformation.prefix_color('1304632BFF','BFF')[0].item_sku_prefix|required#}
Commenting out multiple lines:
{# The following code will not be executed and nothing will be displayed
{% if category.posts %}
This category has posts
{% endif %}
#}
For example, you can use this to test and troubleshoot HTML email campaigns.
Language reference
Terminology
{{ this is an expression }}
{% this is a statement %}
|this is a filter
Variables
You are able to create custom variables in the following way:
String variable
{% set customVariable1 = 'String' %}
Calling a string variable:
{{customVariable1}}
Result: String
String variable with multiple values
{% set customVariable3 = { var1: 'string1'; var2: 'string2' } %}
Calling one of the values of a variable with multiple values:
{{customVariable3.var1}}
Result: string1
Numeric variable
{% set customVariable2 = 4 %}
Calling a numeric variable:
{{customVariable2}}
Result: 4
Variable via API
Let's say that the value for the specific variable is coming from an API request, an external event, for example, {{ event.example }}
. You can use the below variable to get the data from the sample request:
{{ event.product.name }}
{
”product”: {
“name”:”example”
}
}
If you used global object
, make sure you form your code including that.
Codeblock as variable
You can also create a variable containing an entire block of code:
<table cellpadding="0" cellspacing="0" border="0" bgcolor="#ffffff">
<tr>
<td>
{{customBlockVariable}}
</td>
</tr>
</table>
Result:
<table cellpadding="0" cellspacing="0" border="0" bgcolor="#ffffff">
<tr>
<td>
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
This variable is equal to this entire block of code!
</td>
</tr>
</table>
</td>
</tr>
</table>
Tags
if and elseif
An if
statement checks if something evaluates to true. If it does, the block content up to the (mandatory) {% endif %}
is processed. This can be a part of the HTML code.
In this example below, we are looking for system field #3, which is the email address. If the contact has an example.com email address, it is displayed so. Otherwise it is stated that it has a different address.
{% if "@example.com" in contact.3 %}
this is an example.com email address
{% else %}
this is not an example.com email address
{% endif %}
In an if
statement you can also define an empty field {% '' %}
. See example below:
{% if contact.1 !='' %}Dear {{contact.1}}
{% else %}Hallo!{% endif %}
if
example to define if today is someone’ birthday:
{% if contact.4|localized_date('en','Mdd') == 'now'|localized_date('en','Mdd') %}
Happy birthday !
{% endif %}
In the next example we are targeting our customers based on their gender. We can show different products to them, based on the above query.
{% if contact.5 == 1 %}
this is for him
{% else %}
this is for her
{% endif %}
elseif
In an if
statement, the only mandatory pieces are {% if foo == 'bar' %}
and {% endif %}
. else
and elseif
are useful, but optional only.
elseif
is used exclusively for checking another condition after the previous one has failed. If you do not have a second condition to check, you should be using else
. {% elseif %}
is invalid. It should never be used without a preceding if
and the preceding if
must not be already closed with endif
.
Using elseif
for our example where we are targeting our customers based on their gender. We can show different products to them.
{% if contact.5 == 1 %}
this is for him
{% elseif contact.5 == 2 %}
this is for her
{% endif %}
In another example you can use ESL variables with elseif
to define someone's zodiac sign for their horoscope.
Hey {% if contact.4|localized_date('en','Mdd') > 1221 or contact.4|localized_date('en','Mdd') < 120 %}
Capricorn
{% elseif contact.4|localized_date('en','Mdd') > 1122 %}
Sagittarius
{% elseif contact.4|localized_date('en','Mdd') > 1022 %}
Scorpio
{% elseif contact.4|localized_date('en','Mdd') > 922 %}
Libra
{% elseif contact.4|localized_date('en','Mdd') > 822 %}
Virgo
{% elseif contact.4|localized_date('en','Mdd') > 722 %}
Leo
{% elseif contact.4|localized_date('en','Mdd') > 620 %}
Cancer
{% elseif contact.4|localized_date('en','Mdd') > 520 %}
Gemini
{% elseif contact.4|localized_date('en','Mdd') > 419 %}
Taurus
{% elseif contact.4|localized_date('en','Mdd') > 320 %}
Aries
{% elseif contact.4|localized_date('en','Mdd') > 218 %}
Pisces
{% elseif contact.4|localized_date('en','Mdd') > 120 %}
Aquarius
{% endif %}, it's time to celebrate
foreach
The foreach
tag works only on arrays, and is used to loop through each key-value pair in an array.
This example loops through the numbers from 1 to 4, and stops at 3 as the limit is set to the 3rd element. The default and maximum limit is 100.
{% foreach asd in [1, 2, 3, 4] limit 3 %}
{{ asd }}
{% endforeach %}
A more complex foreach example
{
"key_id": "3",
"external_id": "king@north.com",
"data": {
"customer": {
"id": "1111",
"repId": "1111",
"firstName": "John",
"lastName": "Snow",
"email": "king@north.com",
"phone": ""
},
"order": {
"shipping": "UPS",
"shippingPrice": "5",
"tax": "5",
"subtotal": "50",
"total": "60",
"product_details": [
{
"title": "Product1",
"price": "20",
"quantity": "2"
},
{
"title": "Product2",
"price": "5",
"quantity": "1"
},
{
"title": "Product3",
"price": "5",
"quantity": "1"
}
]
}
}
}
{% foreach item in event.order.product_details %}
<tr>
<td>
<table cellpadding="0" cellspacing="0" border="0">
Title:{{item.title}}
Price:${{item.price|number_format('2', '.', ',')}}
Quantity:{{item.quantity}}
Current loop:{{loop.index}}
Is it the first loop?{% if loop.first %}Yes.{% else %}No.{% endif %}
or
Is it the first loop?{% if not loop.first %}No.{% else %}Yes.{% endif %}
Is it the last loop?{% if loop.last %}Yes.{% else %}No.{% endif %}
</table>
</td>
</tr>
{% endforeach %}
Check JSON Source for the values, to understand how they are inserted.
<tr>
<td>
<table cellpadding="0" cellspacing="0" border="0">
Title:Product1
Price:$20.00
Quantity:2
Current loop:0
Is it the first loop? Yes.
or
Is it the first loop? Yes.
Is it the last loop? No.
</table>
</td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" border="0">
Title:Product2
Price:$5.00
Quantity:1
Current loop:1
Is it the first loop? No.
or
Is it the first loop? No.
Is it the last loop? No.
</table>
</td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" border="0">
Title:Product3
Price:$5.00
Quantity:1
Current loop:2
Is it the first loop? No.
or
Is it the first loop? No.
Is it the last loop? Yes.
</table>
</td>
</tr>
Code nesting
ESL allows you to nest codes. For example, if
is your opening condition. For every if
, there must be a closing endif
. It can be nested inside another if
, but both must eventually be closed.
Example
{% if contact.35 == 2 %}
{% if contact.5 == 2 %}
German Language Female
{% else %}
German Language Male
{% endif %}
{% else %}
{% if contact.5 == 2 %}
Default Language Female
{% else %}
Default Language Male
{% endif %}
{% endif %}
Filters
Using multiple filters within one placeholder is allowed. When using nested or multiple filtering, pay attention to the order of the filters, because certain filters need to be in a specific position to work as expected. One example for this is the raw filter.
You can see examples for filters from Twig and specific to ESL here:
With the help of the even/odd Twig filter you can differentiate even and odd values.
{% if 'now' | localized_date('de','yy') is even %}
Now: even years
{% elseif 'now' | localized_date('de','yy') is odd %}
Now: odd years
{% else %}
Now: nothing above
{% endif %}
Result
Now: even years
You can define a split character with the help of the split filter. It divides the string and creates an array from it, which can be used by foreach .
{% foreach item in 'one,two,three'|split(',') %}
{{ item }} <br />
{% endforeach %}
In this example, the split character is , (comma). item loops through all elements from the array and lists them one by one. If you include the HTML <br>
tag, every element will be displayed in a new row:
one
two
three
You can also include a limit argument. There is a difference between working with positive and negative numbers. An example for a positive number is 2, where the first element will be displayed as a standalone string and the remaining elements will become one string.
{% foreach item in "one,two,three,four,five"|split(',',2) %}
{{ item }} <br />
{% endforeach %}
Result
one
two,three,four,five
On the other hand, if the limit is -1, the very last element will not be returned, all the other elements will be displayed.
{% foreach item in "one,two,three,four,five"|split(',',-1) %}
{{ item }} <br />
{% endforeach %}
Result
one
two
three
four
If the limit is zero, one string will be created from all elements in the array.
{% foreach item in "one,two,three,four,five"|split(',',0) %}
{{ item }} <br />
{% endforeach %}
Result
one,two,three,four,five
If you dont define a split character, instead you include an empty string, the array will be split at every character.
{% foreach item in "123"|split('') %}
{{ item }} <br />
{% endforeach %}
Result
1
2
3
If you add a limit to the empty string delimiter, the array will be split at this specific number.
{% foreach item in "112233"|split('',2) %}
{{ item }} <br />
{% endforeach %}
Result
11
22
33
You can cut and display a part of an array or string with the slice filter. If it is an array, foreach is used with it. Its first parameter defines from which element it starts (note that the very first element is signed by 0), and the second parameter defines the number of elements to cut (note that it includes the starting element as well). In the next example, the starting element of an array is 1, so it will start from the second element, and the number of elements is 2, so it will cut 2 elements starting with the second element.
{% foreach item in [1, 2, 3, 4, 5]|slice(1, 2) %}
{{ item }} <br />
{% endforeach %}
Result
2
3
An example for slicing a string is the following:
{{ '12345'|slice(2, 2) }}
Result
34
If the starting number is negative, counting starts from the end of the array or string (e.g. -4 will sign the second element, which is 2 in the above example). If no second parameter is provided, it means including all upcoming elements. Please note that zero cannot be a second parameter.
{{ '12345'|slice(-4) }}
Result
2345
The values of string template placeholders can be defined with the format filter. These placeholders are %s (string) and %d (number). In the following example, the value 10 is set for the message number. The format parameter is added with this variable as a parameter. The result is having this number in the string.
{{ "You have %d messages."|format("10") }}
Result
You have 10 messages.
If there is no defined value, a default (fallback value) can be displayed with using the default filter. In the following example, contact field 7 has no provided value, it is replaced by 5 defined in the brackets.
{{ 30 + contact.7|default(5) }}
Result
35
You can encode a URL or part of a URL which contains invalid URL characters. This means that the invalid URL characters will be replaced by their valid HTML character combinations. An example for a URL particle containing a * (asterisk):
{{ "seg*ment"|url_encode }}
Result
seg%2Ament
Another example is having one or more spaces in the URL:
{{ "one space"|url_encode }}
Result
one%20space
In the next example, a key-value pair is converted into valid URL parameters:
{{ {'key': 'value', 'foo': 'bar'}|url_encode }}
Result
key=value&foo=bar
The characters valid in HTML can be escaped with the escape filter. An example is when the First name field of the contacts is checked. If any contains an HTML character (e.g. <), it will be treated as a simple string.
{{ "< '"|escape }}
Please note that escape can be abbreviated to e.
{{ "< '"|e }}
It marks an escaped character as being a normal character. This is useful if you dont want some characters to be escaped again.
{{ "< '"|raw }}
Result
<
Without using the filter, the output of this expression would be: &lt;
.
If nested filters are used, the raw filter needs to be the last to work properly.
{{ mysubject_line | required | raw }}
The abs filter returns the absolute value. Note that it does not work for strings, only for integers.
{{ -6|abs }}
Result
6
It capitalizes a string.
{{ 'hello sunshine!'|capitalize }}
Result
Hello sunshine!
The first filter returns either the first element of an array or the first value of a key-value pair or the first character of a string.
{{ [1, 2, 3, 4]|first }}
Result
1
{{ { a: 1, b: 2, c: 3, d: 4 }|first }}
Result
1
{{ '1234'|first }}
Result
1
It concatenates the elements of an array to form a single string.
{{ [1, 2, 3]|join }}
Result
123
The join parameter defines the separator between the elements along which the concatenation happens.
{{ [1, 2, 3]|join('|') }}
Result
1|2|3
With the help of the json_encode
filter you can include characters in the json snippets in your ESL content.
{{contact.1234 | json_encode}}
Result
If the contact field contains characters that would break a json format, the json_encode
filter solves this problem.
This returns the keys from an object.
{% foreach key in { a: 1, b: 2, c: 3, d: 4 }|keys %}
{{key}} <br />
{% endforeach %}
Result
a
b
c
d
A separator is also possible to provide.
{% foreach key in { a: 1, b: 2, c: 3, d: 4 }|keys %}
{{key}}, <br />
{% endforeach %}
Result
a,
b,
c,
d,
The last filter returns either the last element of an array or the last value of a key-value pair or the last character of a string.
{{ [1, 2, 3, 4]|last }}
Result
4
{{ { a: 1, b: 2, c: 3, d: 4 }|last }}
Result
4
{{ '1234'|last }}
Result
4
It displays the number of array elements or the string character number.
{{ [1, 2, 3, 4]|length }}
Result
4
{{ '1234'|length }}
Result
4
The length filter can have a parameter and it can be included within if tags. In the following example, the length parameter 10 defines that if the number of users is more than 10, the provided message is shown.
{% if users|length > 10 %}
The number of users is more than 10.
{% endif %}
It converts every string character to lowercase.
{{ 'WELCOME'|lower }}
Result
welcome
It converts every string character to uppercase.
{{ 'welcome'|upper }}
Result
WELCOME
It creates a union from two arrays or objects.
{% foreach item in [1, 2]|merge(['apple', 'orange']) %}
{{item}} <br />
{% endforeach %}
Result
1
2
apple
orange
Please note here that if a value is provided for the same key again in the merge parameter, the original value is overwritten by this new one.
{% foreach item in { 'apple': 'fruit', 'potato': 'unknown' }|merge({ 'potato': 'vegetable'}) %}
{{item}} <br />
{% endforeach %}
Result
fruit
vegetable
It replaces the \ newline characters in the code to HTML line breaks (<br />) so these become new lines in HTML.
{{ "I like Emarsys Scripting Language.nYou will like it too."|nl2br }}
Result
I like Emarsys Scripting Language.<br />
You will like it too.
This formats decimal numbers. The first parameter of number_format determines how many numbers the decimal part contains. In the following example, it is 2, so 2 decimals will be displayed from the 3. The second parameter defines the decimal separator, which is , (comma) in this case. The third parameter stands for the thousands separator, which is . (period) here.
{{ 9800.333|number_format(2, ',', '.') }}
Result
9.800,33
Please note that if no parameter if given for the number_format filter, the decimals are not displayed at all and the thousands separator is , (comma).
{{ 2005.35|number_format }}
Result
2,005
The replace filter replaces string parts.
{{ "I like this and that."|replace({'this': 'chocolate', 'that': 'candy'}) }}
Result
I like chocolate and candy.
Please note that if the very same string part is provided as a value first, then a key in the parameter, it will become overwritten. In this example, you can see that "cats" changes to "dogs" first, then both "dogs" change to "birds".
{{ "I like cats and dogs."|replace({'cats': 'dogs', 'dogs': 'birds'}) }}
Result
I like birds and birds.
The replace
filter cannot handle ESL expressions without a result (the content before the pipeline character in the example). Message including these are not sent out and they increase the "No content" metric.
This filter reverses the order of array or string elements.
{% foreach item in [1, 2]|reverse %}
{{item}} <br />
{% endforeach %}
Result
2
1
{{ '1234'|reverse }}
Result
4321
The rounding conditions can be defined with the help of the round filter. Its first parameter defines the number of decimals, which is 1 in our example. As for the second parameter, two options exist. The first is floor, which rounds the numbers down regardless of the common rounding rules.
{{ 42.58|round(1, 'floor') }}
Result
42.5
The second is ceil, which rounds the numbers up in any case.
{{ 42.54|round(1, 'ceil') }}
Result
42.6
Please note that the default is having no decimals and using common rounding (rounding the value up from decimal 5 and rounding it down under it).
{{ 42.52|round }}
Result
43
It converts the string to titlecased (words will start with uppercase letters).
{{ 'my first car'|title }}
Result
My First Car
Whitespaces can be cut with using this filter.
{{ ' I like Emarsys Scripting Language. '|trim }}
Result
I like Emarsys Scripting Language.
Any other character can be defined to be cut if you include it as a parameter.
{{ ' I like Emarsys Scripting Language.'|trim('.') }}
Result
I like Emarsys Scripting Language
Please note that date formatting follows the ICU convention and is case sensitive. For more information see the ICU Project details.
It is a date formatting function. It displays the date of the specified country and it can also translate this date (it can handle the PHP language translations). Please note that it displays only the date, even if the time is also provided. It can be overwritten, so the predefined date format becomes displayed (e.g. [{locale: en, format: yyyy mm}] means that the format YYYY MM will become visible for English people.) The localized_date filter can have 4 parameters. These are displayed in the order defined by their language settings, and contain the following:
- full: year, month, date, day of the week
- long: year, month, date
- medium: year, abbreviated version of month, date
- short: yyyy/MM/dd, the separator can be different.
The month can be displayed in the following ways:
Syntax | Displayed format |
---|---|
M | 9 |
MM | 09 |
MMM | Sep |
MMMM | September |
MMMMM | S |
For further syntax of the date/time format, see here.
The input string can have the following formats:
- 'yyyy-MM-dd'
- 'now'
- '+[number] hours'
- '+[number] days'
- '-[number] hours'
- '-[number] days'
In this example, a full version is provided on the basis of the stored country+language value in contact field 12 for the specified date (e.g. it will become January 1 Friday, 1999).
{{ '1999-01-01'|localized_date(contact.12, 'full') }}
Ensure that all the fields have valid and relevant data values. The localized_date
filter cannot format empty fields which may lead to an error in the Contact preview.
You can also use a database field placeholder to populate the date dynamically. If you do this, make sure to leave out the single quotes around the field placeholder, as the date value will be returned in quotes. In this example we use value of contact field 12345 to provide the date.
{{ contact.12345|localized_date(contact.12, 'full') }}
As another example, you can have "x days from now" date, too.
{{ '+7 days'|localized_date('de','dd.MM.yyyy') }}
It is a time formatting function. It displays the time of the specified country and it can also translate this time (it can handle the PHP language translations). Please note that it displays only the time, even if the date is also provided. Here you can find what is displayed exactly if you provide parameters.
- full: HH:MM:SS (AM/PM)
- long: HH:MM:SS (AM/PM)
- medium: HH:MM:SS (AM/PM)
- short: HH:MM (AM/PM)
{{ '1999-01-01 13:13:13'|localized_time(contact.12, 'medium') }},
Please note here as well that the input string has a compulsory form (HH:MM:SS for time). In this example, a time is generated from a datetime with the localized_time filter on the basis of the stored country+language value in contact field 12 (13:13:13).
It is a date and time formatting function. It displays the date and time of the specified country and it can also translate this (it can handle the PHP language translations). Regarding its parameters, it puts the values of the above two filters together (e.g. if its parameter is full, the date and time will be displayed as full as well).
{{ '1999-01-01 13:13:13'|localized_datetime(contact.12, 'long') }},
A possible result of the above example is 1999 01 January, 13:13:13.
If the evaluation of the ESL expression does not have a result and this filter is added, the message will not be sent. It will be displayed on the Analysis page in Emarsys though. The default invalid values for this filter are "" (empty string), and the value null.
{{ contact.2|required(['-']) }}
The parameter of this filter (the string - in this case) overwrites the default invalid empty string, so this string will become the new invalid one. You can still include the empty string later as well as it is possible to define more than one invalid values. For example, ([x], [y], [ ]) means that there are 3 invalid elements, the last one is the empty value.
Emails prevented from being sent with the required
filter increase the "No content" metric. For more information on these metrics, see Email response metrics explained.
Personalization
- Do not use the old personalization variable
$
and/or%
together with the new variable{{ }}
in ESL expressions because your email might be cancelled. For more information, please contact Emarsys Support. - Placeholders, variables and scripts used in other platforms (for example, SFMC, Shopify, Magento, etc.) will break ESL and your mail will be cancelled. To prevent such issues, you must remove ALL of these instances from your campaigns.
{{ contact. }}
Contact data is pulled from the Emarsys database. Here, you can find the list of the possible email personalization placeholders:
Currently, contact personalization tokens are only available in English.
Single-choice fields contact fields have both an value ID and a text value.
By single-choice fields you can check for the value of a field with an IF statement (ID used).
To reference the value ID of a single-choice field, use {{contact.XX}}
, if you want to reference the text value, use {{contact.label.XX}}
.
Warning: The value for a given ID can vary by language.
General Information
Parameter |
Description |
---|---|
{{ contact.1 }} |
First Name |
{{ contact.2 }} |
Last Name |
{{ contact.3 }} |
Email |
{{ contact.9 }} |
Title |
{{ contact.26 }} |
Preferred email format |
{{ contact.46 }} |
Salutation |
Personal Information
Parameter |
Description |
---|---|
{{ contact.4 }} |
Date of Birth |
{{ contact.5 }} |
Gender |
{{ contact.6 }} |
Marital Status |
{{ contact.7 }} |
Children |
{{ contact.8 }} |
Education |
{{ contact.10 }} |
Address |
{{ contact.11 }} |
City |
{{ contact.12 }} |
State |
{{ contact.13 }} |
ZIP Code |
{{ contact.14 }} |
Country |
{{ contact.15 }} |
Phone |
{{ contact.16 }} |
Fax |
{{ contact.37 }} |
Mobile |
{{ contact.38 }} |
First Name of Partner |
{{ contact.39 }} |
Birthdate of Partner |
{{ contact.40 }} |
Anniversary |
Company Information
Parameter |
Description |
---|---|
{{ contact.17 }} |
Job Position |
{{ contact.18 }} |
Company |
{{ contact.19 }} |
Department |
{{ contact.20 }} |
Industry |
{{ contact.21 }} |
Phone (office) |
{{ contact.22 }} |
Fax (office) |
{{ contact.23 }} |
Number of Employees |
{{ contact.24 }} |
Annual Revenue (in 000 EUR) |
{{ contact.25 }} |
URL |
{{ contact.41 }} |
Company Address |
{{ contact.42 }} |
ZIP code (office) |
{{ contact.43 }} |
City (office) |
{{ contact.44 }} |
State (office) |
{{ contact.45 }} |
Country (office) |
Other Information
Parameter |
Description |
---|---|
{{ contact.27 }} |
Avg. length of visit (minutes) |
{{ contact.28 }} |
Page views per day |
{{ contact.29 }} |
Days since last email sent |
{{ contact.31 }} |
Opt-In |
{{ contact.32 }} |
User status |
{{ contact.33 }} |
Contact source |
{{ contact.34 }} |
Contact form |
{{ contact.35 }} |
Registration Language |
{{ contact.36 }} |
Newsletter |
{{ contact.47 }} |
Email valid |
{{ contact.48 }} |
Date of first registration |
{{ rds. }}
The data here comes from the Relational Data Service (RDS). Product-recommendation is a view which relates RDS data from different tables. Its parameter is a field on the basis of which you identify the contact (it is the email address here).
Note:
- An RDS parameter can only be a constant (a field or a string), it must remain the same.
- An RDS expression is valid only with a defined index and field name in it.
Example with foreach
{% foreach product in rds.product_recommendation(contact.3) limit 4 %}
{{ product.title }} <br />
{% endforeach %}
Result
T-shirt
Shirt
Trousers
Jeans
Example without applying foreach
{{ rds.product_recommendation(contact.3)[0].title }}
{{ rds.product_recommendation(contact.3)[0].price }}
Result
T-shirt
59.99
{{ voucher. }}
It is possible to integrate vouchers into your personalization using ESL.
Voucher Management
In this scenario, a contact field is created, so the ESL syntax is the same as that of any other contact field. Here it is with the upper
formatting.
{{ voucher.21345 | upper }}
Omnichannel vouchers
For these vouchers, no contact field is created, so the voucherID
needs to be used instead. To get a voucherID
, create a Voucher Personalization Token and then check the ESL in that token.
You can refer to the voucher in the following manner:
{{ voucher.22738 | upper }}
Link Tracking
You can use ESL for link tracking purposes, too, by embedding the link in an ESL expression.
{% if event.payload.example == 'EXAMPLE' %} https://example.com {% endif %}
Please make sure that in the text version of the email, the link and the ESL expression is separated by a space or a line break. Otherwise, link tracking will not work and the campaign may become invalid.
Useful Tools
Twig testing is possible with TwigFiddle, where the result of any twig can be checked. Note here that we have some unique tags, parameters and filters, for which you cannot use this testing method easily. If you wish to test code with the foreach tag, we suggest that you replace it with a simple for
tag, so that you can check your code. Other elements which cannot be tested here are the limit
parameter and the .rds-related codes. From filters, the four unique ones are:
- localized_date
- localized_time
- localized_datetime
- required
For editing your script, Atom and Notepad++ can be useful with a twig-specific add-on.
ESL Validation
When adding an ESL snippet to your content, the ESL script is validated on a campaign level. Unless the script is valid, you cannot save the token. While adding the snippet, the editor automatically checks if your code is valid or not:

Samples
External Data for Transactional Emails
For an example, see Triggering external data for transactional emails.
Common ESL mistakes
Mixing old $ or %% personalization within ESL |
These variables do
not mix. They are handled by two different services and Emarsys Marketing Platform will
either print the placeholders in the email or cancel the email. On the same note, placeholders, variables, and scripting used in other platforms (SFMC, Shopify, Magento, etc.) will break ESL and cancel the email. You must remove each and every instance. |
Using a single = instead of ==
|
In general, you will only every use = for setting a variable and == to check a value or string. |
Skipping a reference to the "global" object in JSON | If it some data is inside the “global” object, you
must reference it in ESL.{{event.ordertotal}} instead of {{event.global.ordertotal}} is incorrect if inside of the "global" object |
ESL/Twig is case sensitive | {{event.global.orderTotal}} is a different variable than {{event.global.ordertotal}} |
With the exception of variables and values, you should exclusively use lowercase | Incorrect: {% if contact.1111 == 'Family' AND contact.2222 == 'Other' %} Incorrect: {% IF contact.1111 == 'Family' and contact.2222 == 'Other' %} Incorrect: {% if CONTACT.1111 == 'Family' and CONTACT.2222 == 'Other' %} Correct: {% if contact.1111 == 'Family' and contact.2222 == 'Other' %}
|
Inconsistent JSON structure | If you reference JSON via ESL without checking for the existence of it first, that variable must ALWAYS exist regardless of whether it is needed. If it is not in the JSON, that email will not be sent due to missing content. |
Usually indicated by a large number of emails cancelled for "No Content" | |
Calling an undefined variable | Just because another system may reference order by {{orders.lastorder.price}} , does not mean ESL will. With the exception of custom variables and variables inside of an array that you are looping, you are limited to using variables that start with the following:{{contact...}} {{event...}}
|
Adding unnecessary spaces, extra words, or omitting spaces in statements | {% end foreach %} instead of {% endforeach %} {% else if %} instead of {% elseif %} {% end if %} instead of {% endif %} {% endelseif %} instead of {% endif %} {%if contact.1111 == 'Family'%} instead of {% if contact.1111 == 'Family' %} This will not necessarily break functionality, but it will break syntax highlighters and can cause confusion. |
Differences between if , elseif , and else |
In an if statement, the only mandatory pieces are {% if foo == 'bar' %} and {% endif %} . elseif and else are always optional but useful.if is your opening condition. For every if , there must be a closing endif . It can be nested inside another if but both must eventually be closed.elseif is used exclusively for checking another condition after the previous one has failed. If you do not have a second condition to check, you should be using else . {% elseif %} is invalid. It should never be used without a preceding if and the preceding if must not be already closed with endif .else is your fallback. If none of the preceding conditions are true, else will be used instead. It is optional. If you do not want to set a fallback, you do not need to use else . The natural behavior of if is to not use a fallback. |
Forgetting to use quotations when referencing a string | Strings are exact. In order to use them or check them, you must use single or double quotations surrounding them. |
Using {{ and {% interchangeably | Incorrect: {{ endif }} Correct: {% endif %} Incorrect: {% contact.1111 %} Correct: {{contact.1111}} {{...}} should always be used to print variables from JSON, RDS, contact table, etc.{%...%} should always be used for statements or logic namely: {% if... {% foreach... {% set... When referencing a variable inside of a statement, you do not need {{…}} since you are not printing the variableCorrect: {% if contact.3 %} Incorrect: {% if {{contact.3}} %}
|
Unsupported ESL expressions
Unsupported syntaxes
Do not use the following syntaxes, since they are not supported:
#DATE#
#TIME#
#L_YEAR#
#S_YEAR#
#S_MONTH#
#L_MONTH#
#L_DAY#
#S_DAY#
#HOUR#
#MINUTE#
#SECOND#
#UNIX_TIMESTAMP#
Unsupported filters
Do not use the following filters, since they are not supported:
required_length
This filter may break a the Visual Link tracking page with the following error message:
The email cannot be previewed here. Please open the email content editor and preview the content there.