Variables and Liquid
Everything dynamic in this app is Liquid - the same templating language Shopify themes use. You write a placeholder, and the app fills it in at send time.
The four namespaces
| Namespace | Where the value comes from | Available in |
|---|---|---|
{{ variables.key }} |
The Variables (JSON) field on the Shopify Flow action | Email subject and body, HTTP URL, headers and body |
{{ secrets.key }} |
The Secrets page | Same, plus SMTP username and password |
{{ shop.name }}, {{ shop.domain }}, {{ shop.email }} |
Your store | Email and HTTP actions |
{{ flow.key }} |
The raw Shopify Flow payload | HTTP requests only |
The System Variables card in the editor lists the shop values with their current contents, and clicking one copies it.
Passing variables from Shopify Flow
In the Flow action, put a JSON object in the Variables (JSON) field:
{
"firstName": "{{ customer.firstName }}",
"orderNumber": "{{ order.name }}",
"total": "{{ order.totalPriceSet.shopMoney.amount }}"
}
Then reference them anywhere in the layout:
Hi {{ variables.firstName }}, your order {{ variables.orderNumber }} is on its way.
The keys are yours to choose. Whatever you name on the left is what you use after variables..
Filters
Standard Liquid filters work. The most useful one is default, which protects you from an empty value:
Hi {{ variables.firstName | default: "there" }}
Others behave as you would expect:
{{ variables.productTitle | upcase }}
{{ variables.total | round: 2 }}
{{ variables.note | truncate: 100 }}
[!NOTE] Templates are rendered leniently on purpose. An unknown variable renders as an empty string and an unknown filter is ignored, rather than throwing an error and failing the send. That keeps a typo from blocking a customer's email - but it also means a typo shows up as a blank, not an error message. When something renders empty, check the spelling first.
Conditionals and loops
Use the HTML / Code block for conditionals:
{% if variables.isVip %}
<p>As a VIP you get free shipping on this order.</p>
{% else %}
<p>Spend a little more to unlock free shipping.</p>
{% endif %}
Use the Loop block to repeat over an array, for example order line items:
{% for item in variables.items %}
<tr>
<td>{{ item.title }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.price }}</td>
</tr>
{% endfor %}
Pass the array from Flow as JSON:
{
"items": [
{ "title": "Classic White T-Shirt", "quantity": 2, "price": "29.00" },
{ "title": "Eco Water Bottle", "quantity": 1, "price": "19.00" }
]
}
To preview a loop in the builder, enter the same JSON array as the value in the Test Variables tab.
Objects behave differently in email and HTTP
This is the one sharp edge worth knowing.
- In an HTTP request, interpolating an object or array serialises it to JSON automatically.
{{ variables.records }}becomes a proper JSON array in the request body. Ajsonfilter is also available if you want to be explicit. - In an email, there is no such conversion. Interpolating a whole object renders the literal text
[object Object].
So in emails, always reach into the object rather than printing it:
Wrong: {{ variables.customer }}
Right: {{ variables.customer.firstName }} {{ variables.customer.lastName }}
Or loop over an array instead of printing it whole.
Secrets in templates
Reference a stored secret the same way:
Authorization: Bearer {{ secrets.airtableAccessToken }}
Use the exact syntax {{ secrets.keyName }}. These do not work:
{{ secret.keyName }}- singular{{ secrets['keyName'] }}- bracket syntax
Key names are case-sensitive. See Secrets.
Body override for HTTP requests
The HTTP action's variables field has a second mode. Pass a body key and it replaces the saved request body entirely:
{
"body": { "anything": "you like", "shaped": ["however"] }
}
This is useful when the payload shape is decided by an earlier Flow step. The one exception: if your saved body itself references {{ variables.body }}, it is treated as an ordinary variable instead.
Testing what you wrote
The on-page preview does not evaluate Liquid - it shows the tags as written. To see resolved values:
- For emails, use Send test email from the layout editor.
- For HTTP requests, use the Test button, which shows the Resolved URL and Resolved Body alongside the response.
Related
- Build an email layout - where these variables go.
- Secrets - storing credentials safely.
- Make an HTTP request from Shopify Flow - variables in a real request.

