Liquid in every field
Every text field of every step is a Liquid template, the same template language Shopify themes use. A plain variable is the simplest case. Filters, conditions and loops work in the same field.
Outputs and filters
An output is written in double curly braces. A filter changes the value. Filters are chained with the pipe character:
{{ event.payload.email | downcase }}
{{ event.payload.title | truncate: 40 }}
{{ event.payload.note | default: "no note" }}
Filters that matter in workflows
| Filter | Use |
|---|---|
json |
Writes a value as JSON, with quotes and escaping. The most important one: see below |
parse_json |
Turns JSON text into an object or a list |
default |
A fallback for an empty value |
date |
Formats a date, for example with '%Y-%m-%d'. The word 'now' is the current time |
plus, minus, times, divided_by, round |
Maths. times: 1 turns text such as "49.90" into a number |
append, prepend, replace, remove |
Build text |
upcase, downcase, capitalize, strip (or trim) |
Tidy text |
split, join, first, last, size |
Lists |
map, where, sort, uniq, concat |
Work on lists of objects, for example all SKUs of an order |
url_encode, escape |
Values inside URLs and HTML |
An unknown filter is an error, not silently ignored, so a typing mistake shows up in the first test.
JSON and the json filter
Many fields expect JSON: the body of an HTTP request, Variables (JSON) of a Shopify step, Input (JSON) of a code step, Payload (JSON). Text from your store can contain quotes and line breaks, and that breaks JSON written by hand.
Wrong, breaks as soon as a title contains a quote:
{ "title": "{{ event.payload.title }}" }
Right. The filter escapes the text and adds the quotes itself, so you write none:
{ "title": {{ event.payload.title | json }} }
It works for everything: a number stays a number, an empty value becomes null, an object or a list is written in full:
{ "order": {{ event.payload | json }}, "skus": {{ event.payload.line_items | map: "sku" | json }} }
Objects and lists print as JSON even without the filter, and an empty value prints nothing. Inside JSON, still use the filter, so that an empty value becomes null instead of a gap.
Keeping the type
A field that is exactly ONE output keeps the value's type. This hands a real list to Repeat for each, not text:
{{ steps.orders.output.orders.nodes }}
As soon as there is anything else in the field, the result is text.
if and for
A condition compares values. It cannot use a filter, so work the value out first with assign:
{% assign total = event.payload.total_price | times: 1 %}
{% if total > 500 and event.payload.currency == "EUR" %}Priority order{% else %}Order{% endif %} {{ event.payload.name }}
A loop, for the lines of an email or of a CSV file:
{% for line in event.payload.line_items %}{{ line.quantity }} x {{ line.title }}
{% endfor %}
In JSON, mind the comma between entries:
[{% for line in event.payload.line_items %}{{ line.sku | json }}{% unless forloop.last %},{% endunless %}{% endfor %}]
Dates
{{ 'now' | date: '%Y-%m-%d' }}
{{ event.payload.created_at | date: '%d.%m.%Y %H:%M' }}
{{ 'now' | date: '%s' | minus: 1209600 | date: '%Y-%m-%d' }}
The last line is the date 14 days ago: the time in seconds, minus 14 days in seconds, formatted again. The maintenance templates use it in a Shopify search.
Limits
- A field's template can be 100 KB, and must finish within 2 seconds.
- Including other templates (
render,include) is not available. - A missing value renders as nothing and does not fail the step. See Variables and the variable picker.
- Conditions do not use templates. Their Field is a plain path such as
event.payload.vendor. See Conditions.
For a whole script that reads and changes your store in Liquid, see Liquid script step.

