React Flow recipes: Liquid in emails, HTTP request bodies, tags from metafields and win-back
React Flow recipes: Liquid in emails, HTTP request bodies, tags from metafields and win-back
In this article:
- Before you start: test Liquid with Log output
- Recipe 1: Liquid in an email template
- Recipe 2: JSON body for an HTTP request
- Recipe 3: tag products from a metafield
- Recipe 4: win-back campaign for inactive customers
- Liquid cheat sheet
These recipes show how to combine React Flow steps and Liquid variables to solve common tasks. Each recipe lists the trigger, the steps and the exact values to enter, so you can rebuild it in a few minutes and adjust it to your store.
Before you start: test Liquid with Log output
Almost every text field in React Flow accepts Liquid: email subjects and bodies, tags, HTTP request bodies, file names and more. Variables use the Shopify field names, for example {{ ``order.name`` }}, {{ product.title }} or {{ customer.firstName }}. Click Add variables next to a field to find the right name.
To check what a piece of Liquid returns before you use it:
- Add the Log output action ("Log and inspect Liquid variable values for debugging").
- Paste your Liquid into Output.
- Click Run Test (or Run flow in a One-Shot workflow) and open Log output in the result to see the rendered text.

As a result, some Shopify-specific Liquid tags and filters may not be fully supported or may behave differently.
Recipe 1: Liquid in an email template
Goal: a personal order email that greets the customer, lists the purchased products with links, and shows a special line for large orders.
- Trigger: Order created (or Order fulfilled for a review request).
- Action: Send email.
- Send to:
{{ ``order.email`` }} - Subject:
Thank you for your order {{ ``order.name`` }}, {{ order.customer.firstName | default: "friend" }}! - Message: click <> in the editor toolbar to switch to HTML and paste:
<p>Hi {{ order.customer.firstName | default: "there" }},</p>
<p>Here is what you ordered:</p>
<ul>
{% for line_item in order.lineItems %}
<li><a href="{{ shop.primaryDomain.url }}/products/{{ line_item.product.handle }}">{{ line_item.name }}</a></li>
{% endfor %}
</ul>
{% assign total = order.totalPriceSet.shopMoney.amount | plus: 0 %}
{% if total > 100 %}
<p>Your order qualifies for free gift wrapping. Just reply to this email!</p>
{% endif %}
<p>{{ shop.name }}</p>
- Click Preview above the editor to check the layout, then Run Test > Simulate with a real order.
Tips:
- Use
| default: "..."for values that can be empty, such as the first name of a guest customer. - For a reliable delivery, send emails through your own SMTP or Microsoft connector.
Recipe 2: JSON body for an HTTP request
Goal: send product data to an external system (an ERP, a warehouse or a webhook) whenever a product is created.
- Trigger: Product added to store.
- Action: Send HTTP request.
- Method:
POST - Url: the address of the receiving system, for example
https://example.com/webhooks/products - Headers:
Content-Type=application/json. Add an authorization header if the system requires one, for exampleAuthorization=Bearer <your token>. - Body:
{
"id": "{{ product.id }}",
"title": {{ product.title | json }},
"tags": {{ product.tags | json }}
}

Why | json? It wraps text in quotes and escapes special characters, and it turns lists into JSON arrays. In our test, {{ product.title | json }} returned "Snowboard: Liquid" and {{ product.tags | json }} returned ["Accessory","customize","Sport","Winter"]. Without the filter, a title with a quote mark would break the JSON.
Optional settings:
- Handle HTTP error codes: treat error responses as a normal result and get access to the error in the workflow variables, so you can branch on it with a condition.
- Output results to use in other action or condition: save the response. The field below shows the variable to use in the next steps, for example
{{workflow.e021529... | json}}.
Recipe 3: tag products from a metafield
Goal: add a tag based on a product metafield, for example material-cotton from the metafield custom.material, so you can use the tag in collections and filters.
- Trigger: Product added to store, or Product updated with a field to observe.
- Condition (optional, to skip products without the metafield): Liquid > Liquid result, with
{{ product.metafields.custom.material }}and the matcher is not empty. - Action under then: Add product tags with the tag:
material-{{ product.metafields.custom.material | downcase | replace: " ", "-" }}Replace custom.material with the namespace and key of your metafield. You can find them in Shopify Settings > Metafields and metaobjects > Products: open the metafield definition and look at its namespace and key, for example custom.material.
In our test, a product with the metafield test_data.binding_mount = met3 got the tag binding-met3. Products without the metafield returned an empty value, which is why the condition in step 2 is useful.
To tag the products you already have, build the same steps in a One-Shot workflow: Fetch resources > Products, then the condition and Add product tags under for each.
for loop and {% if forloop.last == false %},{% endif %}.Recipe 4: win-back campaign for inactive customers
Goal: every day, find customers who haven't ordered for 90 days, send each of them a personal discount code once, and mark them so they don't get it again.

- Trigger: Scheduled time. Set the start time and click Add repeat: every 1 Days.
- Action: Fetch customers.
- Query type: Segments, Advanced, and in Edit query:
number_of_orders >= 1 AND last_order_date < -90d AND customer_tags NOT CONTAINS 'winback-sent'
- Optionally add
AND email_subscription_status = 'SUBSCRIBED'to email only subscribers. - Check the Runs counter: each fetched customer counts as a run.
- Under for each: Create discount code.
- Discount type: Amount off order, Generate random discount code, Percentage
15. - Usage limit
1. Customer:{{ ``loop.customer.id`` }}, so only this customer can use the code. - Ends in: Within days
14. - Select Output results to use in other action or condition. Copy the variable shown under "Use this code to access as variable", for example
{{loop.workflow.975ce...}}.
- Discount type: Amount off order, Generate random discount code, Percentage

- Next: Send email.
- Send to:
{{ loop.customer.defaultEmailAddress.emailAddress }} - Subject:
We miss you, {{ loop.customer.firstName }}! Here is 15% off - In the message, insert the code with the copied variable plus
.title, for example{{loop.workflow.975ce....title}}..discountValuereturns the discount value.
- Send to:
- Next: Add customer tags with
winback-sent. Because the query excludes this tag, each customer gets the offer only once. - Click Save, test with Run Test > Simulate, then Turn on.
There is also a ready-made win-back template in Templates and Playbooks. It runs monthly, picks customers whose last order was 30–365 days ago, and calculates a bigger discount for customers who have been inactive longer. The Playbooks version also provides additional analytics on the revenue generated by the playbook.
Liquid cheat sheet
- Default value:
{{ order.customer.firstName | default: "there" }} - Upper or lower case:
{{ product.title | upcase }},{{ product.vendor | downcase }} - Replace spaces:
{{ product.title | downcase | replace: " ", "-" }} - Format a date:
{{ order.createdAt | date: "%Y-%m-%d" }}. Use"now"to get the current date, for example:{{ "now" | date: "%Y-%m-%d" }}. - Join a list:
{{ product.tags | join: ", " }} - Safe JSON value:
{{ product.title | json }} - Condition with a number:
{% assign total = order.totalPriceSet.shopMoney.amount | plus: 0 %}{% if total > 100 %}...{% endif %}(plus: 0turns the amount into a number) - Loop:
{% for line_item in order.lineItems %}{{ line_item.name }}{% endfor %} - Metafield:
{{ product.metafields.namespace.key }}
✔️ Next step
Check our articles to learn more about the app's functionality:
Updated on: 14/10/2026
Thank you!
