Custom email templates
What you can change
For the emails listed below you can supply your own body and, optionally, your own subject.
The frame around every email stays ours: the logo band, the legal footer and the French section are
added automatically and cannot be replaced. Text messages (SMS, WhatsApp) are not affected.
Send us one .tpl file per email (maximum 256 KB) and, if you want one, a subject line. We load it
for your account, check it, send you a preview, and switch it on. If anything in your template ever
fails to render, the standard email is sent instead (with the standard subject), so a mistake in a
template never blocks a payment email.
Which emails
eLinx notification emails
Each key has four parts: elinx.<event>.<product case>.<role>.
| Part | Values |
|---|---|
| event | confirmation, first_notification, cancellation, expired, decline, reminder, funds_deposit, scheduled_failure, payment_failed, upcoming_payment |
| product case | one_time_collect, one_time_pay, recurring_collect, recurring_pay, beneficiary, or any |
| role | receiver, sender, or any |
Example: elinx.confirmation.one_time_collect.receiver is the confirmation email a receiver gets for
a one-time collection. elinx.decline.any.any is every decline email.
When an email is sent, we look for your template in this order and use the first one that exists:
elinx.<event>.<product case>.<role>(exactly this email)elinx.<event>.any.<role>(this event and role, any product case)elinx.<event>.<product case>.any(this event and product case, either role)elinx.<event>.any.any(this event, everything)elinx.body(every eLinx email)
So you can supply one elinx.body template for everything and add more specific ones only where you
want different wording. The event is available inside the template ({!$Message.Event}), so a single
template can also use {!if} to vary its wording.
Transaction approval email
transaction_approval.single is the email sent to approvers when a transaction needs approval.
Template language
Templates use tags delimited by {! and }, so ordinary braces in CSS ({ color: red; }) need no
escaping. Write {!ldelim} / {!rdelim} for a literal {! or }.
{!$Message.Summary} print a value
{!$Message.GreetingName|default:'there'} print a value with a fallback
{!if $Message.CtaUrl}...{!elseif $x}...{!else}...{!/if}
{!foreach $Message.FieldList as $Row}...{!/foreach}
{!* this is a comment *}Allowed
- Printing a variable, with dotted paths (
$Message.Fields.Amount) and the loop properties
@first,@last,@index,@iteration,@total(for example$Row@first). {!if},{!elseif},{!else},{!/if}using variables, quoted text, numbers,true/false/null
and the operators== != > < >= <= eq ne gt lt gte lte and or not !.{!foreach $List as $Item}(oras $Key => $Item),{!foreachelse},{!/foreach}.- Comments,
{!ldelim},{!rdelim}. - Modifiers:
escape,default,date_format,number_format,upper,lower,truncate,
nl2br,replace. Arguments are quoted text or numbers:|truncate:80,|replace:'a':'b',
|date_format:'%B %e, %Y',|number_format:2.
Not allowed
Anything else is rejected when we load the file, including {!assign}, {!include}, function and
method calls, {!php}, $smarty, backticks and backslashes inside a tag, and PHP tags. A PHP tag is
<?, ?>, <%, %> or <script language="php"> anywhere in the file, so write a percentage in HTML as
width="100%", never width=100%>.
Limits
- File size 256 KB; at most 2000 tags; blocks nested at most 10 deep.
{!foreach}cannot be nested inside another{!foreach}, and$Blockscannot be used inside one.- A list (such as
$Message.ScheduleRows) is cut to its first 1000 items. - Numbers written in a tag have at most 6 digits;
|number_formattakes 0 to 10 decimals and|truncate
a length of 0 to 10000. - A tag has at most 3 modifiers, and at most one
|escape, whose type must behtml,htmlall,urlor
quotes(plain|escapemeanshtml). - The text written inside one
{!foreach}is at most 16 KB, and each$Blocks.<Name>can be used at most
twice in a template. - Modifiers work on single values only. A list or object (
$Message,$Message.FieldList,
$Message.Fields, a{!foreach}row such as$r) can be printed bare but cannot take a modifier;
modify one field instead, for example{!$r.Amount|upper}. - A loop variable (
$rin{!foreach $Message.ScheduleRows as $r}, and the key in$k => $v) only exists
between its{!foreach}and{!/foreach}; using it after the loop is rejected. - Loop variable names must be unique in the template: each
{!foreach}needs its own names, a name cannot
be reused by a later loop, cannot be read anywhere outside its loop (before or after), and cannot be the
name of a template variable (Message,Blocks,PartnerName,recipient_name, ...). - Nothing between
{!foreachelse}and{!/foreach}may use the loop's variables ($r,$k); the loop
has no rows there. Print other variables, such as$Message.Summary, instead. - Only plain
{!$variable.path}references are accepted: no$variable::...and no$variable->.... - A comment cannot contain
{!. - Quoted text inside a tag: at most 1024 characters; the two texts of
|replaceat most 64
characters each, and|replaceat most once per tag. Double-quoted text cannot contain$,{or
}; use single quotes. - Rendered output is capped at 2 MB. Before sending, we work out the most a template could produce from
the data of that email (loops multiply their body by the number of rows, modifiers can grow a value).
A template over the cap is not used and the standard email is sent instead. We check this when we load
the file with sample data (an error) and with the longest possible lists (a warning: "may fall back to
the default email for very long schedules").
Escaping: the one rule to remember
Values are prepared for you before your template runs:
- Text values (names, amounts, labels) and URLs are already HTML-escaped. Print them as
they are. Do not add|escape: it would show&amp;. (We warn you when you do.) - HTML values are our own markup and are printed as-is (for example
$Message.Summary, which
contains bold amounts, and$Message.ExpiryLine). $Blocks.*are ready-made pieces of our standard email, already rendered to HTML. Each is an
empty string when it does not apply to the email being sent, so you can always print it.- A URL is only passed through when it starts with
http://orhttps://; otherwise it is empty.
Optional values may be empty: wrap them in {!if}. Only the variables listed below are available.
Variables
Every template can use these branding values.
| Variable | Kind | Meaning |
|---|---|---|
$PrimaryColor | text | Brand colour for buttons, e.g. #FFD147 |
$ButtonTextColor | text | Text colour on that button |
$PartnerName | text | Brand name |
$WhitelabellingEnabled | yes/no | Whether the email is shown under your brand |
eLinx emails (all elinx.* keys)
elinx.* keys)| Variable | Kind | Meaning |
|---|---|---|
$Message.Event | text | The event, e.g. confirmation |
$Message.Subject | text | Our standard subject |
$Message.GreetingName | text | Name to greet |
$Message.Summary | html | The main sentence |
$Message.FieldList | list | Detail rows; each has Label and Value (text) |
$Message.Fields | map of text | The same details by name, e.g. $Message.Fields.Amount, $Message.Fields.DueDate |
$Message.ScheduleRows | list | Payment schedule; each has Number, PaymentDate, Amount (text) |
$Message.CtaLabel | text | Button label (empty when there is no button) |
$Message.CtaUrl | url | Button link |
$Message.DocumentUrl | url | Link to a document |
$Message.ExpiryLine | html | "This request expires on ..." line |
$Message.SignOff | text | Sign-off name |
$Blocks.DetailsCard | html | Our details card |
$Blocks.ScheduleTable | html | Our payment schedule table |
$Blocks.CtaButton | html | Our branded button |
$Blocks.DocumentLink | html | Our document link |
Subject tokens: {ReceiverName}, {SenderName}, {Amount}, {Frequency}, {FirstPaymentDate},
{ConnectionDate}, {DueDate}, {ExpiryDate}, {ScheduledDate}. A token with no value for a given
email becomes empty, so use the ones that make sense for the event. Example:
{SenderName} has requested {Amount}.
Transaction approval (transaction_approval.single)
transaction_approval.single)| Variable | Kind | Meaning | |
|---|---|---|---|
$transaction_label | text | Label for the ID row, e.g. "Transaction ID" | |
$transaction_id | text | Transaction ID | |
$transaction_type | text | Type, e.g. "Payment" | |
$payment_rail | text | Payment method | |
$recipient_name | text | Who is being paid | |
$currency | text | Currency code | |
$amount | number | Amount (use ` | number_format:2`) |
$schedule_type | text | Schedule, empty for a one-off | |
$date_submitted | text | Submission date and time | |
$has_invoice | yes/no | Whether an invoice is attached | |
$invoice_number | text | Invoice number | |
$funding_intro | text | Funding explanation, may be empty | |
$funding_row_label | text | Label of the funding row, may be empty | |
$funding_row_value | text | Value of the funding row | |
$funds_payment | text | Funding payment detail, may be empty | |
$consequence | text | What happens on approval, may be empty | |
$is_funding_role | yes/no | Whether the approver is the funding party | |
$url | url | The approval link; every approval email should show it |
Subject tokens: {TransactionID}, {Amount}, {Currency}, {RecipientName}, {PaymentRail}.
Subjects are limited to 255 characters and line breaks are removed.
Sample templates
All samples pass our checks as written; copy and adapt them.
eLinx (use as elinx.body, or for any specific elinx.* key)
elinx.body, or for any specific elinx.* key){!* Example body for the eLinx key family. Replace the wording and styling freely. *}
<div style="font-family: Arial, sans-serif; color: #222222; font-size: 15px; line-height: 1.5;">
<p>Hello {!$Message.GreetingName|default:'there'},</p>
<p>{!$Message.Summary}</p>
{!if $Message.FieldList}
<table style="border-collapse: collapse; width: 100%; margin: 16px 0;">
{!foreach $Message.FieldList as $Row}
<tr>
<td style="padding: 6px 8px; color: #666666;">{!$Row.Label}</td>
<td style="padding: 6px 8px; font-weight: bold;">{!$Row.Value}</td>
</tr>
{!/foreach}
</table>
{!/if}
{!if $Message.ScheduleRows}
<table style="border-collapse: collapse; width: 100%; margin: 16px 0;">
<tr><th align="left">#</th><th align="left">Payment date</th><th align="left">Amount</th></tr>
{!foreach $Message.ScheduleRows as $Payment}
<tr><td>{!$Payment.Number}</td><td>{!$Payment.PaymentDate}</td><td>{!$Payment.Amount}</td></tr>
{!/foreach}
</table>
{!/if}
{!if $Message.CtaUrl and $Message.CtaLabel}
<p>
<a href="{!$Message.CtaUrl}"
style="display: inline-block; padding: 12px 24px; border-radius: 4px; text-decoration: none; background: {!$PrimaryColor}; color: {!$ButtonTextColor};">{!$Message.CtaLabel}</a>
</p>
{!/if}
{!if $Message.DocumentUrl}
<p><a href="{!$Message.DocumentUrl}">View the attached document</a></p>
{!/if}
{!if $Message.ExpiryLine}<p style="color: #666666;">{!$Message.ExpiryLine}</p>{!/if}
{!* Our ready-made pieces are also available as the Blocks variables, e.g. DetailsCard or CtaButton *}
<p>{!$Message.SignOff}</p>
</div>eLinx, one specific email (elinx.confirmation.one_time_collect.receiver)
elinx.confirmation.one_time_collect.receiver)Used only for the confirmation sent to the person paying a one-time request. Every other eLinx email
keeps falling back to a broader key (or to our default). It reads single values by name from
$Message.Fields and drops in our ready-made $Blocks pieces.
{!* Example for one specific eLinx email: the confirmation sent to the person paying a one-time request.
It reads single fields by name from $Message.Fields and reuses our ready-made Blocks. *}
<div style="font-family: Arial, sans-serif; color: #222222; font-size: 15px; line-height: 1.5;">
<p>Hi {!$Message.GreetingName|default:'there'},</p>
<p>Thanks, your payment is confirmed.</p>
{!if $Message.Fields.Amount}
<p style="font-size: 22px; font-weight: bold; margin: 16px 0;">{!$Message.Fields.Amount}</p>
{!/if}
{!if $Message.Fields.DueDate}
<p style="color: #666666;">Due {!$Message.Fields.DueDate}</p>
{!/if}
{!* Our standard details card and button, already built with your branding colours *}
{!$Blocks.DetailsCard}
{!$Blocks.CtaButton}
<p>{!$Message.SignOff}</p>
</div>Transaction approval (transaction_approval.single)
transaction_approval.single){!* Example body for transaction_approval.single. *}
<div style="font-family: Arial, sans-serif; color: #222222; font-size: 15px; line-height: 1.5;">
<p>A {!$transaction_type|lower} of <b>{!$currency} {!$amount|number_format:2}</b> to {!$recipient_name} is waiting for your approval.</p>
<table style="border-collapse: collapse; margin: 16px 0;">
<tr><td style="padding: 4px 12px 4px 0; color: #666666;">{!$transaction_label}</td><td>{!$transaction_id}</td></tr>
<tr><td style="padding: 4px 12px 4px 0; color: #666666;">Payment method</td><td>{!$payment_rail}</td></tr>
<tr><td style="padding: 4px 12px 4px 0; color: #666666;">Submitted</td><td>{!$date_submitted|date_format:'%B %e, %Y'}</td></tr>
{!if $schedule_type}
<tr><td style="padding: 4px 12px 4px 0; color: #666666;">Schedule</td><td>{!$schedule_type|upper}</td></tr>
{!/if}
{!if $has_invoice}
<tr><td style="padding: 4px 12px 4px 0; color: #666666;">Invoice</td><td>{!$invoice_number|default:'-'}</td></tr>
{!/if}
{!if $is_funding_role and $funding_row_label}
<tr><td style="padding: 4px 12px 4px 0; color: #666666;">{!$funding_row_label}</td><td>{!$funding_row_value}</td></tr>
{!/if}
</table>
{!if $funding_intro}<p>{!$funding_intro}</p>{!/if}
{!if $consequence}<p style="color: #666666;">{!$consequence}</p>{!/if}
<p>
<a href="{!$url}"
style="display: inline-block; padding: 12px 24px; border-radius: 4px; text-decoration: none; background: {!$PrimaryColor}; color: {!$ButtonTextColor};">Review and approve</a>
</p>
</div>Updated about 2 hours ago