Templates
Create dynamic notification content with Liquid templating
Templates define the content of your notifications. Notiflows uses Liquid templating to insert dynamic content into your notifications.
Variable Contexts
Three variable contexts are available in every template:
| Context | Description | Example |
|---|---|---|
recipient.* | Recipient user attributes | {{ recipient.first_name }} |
actor.* | User who triggered the notification | {{ actor.email }} |
data.* | Custom payload from the API call | {{ data.order_id }} |
These contexts are populated from the data you pass when running a notiflow.
Basic Syntax
Insert variables with double curly braces:
Hi {{ recipient.first_name }},
{{ actor.first_name }} left a comment on your post.Filters
Transform values with filters:
{{ recipient.first_name | capitalize }}
{{ data.price | money }}
{{ data.created_at | date: "%B %d, %Y" }}Conditionals
Use conditional logic:
{% if data.discount %}
You saved {{ data.discount }}!
{% endif %}
{% if recipient.custom_fields.plan == "premium" %}
As a premium member, you get early access.
{% else %}
Upgrade to premium for early access.
{% endif %}Loops
Iterate over arrays:
Your items:
{% for item in data.items %}
- {{ item.name }}: {{ item.price }}
{% endfor %}Template Formats by Channel
Email templates support three content types:
- Visual — Drag-and-drop editor for rich HTML emails
- HTML — Raw HTML with full control over layout
- Plaintext — Simple text emails
Subject: Order #{{ data.order_id }} confirmed
Hi {{ recipient.first_name }},
Thanks for your order! Here's what you purchased:
{% for item in data.items %}
- {{ item.name }} ({{ item.quantity }}x)
{% endfor %}
Total: {{ data.total }}SMS
SMS templates use plaintext only:
Hi {{ recipient.first_name }}, your order #{{ data.order_id }} has shipped! Track it: {{ data.tracking_url }}Mobile Push
Mobile push templates have a title and body field (both plaintext):
Title: New message from {{ actor.first_name }}
Body: {{ data.message_preview }}Web Push
Web push templates have a title and body (both plaintext), plus optional icon, image, and action URL fields:
| Field | Required | Example |
|---|---|---|
| Title | Yes | {{ actor.first_name }} commented on your post |
| Body | Yes | {{ data.comment_preview }} |
| Icon URL | No | https://yourapp.com/icon.png |
| Image URL | No | {{ data.post_image_url }} |
| Action URL | No | https://yourapp.com/posts/{{ data.post_id }} |
In-App
In-app templates have a body (markdown) and an optional action URL:
Body: **{{ actor.first_name }}** commented on your post: "{{ data.comment_preview }}"
Action URL: {{ data.post_url }}Chat (Slack)
Chat templates support markdown or JSON for rich formatting:
Markdown:
*New order #{{ data.order_id }}*
Customer: {{ actor.first_name }} {{ actor.last_name }}
Total: {{ data.total }}JSON (Slack Block Kit):
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Order #{{ data.order_id }}*\nCustomer: {{ actor.first_name }}"
}
}
]
}Digest Templates
When using a digest step, templates have access to the collected events via the digest context:
| Variable | Description |
|---|---|
digest.total_items | Number of events collected in the digest window |
digest.items | Array of collected events |
digest.items[].data | The original notification data payload |
digest.items[].actor | The actor who triggered the event (id, external_id, email, first_name, last_name) |
digest.items[].recipient | The recipient of the notification (same fields as actor) |
You have {{ digest.total_items }} new comments:
{% for item in digest.items %}
- {{ item.actor.first_name }}: {{ item.data.message }}
{% endfor %}