Using the SendinBlue template language to create email templates [NEW]

When coding HTML email templates for use on the SendinBlue platform, you may use our template language to add advanced functionality to your Transactional or Automation emails.

The template language provides a standardized, common structure for more efficient templates and more powerful design options.

Before getting started

Good to know: Build and save your template in the My SMTP Templates area. Templates using this template language can be triggered to send via SendinBlue SMTP API v3 when doing a call to v3/smtp/email while providing the templateid or via the SMTP relay while providing the header X-SIB-API with the correct parameters. They are not currently compatible with the SendinBlue Automation platform or Campaigns platform for sending bulk emails, however, this functionality will be available in the future.

If you have not used our Transactional (SMTP) platform before, you may need to request its activation. To check, log in to your account and visit this page. If you do not see a prompt asking you to activate your SMTP account and your quota is higher than 0, you’re ready to start sending.


The template language makes use of tags to control the logic of the template, and variables and filters which get replaced with values when the template is triggered to be sent.

There are two kinds of delimiters available: {% ... %} and {{ ... }}. The first one is used to execute statement tags such as for-loops. The latter “prints” or outputs the result of a variable or filter to the template.

Good to know: The template language may be applied to modify several aspects of your email including the subject line, preheader, within HTML <body> tags, and the recipient “to” field. HTML is automatically escaped by default inside templates, but this behavior may be changed using the autoescape tag described in detail below.


The most common tags available to use in templates are for, if and autoescape. Guidelines and examples for each are included below. 


As we’ll explore later in this guide, for is quite powerful when combined with your transactional parameters. It enables you to easily do complex work, such as inserting dynamic lists of products.

The tag {% for %} for enables you to loop, or iterate, over each item in a sequence. This is particularly helpful when the number of items in the sequence is unknown when creating a template.

Here are several common ways to use for:

for, in

display a list

   {% for user in params.USERS %}
       <li>{{ user.username }}</li>
   {% endfor %}


render a replacement block if your sequence is empty

   {% for user in params.USERS %}
       <li>{{ user.username }}</li>
   {% else %}
       <li><em>no user found</em></li>
   {% endfor %}

Would display “no user found”.


reverses the print order of items in your sequence

{% for country in params.countries reversed %} {% end for %}

Would display a list of countries, but in reverse order compared to the original list.


Loop variables

Inside of a for -loop block you can also access these special variables:


Always set to an integer representing the number of times the loop has been entered. This is one-indexed, so the first time through the loop, forloop.Counter will be set to 1.


The current iteration of the loop. (0 indexed)


Always set to an integer representing the number of remaining items in the loop. The first time through the loop, forloop.Revcounter will be set to the total number of items in the sequence you’re traversing. The last time through the loop, forloop.revcounter will be set to 1.


The number of iterations from the end of the loop (0 indexed)


Boolean value set to True if this is the first time through the loop. This is convenient for special-casing the first item in a list.


Boolean value set to True if this is the last time through the loop. A common use for this is to put separating characters (like , or |) in a list of items.


References the forloop object for the parent loop, in case of nested loops. Here’s an example:

{% for country in countries %}
{% for city in country.city_list %}
<td>Country #{{ forloop.Parentloop.counter }}</td>
<td>City #{{ forloop.Counter }}</td>
<td>{{ city }}</td>
{% endfor %}
{% endfor %}


Good to know: Loop variable names are case sensitive.

e.g. to assign a numeric value (counting from 1) to each item printed from your iteration, use {{ forloop.Counter }}:

{% for user in users %}
   {{ forloop.Counter }} - {{ user.username }}
{% endfor %}


The powerful logic offered by if enables you to add or remove entire content blocks within your template (or modify content within a block). This effectively extends the usefulness of a single template for multiple scenarios.

The tag {% if %} enables you to test if an expression is true (or if an array is empty), and modify the email content based on the result. You can also check for false values, multiple conditions, and multiple branches.

Here are several common ways to use if:


Checks if a value is true or if an array is not empty

{% if contact.SUBSCRIBED %}
<b>Thank you for your support!</b>
{% endif %}


{% if params.users %}
{% for user in params.users %}
<li>{{ user.username }}</li>
{% endfor %}
{% endif %}


check if an expression is true

{% if coupon == “WELCOME” %}
<p>Welcome to our list! Here’s your first coupon: WELCOME25</p>
{% endif %} 

if, in

