Liquid
Liquid inserts supplied facts into an email: a customer name, order total, renewal date or destination link. Use it for values and conditional content that should follow defined rules, alongside Adaptyle for passages that need generated writing.
That is the difference from Adaptyle, which writes new copy for each person. Liquid fills in what you know. Both work anywhere in a template, a broadcast, or a workflow's send-email step, and they mix freely in the same email.
Insert your first value
Wrap the value in double braces:
Hi {{ contact.firstName }}, your order is on its way.
contact there is a key, not a fixed word. It is right only when your email's key for the recipient really is contact, and the next section is where that key comes from.
What you can reference
An email declares what information it needs, and you address each piece by the key on that list, not by its schema name. Where the key comes from depends on what is sending the email:
- On a template or a workflow send-email step, you name it yourself when you add the property.
- On a broadcast, maxclicks names it for you, after the audience's contact schema slug. A
Learneraudience gives youlearner, so you write{{ learner.firstName }}. A schema sluggedabandoned-cartgives youabandoned_cart.
Open the Data tab in the editor if you are unsure: the key is on the row. The examples on this page use contact, standing in for whatever your key actually is.
A record gives you its stored writable attributes. Evaluated lookups and computed attributes require explicit expansion. A computed value may be missing or stale; selecting it reads a published result rather than forcing a new background calculation. Active broadcast revisions and workflow root runs retain their selected generations; a newer publication does not replace their captured context. See expansion freshness.
Contact fields
Every contact carries these, plus your own attributes.
| Field | Type |
|---|---|
id | string |
fullName, firstName, lastName | string |
email | |
phone | phone |
userId | string |
avatarUrl | url |
notes | string |
tags | string list |
subscriptions | string list |
topicIds | string list |
source | string |
emailStatistics | object |
createdAt, updatedAt | date and time |
emailStatistics holds sent, delivered, bounced, complained, unsubscribed, uniqueOpens and uniqueClicks.
{{ contact.firstName }}
{{ contact.emailStatistics.uniqueOpens }}
{{ contact.company }}
contact.company only resolves if company is a field you added to that contact schema.
Objects, events and plain values
Objects and events give you their fields under the key you gave them. A plain JSON value gives you whatever its shape defines.
{{ order.total }}
{{ purchaseEvent.amount }}
{{ customData.nested.field }}
Give it a fallback
A misspelled field does not fail the send. It renders as nothing, so {{ contact.frstName }} quietly ships an email that starts "Hi ,". Always give anything that might be empty a fallback.
Hi {{ contact.firstName | default: "there" }},
The editor catches the typo before you get there: it checks every expression against the information the email says it needs, flags a field or a filter it does not recognise, suggests the closest match, and offers a one-click fix in the visual editors. Once the template has sample data it also shows you what each expression resolves to, and warns when one comes out empty. The MJML and HTML code editors underline the same problems in place.
Let maxinja finish the expression
In the two visual editors, keep typing inside a Liquid expression and maxinja offers the rest of it as faded text a moment after you pause. Tab accepts it, Escape dismisses it, and typing the same characters yourself eats into it rather than dismissing it. It waits until there are a few characters to go on, so a bare {{ }} gets nothing.
Each suggestion is a small AI call, so it is a workspace-wide choice rather than a per-email one. Find it under Settings, maxinja, on the General tab, as Editor autocomplete: Automatic suggests as you type, Off means no suggestions and no cost.
Show something only when it applies
{% if contact.firstName %}
Hello {{ contact.firstName }}!
{% else %}
Hello there!
{% endif %}
| Operator | Meaning |
|---|---|
== | Equal |
!= | Not equal |
>, >=, <, <= | Numeric comparison |
contains | Contains this text, or this item |
and, or | Combine conditions |
unless, elsif and case/when work too.
{% case contact.userGroup %}
{% when "premium" %}Welcome, Premium Member!
{% when "trial" %}Enjoying your trial?
{% else %}Welcome!
{% endcase %}
Repeat a block for every item
{% for item in order.items %}
{{ forloop.index }}. {{ item.name }} - ${{ item.price }}
{% else %}
No items.
{% endfor %}
Add limit:3, offset:1 or reversed to control what you loop over. Inside the loop, forloop.index, forloop.index0, forloop.first, forloop.last and forloop.length tell you where you are.
Reshape a value with filters
Pipe a value through a filter with |, and chain as many as you like, left to right.
{{ contact.firstName | upcase }}
{{ contact.notes | truncate: 50 }}
{{ contact.tags | join: ", " }}
{{ order.total | round: 2 }}
{{ contact.createdAt | date: "%B %d, %Y" }}
{{ contact.email | url_encode }}
{{ contact.fullName | default: "Customer" | capitalize }}
| For | Filters |
|---|---|
| Text | upcase, downcase, capitalize, strip, strip_html, truncate, replace, url_encode |
| Numbers | round, floor, ceil |
| Lists | join, first, last, size, map |
| Dates | date, with codes like %Y, %m, %d, %B, %H, %M |
| Anything | default |
maxclicks uses LiquidJS for standard filters and tags, with bounded rendering and no filesystem template library. include, render and layout cannot load files from your project or a remote URL. Put the required email content in the template itself.
Keep data, markup and AI instructions separate
Liquid runs before Adaptyle. An authored if condition can include or exclude an AI prompt for a particular recipient, so one email can need AI while another follows only its static branch.
{% if contact.firstName != blank %}
Hello {{ contact.firstName | escape }},
{% else %}
Hello there,
{% endif %}
Use escape for customer text inserted into HTML. url_encode encodes a query-string value; it does not replace a complete, valid destination URL. Supply absolute links in email markup.
Text stored in a customer field is treated as data. Writing Adaptyle delimiters into that field does not turn it into an executable personalization instruction. Author prompts directly in the template. Avoid capturing and transforming blocks of Adaptyle syntax with Liquid filters; transforming protected syntax markers is rejected.
Rendering is bounded: the Liquid source is limited to 1,048,576 characters, serialized data and rendered output to 2,097,152 characters, and rendering to a 1-second budget with memory and evaluation limits. Long loops or oversized data can therefore fail even when the syntax is valid. Narrow the data and loop length before retrying.
Pull in your brand
The brand kit you picked in the editor arrives in the email as branding, so you reference your colours and logo instead of pasting them in by hand and re-pasting them when the brand changes.
{{ branding.brandName }}
{{ branding.logoUrl }}
{{ branding.colorPalette.buttonBackgroundColor }}
{{ branding.tones | join: ', ' }}
| Key | Holds |
|---|---|
brandName, brandSummary | Your brand's name and one-line summary |
websiteUrl | Your site |
logoUrl, iconUrl | Logo and icon |
values, tones | Brand values and tones of voice, as a list |
assets | Images from the selected collection, each with a fileUrl |
colorPalette | canvasBackgroundColor, backgroundColor, headingColor, textColor, buttonBackgroundColor, buttonColor, linkColor |
stylePack | cornersRadiusPixels, spacingPixels, headingFontFamily, heading1FontSizePixels through heading6FontSizePixels, textFontFamily, textFontSizePixels |
includingRule, avoidingRule | The Always include and Always avoid notes from your brand voice |
Check it before you send
The Base preview resolves Liquid against your sample data without a model call. Generating sample data or a personalized Adaptyle preview is a separate AI action. That is the fastest way to catch an expression that resolves to nothing before 50,000 people see it.
Related
- Adaptyle: write an instruction and have maxinja rewrite that part per recipient.
- Email editor: where you write all of this.
- Templates and Broadcasts: the two ways an email goes out.
- Contacts: the fields a contact carries.