---
title: "Template variables and Liquid - Workflow Documents"
description: "The data a template can print for an order, a product or a customer, the brand object, the money, percent and br filters, and what Liquid in a document template does not support."
canonical: "https://docs.workflow-documents.app/template-variables-and-liquid"
---

# Template variables and Liquid

A template is HTML with Liquid tags. When a document is generated, the Liquid is filled with the data of one order, one product or one customer, plus your shop and your branding. This page lists what a template can print and which filters it can use.

The editor shows the same list for the template you have open: the **Variables** card under the code has a short introduction and the full list of variables to expand.

## What every template sees

| Object | What it holds |
| --- | --- |
| `order`, `product` or `customer` | The one the template renders, set with **Renders** on the Settings tab |
| `shop` | Your store: `shop.name`, `shop.email`, `shop.currency`, `shop.domain`, `shop.url`, `shop.address` |
| `brand` | Your branding from Settings, see below |
| `language` | The language the document is made for, see [Templates in several languages](https://docs.workflow-documents.app/languages.md) |

Print a value with double braces and loop over a list with `for`:

```liquid
<h1>Invoice {{ order.name }}</h1>

{% for line in order.line_items %}
  <p>{{ line.quantity }} x {{ line.title }} - {{ line.discounted_total | money }}</p>
{% endfor %}

<p>Total: {{ order.total_price | money }}</p>
```

## Order

For templates that render **An order**.

| Variable | What it holds |
| --- | --- |
| `order.name` | The order number, for example #1001 |
| `order.id` | The Shopify id of the order |
| `order.created_at`, `order.processed_at` | Dates, print them with the `date` filter |
| `order.email`, `order.phone` | Contact details on the order |
| `order.note`, `order.po_number`, `order.tags` | Note, purchase order number and tags |
| `order.customer_locale` | The language the customer ordered in |
| `order.currency` | The currency code |
| `order.financial_status`, `order.fulfillment_status` | For example PAID and UNFULFILLED |
| `order.customer` | `first_name`, `last_name`, `name`, `email` |
| `order.billing_address`, `order.shipping_address` | `name`, `company`, `address1`, `address2`, `zip`, `city`, `province`, `country`, `country_code`, `phone` |
| `order.shipping_method` | The title of the shipping line |
| `order.payment_gateways`, `order.discount_codes` | Lists of text |
| `order.subtotal_price`, `order.shipping_price`, `order.total_tax`, `order.total_discounts`, `order.total_price`, `order.total_refunded` | Amounts as numbers |
| `order.tax_lines` | A list, each with `title`, `rate`, `price` |
| `order.line_items` | A list, see the next table |

Each entry of `order.line_items`:

| Variable | What it holds |
| --- | --- |
| `title`, `variant_title`, `sku`, `vendor` | Text |
| `quantity` | Number |
| `unit_price`, `discounted_unit_price` | Price of one unit, before and after discounts |
| `total`, `discounted_total` | Line total, before and after discounts |
| `tax_lines` | A list, each with `title`, `rate`, `price` |
| `image_url` | The line item's image, when it has one |

A document carries up to 250 line items of an order.

## Product

For templates that render **A product**.

| Variable | What it holds |
| --- | --- |
| `product.title`, `product.handle`, `product.vendor`, `product.product_type` | Text |
| `product.description_html` | The description as you wrote it, as HTML. Print it with the `raw` filter |
| `product.tags`, `product.status` | Tags and status, for example ACTIVE |
| `product.created_at`, `product.updated_at` | Dates |
| `product.url` | The product page in your online store, when it is published there |
| `product.image_url` | The featured image |
| `product.options` | A list, each with `name` and `values` |
| `product.currency`, `product.min_price`, `product.max_price` | The price range |
| `product.variants` | A list, each with `id`, `title`, `sku`, `barcode`, `price`, `compare_at_price`, `inventory_quantity`, `options` (each `name` and `value`) and `image_url` |

A document carries up to 100 variants of a product.

## Customer

For templates that render **A customer**.

| Variable | What it holds |
| --- | --- |
| `customer.name`, `customer.first_name`, `customer.last_name` | Name |
| `customer.email`, `customer.phone` | Contact details |
| `customer.note`, `customer.tags` | Note and tags |
| `customer.locale` | The customer's language |
| `customer.created_at` | Date |
| `customer.address` | The default address, with the same fields as an order address |
| `customer.orders_count`, `customer.amount_spent`, `customer.currency` | Totals |
| `customer.orders` | The 20 most recent orders, newest first, each with `id`, `name`, `created_at`, `financial_status`, `fulfillment_status`, `total_price` |

## The brand object

Your branding from **Settings** is available in every template, as the `brand` object and as CSS variables. See [Branding your documents](https://docs.workflow-documents.app/branding.md) for where the values come from.

| Variable | What it holds |
| --- | --- |
| `brand.logo_url`, `brand.logo_width` | Your logo and the width it is printed at. `logo_url` is empty when no logo is uploaded |
| `brand.company_name`, `brand.address`, `brand.tax_id`, `brand.footer_text` | Your company details. Empty text when a field is not filled |
| `brand.primary_color`, `brand.accent_color`, `brand.text_color`, `brand.muted_color`, `brand.border_color` | Hex colors |
| `brand.font_family`, `brand.heading_font_family`, `brand.font_size` | Body font, heading font and base font size |

In CSS, use the variables instead: `var(--brand-primary)`, `var(--brand-accent)`, `var(--brand-text)`, `var(--brand-muted)`, `var(--brand-border)`, `var(--brand-font)`, `var(--brand-heading-font)` and `var(--brand-font-size)`. The page already uses your body font, base size and text color, and your heading font on headings, so a template only needs them for its own accents.

```liquid
{% if brand.logo_url %}
  <img src="{{ brand.logo_url }}" width="{{ brand.logo_width }}" alt="">
{% else %}
  <strong>{{ brand.company_name | default: shop.name }}</strong>
{% endif %}

<p>{{ brand.address | br }}</p>
```

## Filters

Amounts are plain numbers and text is escaped, so three filters of our own do the formatting:

| Filter | What it does | Example |
| --- | --- | --- |
| `money` | Formats an amount as money, in the currency of the order, product or customer and for the language of the document | `order.total_price` with `money` prints an amount such as 64.70 with the currency symbol |
| `money: "USD"` | The same with a currency code you choose. It only changes the format, it does not convert the amount | |
| `percent` | Turns a rate into a percentage | A tax rate of 0.19 prints as 19% |
| `br` | Keeps the line breaks of a text with several lines | `brand.address` with `br` prints one line per row |
| `raw` | Prints HTML as it is, without escaping | `product.description_html` with `raw` |

The standard Liquid filters work too, for example `date`, `default`, `upcase`, `plus`, `times`, `size`, `join` and `first`.

```liquid
{{ order.processed_at | date: "%Y-%m-%d" }}

{% for tax in order.tax_lines %}
  {{ tax.title }} {{ tax.rate | percent }}: {{ tax.price | money }}
{% endfor %}

{{ product.description_html | raw }}
```

> [!NOTE]
> **Output is escaped**
> Everything a template prints is HTML-escaped, so a customer name or an order note cannot break your layout. Use `raw` only for values you trust to be HTML, such as your own product description.

## The language variable

`language` holds the language the document is made for: the Language of the Flow action when it is filled, else the language of the order's customer or of the customer, else the template's main language. Amounts are formatted for it.

```liquid
{% if language == "de" %}
  <h1>Rechnung {{ order.name }}</h1>
{% else %}
  <h1>Invoice {{ order.name }}</h1>
{% endif %}
```

For more than a few words, give each language its own HTML instead. See [Templates in several languages](https://docs.workflow-documents.app/languages.md).

## What Liquid in a template does not support

A document template is one self-contained HTML file. Some things you may know from Shopify themes are not available:

- **No includes, partials or layouts.** The tags `render`, `include`, `layout` and `block` are refused when you save.
- **No theme objects or theme filters.** Only the data on this page exists. An unknown filter is reported as an error with its line, it is not ignored.
- **No scripts.** JavaScript in a template does not run, neither in the preview nor in the PDF.
- **Images need absolute URLs**, for example a file from your Shopify Files or `brand.logo_url`.
- **Limits.** The HTML can be up to 200 KB and the CSS up to 100 KB, and a template has 3 seconds to render.

A variable that does not exist prints nothing. Check values that can be empty with `if` before you print a label for them.

## Next steps

- [The template editor](https://docs.workflow-documents.app/template-editor.md) - where you write and preview the template.
- [Branding your documents](https://docs.workflow-documents.app/branding.md) - the values behind the brand object.
- [Troubleshooting](https://docs.workflow-documents.app/troubleshooting.md) - template errors and missing data.