check if a value (substring) exists within a string or if a variable exists within array

{% if “" in "" %}
This appears since "" is a substring of ""
{% endif %}


{% if "Piano" in params.types %}
Be careful! There are heavy items in your order. You must be present to receive this delivery.
{% endif %}


check for values that are false

{% if not user.subscribed %}
   <p>You are not subscribed to our secret sale alerts. Sign up here.</p>
{% endif %}

and / or

evaluate multiple conditions

{% if temperature > 10 and temperature < 55 %}
   <p>Brr. It’s cold! Here’s a coupon for 20% off of any hot beverage, today only.</p>
{% endif %}


{% if contact.LANG == “FR” and contact.COUNTRY == “Canada” %}
{% endif %}


{% if contact.COUNTRY == “United States” or contact.COUNTRY == “Canada” %}
{% endif %}

elseif, else

evaluate multiple branches

{% if event.paid %}
   Your spot is reserved. Thanks for submitting your payment.
{% elseif event.registered %}
   Your spot is reserved but will be released without payment by January 2.
{% else %}
   There’s still time to sign up! Click here.
{% endif %}



Good to know: By default SendinBlue escapes the HTML (and JS) content of all variables. This means any HTML passed within a variable will be escaped and printed as text. For example, if your variable content is <h1>My title</h1>, then your template would output exactly this text within your email: <h1>My title</h1> rather than outputting the phrase “My title” formatted as an H1 title.

The autoescape tag controls the current auto-escaping behavior. Since SendinBlue templates autoescape HTML by default, you will typically need to apply this tag to disable auto-escaping within a particular block. The block should be closed with an endautoescape ending tag.

We’ll review a common example when disabling autoescape may be useful. Let’s say that you pass products in a variable, but each product requires unique formatting.

If autoescape is used like this:

{% autoescape off %}{{ params.my_html }}{% endautoescape %}

Then, auto-escaping will be disabled for the variable {{ params.my_html }}. Now, if your variable content is <h1>My title</h1>, it will display as an H1 title. 

The only exceptions are variables that are already marked as safe from escaping, either by the code that populated the variable, or because it has had the safe filter applied.


There are three types of variables available and each is described below.

1. Predefined variables  

These are available for all SendinBlue users:

{{ mirror }}

inserts a link to view your email as a web page

This variable is not mandatory.

Typically placed at the top of an email and formatted as a link like this:

<a href={{ mirror }}>Click here to view this message in your browser.</a>

{{ unsubscribe }}

inserts a link to unsubscribe from your emails

Any non-transactional emails sent via SendinBlue should include an unsubscribe link.

Typically placed in the footer and formatted as a link like this:

<a href={{ unsubscribe }}>Click here to unsubscribe.</a>


2. Contact attributes   

This variable type contains two elements and is structured as:

{{ contact.attributeNAME }}

Attribute = your SendinBlue contact attribute name as it appears on your Contacts page in SendinBlue. Attributes are always in CAPS and never contain spaces.

e.g., to insert a contact’s phone number where the attribute is named PHONE, use: {{ contact.PHONE }}

If the contact attribute name contains a dash -, such as CELL-PHONE, format your variable like this instead: {{ contact|key:"ATTRIBUTE" }}.  

e.g. {{ contact|key:"CELL-PHONE" }}

3. Transactional parameters

Transactional parameter variables also contain two elements and are structured as:

{{ params.parameterNAME }}

parameterNAME = your transactional parameter name, formatted and cased exactly as you pass it via the API.

e.g., to insert a list of products where the parameter is named PRODUCTS, use: {{ params.PRODUCTS }}.

Now, let’s consider a common use case: generating a list of products purchased by a customer. Since the number of products purchased varies per customer, we can combine a for-loop with your PRODUCTS transactional parameter, like this example:  

{% for product in params.products %}

   <li><strong>{{ product.NAME }}</strong> - {{ product.PRICE }}</li>
{% endfor %}

This would output an unordered, bulleted list of products including the product name and price, like this and each recipient’s list may include a unique number of products:  

  • Product 1 - $10
  • Product 2 - $10
  • Product 3 - $10

Good to know: We recommend avoiding the use of hyphens or any special characters in your variable/parameter names. If you have no other choice, you can use {{ params|key:”parameter-NAME” }}. You may nest the filter multiple times, such as in {{ params|key:”my-product”|key:”my-product-name” }}.


When using an array, you may access all individual elements. Specify their ID like this: params.parameterARRAY.ID.parameterNAME. For example: {{ params.PRODUCTS.1.NAME }}.


Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (|) and may have optional arguments in parentheses. Multiple filters can be chained. The output of one filter is applied to the next.

Most commonly used filters

Modifying and formatting word case


capitalizes a value

The first character will be uppercase, all others lowercase.

{{ contact.NAME|capfirst }}

john doe will output John doe.


returns a titlecased version of the value

Words will start with uppercase letters, all remaining characters are lowercase.

{{ contact.NAME|title }}

john doe will output John Doe.


converts a value to uppercase

{{ contact.NAME|upper }}

john doe will output JOHN DOE.


converts a value to lowercase

{{ contact.NAME|lower }}

JOHN DOE will output john doe.


Truncates a string if it is longer than the specified number of characters. Truncated strings will end with a translatable ellipsis sequence (…).

{{ value|truncatechars:9 }}

If value = congratulations, it would output as congratul…

Formatting numbers


rounds a number to a given precision using the common logic 

{{ 42.55|floatformat:0 }}

Will output as 43

{{ 42.55|floatformat:1 }}

Will output as 42.5

Formatting dates


Converts your date format (passed as a string) to a standard date format that may be used with other filters

Pass your current date format as an argument: format the exact date/time of how Monday January 2 15:04:05 -0700 MST 2006 would be displayed if it were the value.

If your string is formatted using RFC3339 you may use the dedicated parser time_parse_rfc3339 without an argument.

{{ params.my_date|time_parse:"15:04 02/01/2006” }}

{ params.my_date|time_parse:"Monday 02 January 2006” }}

Note: Only English words for day and month are recognized.


Prints the date in the specified format
To define your own format, write down what the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would look like formatted your way.

Please note that you must pass a date to this filter.

You’ll get a date from a string using the time_parse filter.

{{ "14:01 01/06/2018"|time_parse:"15:04 02/01/2006"|date:"Mon Jan 2 15:04:05 2006"}}

Will output as:

Fri Jun 1 14:01:00 2018

Note: Only English words for day and month are recognized.


Please note:

  • Day and month abbreviations and full names are supported in English only
  • If a value is not passed, it’s considered to be 0
  • The default timezone (if not passed) is UTC
  • You may use the am/pm keywords
  • The most common ways to define the timezone are: the dedicated keywords (MST, CET, UTC, Asian/Kolkata etc.), or as numeric, +0100 or -0100, to UTC.

Other filters


returns the first "element" of a sequence, a mapping, or a string

{{ [1, 2, 3, 4]|first }}

Will output as 1


returns a string which is the concatenation of the items of a sequence

The separator between elements is an empty string by default. You may define it with the optional first parameter.

{{ [1, 2, 3]|join }}

Will output as 123  

{{ [1, 2, 3]|join('|') }}

Will output as 1|2|3


returns the last "element" of a sequence, a mapping, or a string

{{ [1, 2, 3, 4]|last }}

Will output as 4


returns the number of items of a sequence or mapping, or the length of a string

{% if users|length > 10 %}
{% endif %}


extracts a slice of a sequence, a mapping, or a string

{% for i in [1, 2, 3, 4, 5]|slice(1, 2) %}
{% endfor %}

Will iterate over 2 and 3

{{ '12345'|slice(1, 2) }}
Will output as 23

Additional documentation resources

The template language used by SendinBlue is based on the Django Template Language.

This guide covers the most relevant and frequently used template language structure and elements. Below is an exhaustive list of all supported tags and filters. Reference this Django documentation to view more details.

All supported tags

autoescape, comment, cycle, filter, firstof, for, if, ifchanged, now, set, spaceless, templatetag, with

All supported filters

escape, safe, escapejs, add, addslashes, capfirst, center, cut, date, default, default_if_none, divisibleby, first, floatformat, get_digit, iriencode, join, last, length, length_is, linebreaks, linebreaksbr, linenumbers, ljust, lower, make_list, phone2numeric, pluralize, random, removetags, rjust, slice, stringformat, striptags, time, title, truncatechars, truncatechars_html, truncatewords, truncatewords_html, upper, urlencode, urlize, urlizetrunc, wordcount, wordwrap, yesno

Please note: If you identify a desirable element that is not yet supported, please reach out to our customer care team at to request it.