How Can We Help?

You are here:

Advanced Email Template Syntax

Our Email Template system is built on a variant of the Liquid syntax using {{ }} as the start and end of a syntax element. Using the Liquid Syntax can result in errors and you will receive an error prompt if we cannot parse your Liquid Syntax.

Campaign Deputy Liquid Fields

For Emails sent via the Message Center as a Campaign, the below template variables are available.

  • Name
    • {{name.first}} The first name of the donor
    • {{name.last}} – The last name of the donor if available
    • {{name.first | empty "Friend" }} First name if available, Friend if not. Spaces in a First Name are considered as blanks for empty
  • {{last_contribution_this_cycle}} – A DateTime value of the last date of a Donors contribution, if available. Cycle is the Candidate Cycle according to Campaign Finance Regulations or the current year for PACs and Non-profits.
  • {{highest_contribution_this_cycle}} – A Decimal value of the highest contribution for the donor, if available. Cycle is the Candidate Cycle according to Campaign Finance Regulations or the current year for PACs and Non-profits.
  • {{total_contributions_this_cycle}} – A Decimal value of the highest contribution for the donor, if available. Cycle is the Candidate Cycle according to Campaign Finance Regulations or the current year for PACs and Non-profits.

Common Functions

  • empty
    • Input: {{ name.first | empty "Friend"}} Output: Friend
  • multiply
    • Input: {{ highest_contribution_this_cycle | multiply 1.5 }} Output: 7.50
  • max
    • Input: `{{ total_contributions_this_cycle | max 2800 }}` Output 2800
  • default
    • Input: {{highest_contribution_this_cycle | default 5 }} Output: 5

Fundraising Examples

To increase a donor’s highest amount while staying in compliance, you can use the following syntax (For Federal Campaigns in 2020)

{{ highest_contribution_this_cycle | multiply 1.5 | default 5 | max 2800}}

If a donor has given $5, they will be asked for $7.5. If a donor has given $2,800, they will be asked for $2,800. If they have not given, they will be asked for $5.

Letting a donor know the last time they gave to change your message. Pending a hotfix to allow for this shortened version soon

{{if (last_contribution_this_cycle && (date.add_days last_contribution_this_cycle 1)) <}}
It has been over {{( – (date.parse model.last_contribution_this_cycle)).days}} days since your last donation.
{{ else }}
Stay active!
{{ end}}

last_contribution_this_cycle is a date with no time attached to it and may not exist for contacts without a donor history. Checking that it does not have a value is important. If the value is blank, the last_contribution_this_cycle will be treated as false. The && being the logical AND requites both sides to be set as true.


Within a code block, Campaign Deputy supports single line comments # and multi-line comments ##:

