The Emarsys Scripting Language (ESL) is a scripting language for creating dynamic content in your email campaigns, based on predefined rules. As opposed to our standard personalization options, which can populate email content based only on contact database fields, ESL lets you personalize content based on external factors, too.
It is written specifically for people with a knowledge of HTML.
Contents
Introduction
How does this compare to Twig?
Twig does share similarities with the Emarsys Scripting Language, but there are huge differences as well. While 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), the Emarsys Scripting Language is a genuine scripting language. As well as being used for templates, it can also handle more complex algorithms.
It also offers a universal solution for handling personalization, conditional text, and block targeting, and is simpler and more flexible than our current methods.
Please note that 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.
Why should I 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 options in Emarsys. Secondly, you can adjust any rule based on the values of your personalization fields for a specific email campaign.
Examples
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 email creation a lot easier.
- Counters - You can place a counter into your email 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.
Editor-specific details
The Emarsys Scripting Language is supported only by custom HTML email campaigns and by email campaigns created in the new Visual Content Editor. Old template-based campaigns do not support it.
Note that operators are case sensitive and they should always be in lower case: "and", "or".
Language Reference
Terminology
{{ this is an expression }} {% this is a statement %} |this is a filter
Tags
if
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:
{% if "@example.com" in contact.3 %} this is an example.com email address {% else %} this is not an example.com email address {% endif %}
In this example above, 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 contact.5 == 1 %} this is for him {% else %} this is for her {% endif %}
In this example we are targeting our customers based on their gender. We can show different products to them, based on the above query.
foreach
The foreach tag works only on arrays, and is used to loop through each key-value pair in an array.
{% foreach asd in [1, 2, 3, 4] limit 3 %} {{ asd }} {% endforeach %}
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.
Filters
Contents
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 last 2 elements will become one string and the remaining elements will stay separately.
{% 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 can be displayed with using the default filter. In the following example, contact field 7 has no provided value.
{{ 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;
.
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
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' Script 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.
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 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') }}
You can also use a database field placeholder to populate the date dynamically. In 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 does not have a result and this filter is added, the email campaign 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.
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:
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. }}
{% foreach product in rds.product_recommendation(contact.3) %} {{ product.name }} <br /> {% endforeach %}
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).
Result
t-shirt trouser
Please note here that the parameter can only be a constant (a field or a string), it must remain the same.
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.
Samples
External Data for Transactional Emails
If you want to create transactional emails in Visual Content Editor, you need to have the following:
- a JSON to trigger the API
- an HTML snippet to personalize with the data
JSON
{ "key_id": "3", "external_id": "test@example.com", "data": { "orderId": 1234, "orderItems": [ { "productCode": 12, "imageUrl": "http://domain.com/12.png" }, { "productCode": 13, "imageUrl": "http://domain.com/13.png" } ] } }
With the above JSON example, order 1234, its related products 12 and 13 and their images will be available for the email campaign. The data displayed in the email itself will be gathered from here. You can include any number of items.
You have the possibility to test your JSON on the API Demo API Demo page. You need to include the data part of your JSON in the text field of data and define the remaining parameters.
You can check how the Triggering an External Event API endpoint works here.
HTML Snippet
To create your HTML code, you must reference your external data with this syntax:
{{ event.variableName }}
An example for the HTML code is the following:
<h1>Order id: {{event.orderId}}</h1> <ul> {% foreach order in event.orderItems limit 5 %} <li> {{order.productCode}} <img src="{{order.imageUrl}}" /> </li> {% endforeach %} </ul>
With the help of the above HTML snippet, a header containing the order ID is displayed in the transactional email. The code contains the foreach tag, which loops through all product codes and image URLs provided in the JSON. The limit 5
restrictions means that the email will list the first five product codes and image URLs.
Please note that the default and maximum limit is 100.
The very last step is to finalize your campaign and activate it in the Automation Center or use it as a simple external event triggered message.