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.