{{ name # this is a single line comment }}


{{ ## This
is a multi
comment ## }}


As you can notice, both single line and multi-line comments can be closed by the presence of a code block exit tag }}



Campaign Deputy supports two types of strings:

regular strings enclosed by double quotes “…” or simple quotes ‘…’. Regular strings supports multiline and will interpret the following escape sequences:

\’ single quote
\” double quote
\ backslash
\n new line
\r carriage return
\t tab
\b backspace
\f form feed
\uxxxx where xxxx is a unicode hexa code number 0000 to ffff
\x00-\xFF a hexadecimal ranging from 0x00 to 0xFF
verbatim strings enclosed by backstick quotes .... They are, for example, useful to use with for regex patterns :


{{ "this is a text" | regex.split \s+ }}
[this, is, a, test]


A number in Campaign Deputy {{ 100 }} is similar to a javascript number:

Integers: 100, 1e3
Hexadecimal integers: 0x1ef and unsigned 0x80000000u
Floats: 100.0, 1.0e3, 1.0e-3
32-bit floats: 100.0f
64-bit floats: 100.0d
128-bit decimals: 100.0m


The boolean value {{ true }} or {{ false }}


{{ true }}
{{ false }}



3.4 null
The null value {{ null }}

When resolving to a string output, the null value will output an empty string:


{{ null }}


Campaign Deputy supports the concept of global and local variables.

A global/property variable like {{ name }} is a liquid like handle, starting by a letter or underscore _ and following by a letter A-Z a-z, a digit 0-9, an underscore _

The following text are valid variable names:


NOTE: In some Liquid languages, the character – is allowed in a variable name, but when using Campaign Deputy, you will have to enclose it into a quoted string

A local variable like {{ $name }} is an identifier starting with $. A local variable is only accessible within the same include page or function body.

The special local variable $ alone is an array containing the arguments passed to the current function or include page.

The special local variables $0 $1 … $n is a shorthand of $[0], $[1] … $[n]. e.g Using $0 returns the first argument of the current function or including page.

The special variable this

The this variable gives you access to the current object bound where you have access to all local variables for the current scope.

Thus the following variable access are equivalent:


a = 5
a # output 5
this.a = 6
a # output 6
this["a"] = 7
a # output 7

output 567
In the case of the with statement, the this operator refers to the object passed to with:


a = {x: 1, y: 2}
with a
b = this

output 1

The special variable empty

The empty variable represents simply an empty object. It is mainly relevant to be compatible with liquid, by providing a way to compare an object with the empty object to check if it is empty or not:


a = {}
b = [1, 2]~}}
{{a == empty}}
{{b == empty}}




Campaign Deputy supports javascript like objects {…}

An object can be initialized empty :

{{ myobject = {} }}

An object can be initialized with some members:

{{ myobject = { member1: "yes", member2: "no" } }}

or use a json syntax:

{{ myobject = { "member1": "yes", "member2": "no" } }}

An object can be initialized with some members over multiple lines:

myobject = {
member1: "yes",
member2: "no"

Members of an object can be accessed:

{{ myobject.member1 }} also equivalent to {{ myobject["member1"] }}

You can access optional members in chain via the optional member operator ?. (instead of the regular member operator: . )

{{ myobject.member1?.submember1?.submember2 ?? "nothing" }} will return “nothing” as member1 doesn’t contain a submember1/submember2.

If the object is a “pure” objects (created with a {…} or instantiated by the runtime as a ScriptObject), you can also add members to it with a simple assignment:


myobject = {}
myobject.member3 = "may be"


may be

By default, Properties and methods of .NET objects are automatically exposed with lowercase and _ names. It means that a property like MyMethodIsNice will be exposed as my_method_is_nice. This is the default convention, originally to match the behavior of liquid templates. If you want to change this behavior, you need to use a MemberRenamer delegate

The special property empty?

Any object can respond the the property .empty? to check if it is empty or not:


a = {}
b = [1, 2]~}}




An array can be initialized empty :

{{ myarray = [] }}

An array can be initialized with some items:

{{ myarray = [1, 2, 3, 4] }}

An array can be initialized with some items over multiple lines:

myarray = [

Items of an array can be zero-based indexed:

{{ myarray[0] }}

If the array is a “pure” array (created with a […] or instantiated by the runtime as a ScriptArray), you can also add items to it with a simple assignment that will expand automatically the array depending on the index:

myarray = []
myarray[0] = 1
myarray[1] = 2
myarray[2] = 3
myarray[3] = 4

You can also manipulate arrays with the array builtin object.

Important notice

While whitespace characters are mostly not relevant while parsing, there is a case where a whitespace helps to disambiguate between an array indexer and an array initializer.

For instance, if a whitespace is found before a [ and the previous expression was a variable path expressions (see later), the following expression […] will be considered as an array initializer instead of an array indexer:

myfunction [1] # There is a whitespace after myfunction.
# It will result in a call to myfunction passing an array as an argument
myvariable[1] # Without a whitespace, this is accessing
# an element in the array provided by myvariable

Array with properties

An array can also contains attached properties:


a = [5, 6, 7]
a.x = "yes"
a.x + a[0]



The special size property

Arrays have a size property that can be used to query the number of elements in the array:


a = [1, 2, 3]



Code Block

A text enclosed by {{ and }} is a code block that will be evaluated by the templating engine.

A code block may contain:

a single line expression statement: {{ name }}
or a multiline statements:
if !name
name = “default”
or statements separated by a semi-colon ; to allow compact forms in some use cases:
{{if !name; name = “default”; end; name }}
Inside a code block, except for the EOL after each statement, white spaces characters are not affecting the parsing. There is only one case where whitespace is used to disambiguate between an array indexer and an array initializer.

Also, if a statement is an expression (but not an assignment expression), the result of the expression will be output to the rendering output of the template:


x = "5" # This assignment will not output anything
x # This expression will print 5
x + 1 # This expression will print 6

Note that in the previous example, there is no EOL between 5 and 6 because we are inside a code block. You can still use a plain string with an EOL inside a code block “\n” or you could use mixed code and text blocks:

Escape Block

Any code and text block can be escaped to produce a text block by enclosing it with {%{ and }%}

For example the following escape:

input{%{Hello this is {{ name }}}%}
outputHello this is {{ name }}

Any escape block can be also escaped by increasing the number of % in the starting and ending block:

input{%%{This is an escaped block: }%} here}%%} outputThis is an escaped block: }%} here

This allow effectively to nest escape blocks and still be able to escape them.

Hence a starting escape block {%%%%{ will required an ending }%%%%}

Whitespace Control

By default, any whitespace (including new lines) before or after a code/escape block are copied as-is to the output.

We provides two modes for controlling whitespace:

  • The greedy mode using the character - (e.g {{- or -}}), removes any whitespace, including newlines Examples with the variable name = "foo":
    • Strip whitespace on the left:
      input This is a < {{- name}}> text
      output This is a <foo> a text
    • Strip on the right:
      input This is a <{{ name -}} > text`
      output This is a <foo> text
    • Strip on both left and right:
      input This is a < {{- name -}} > text
      output This is a <foo> text

The non greedy mode using the character ~

  • Using a {{~ will remove any whitespace before but will stop on the first newline without including it
  • Using a ~}} will remove any whitespace after including the first newline but will stop after

Statements (if else)

if , else, else if

The general syntax is:


else if



An if statement must be closed by an end or followed by a else or else if statement. An else or else if statement must be followed by a else, else if or closed by an end statement.

An expression evaluated for a if or else if will be converted to a boolean.

Truthy and Falsy

By default, only the null and boolean false are considered as false when evaluated as booleans.

The following values are used for converting literals to boolean:

0 -> true
1 -> true or any non zero value
null -> false
false -> false
non_null_object -> true
“” -> true An empty string returns true
“foo” -> true
Example testing a page object:

{{ if page }}Page is not null{{ else }}Page is null!{{ end }}


for in … end
{{for in }}


The expression can be an array or a range iterator:

Loop on an array: {{ for page in pages }} This is the page {{ page.title }}{{ end }}

Loop on a range: {{ for x in 1..n }}This is the loop step [{{x}}]{{ end }}

The for loop (along with the tablerow statement below) supports additional parameters, offset, limit and reversed that can also be used togethers:

The offset parameter
Allows to start the iteration of the loop at the specified zero-based index:


{{~ for $i in 4..9 offset:2 ~}}
{{ $i }}
{{~ endfor ~}}



The limit parameter

Allows to limit the iteration of the loop for the specified count


{{~ for $i in 4..9 limit:2 ~}}
{{ $i }}
{{~ endfor ~}}



The reversed parameter

Allows to reverse the iteration on the elements


{{~ for $i in 1..3 reversed ~}}
{{ $i }}
{{~ endfor ~}}



break and continue

The break statement allows to early exit a loop

{{ for i in 1..5
if i > 2
end }}

The continue statement allows to skip the rest of a loop and continue on the next step

{{ for i in 1..5
if i == 2
[{{i}}]] step
{{ end }}

Will output:

[1] step
[3] step
[4] step
[5] step

Previous A/B Testing Email Campaigns
Next Digital – Series Setup
Table of Contents