Pipory
Concepts

Expressions

The {{ }} expression language - how data flows between nodes, and the 91 functions you can call inside an expression.

Any field with the expression marker accepts {{ }} expressions. Inside the braces you can read anything the run has produced so far, and transform it with the functions below.

Reading data

An expression's simplest form is a path into the run context:

{{webhook.email}}
{{lookup.httpResponse.status}}
{{vars.apiBase}}

Every node writes its output under its own variableName, so a node named lookup is readable downstream as {{lookup...}}. See Workflows for the full context model.

Start typing {{ in any expression field and the editor offers the data your upstream nodes actually produced, plus every function on this page, with its call shape and a worked example. Hovering a finished expression shows the value it currently resolves to.

Calling a function

A function is written first inside the braces, with its arguments after it, separated by spaces - not commas, and not parentheses around the whole call:

{{upper contact.name}}
{{dateFormat order.createdAt "yyyy-MM-dd"}}
{{join (pluck order.lineItems "sku") ", "}}

Nest a call inside another by wrapping it in parentheses, as pluck is above. String arguments need quotes; numbers and paths do not.

Two rules worth knowing:

  • Your data always wins. If a function shares a name with one of your fields, a bare {{sum}} still resolves your field. The function only takes over when you call it with an argument: {{sum order.lineItems "amount"}}. Adding functions can never change what an existing workflow resolves to.
  • Nothing throws. A function given something it cannot use resolves to an empty string rather than failing the run, the same as a malformed template or a path that doesn't exist.

Conditionals and loops

Handlebars' own block syntax works everywhere too, and the comparison functions below are what you put inside it:

{{#if (gt order.total 100)}}Free shipping{{else}}Standard shipping{{/if}}
{{#each order.lineItems}}{{this.sku}} x{{this.qty}}
{{/each}}

Function reference

Text

FunctionWhat it doesExampleResult
upper textUppercases the text.{{upper contact.name}}ADA LOVELACE
lower textLowercases the text.{{lower contact.email}}ada@example.com
trim textRemoves leading and trailing whitespace.{{trim form.comment}}looks good
capitalize textUppercases the first character only.{{capitalize status}}Shipped
titleCase textUppercases the first letter of every word.{{titleCase product.name}}Blue Widget
replace text search replacementReplaces every occurrence of a substring.{{replace order.sku "-" "_"}}SKU_1024
split text separatorSplits text into an array on a separator.{{split tags ","}}[urgent, billing]
substring text start [end]Characters between two zero-based positions.{{substring order.id 0 8}}ord_9f2a
truncate text length [suffix]Shortens text, appending "…" (or your suffix) if it was cut.{{truncate issue.body 80}}The checkout page throws a 500 when the cart is…
padStart text length [char]Pads the front of the text to a fixed width.{{padStart invoice.number 6 "0"}}000042
padEnd text length [char]Pads the end of the text to a fixed width.{{padEnd label 10 "."}}Total.....
repeat text countRepeats the text n times.{{repeat "-" 20}}--------------------
concat a b [c…]Joins any number of values into one string.{{concat contact.first " " contact.last}}Ada Lovelace
slugify textLowercase, hyphenated, URL-safe form of the text.{{slugify post.title}}shipping-the-new-editor
urlEncode textPercent-encodes the text for use in a URL or query string.{{urlEncode search.term}}blue%20widget
urlDecode textReverses percent-encoding.{{urlDecode webhook.query.q}}blue widget
base64Encode textBase64-encodes the text (UTF-8 safe).{{base64Encode (concat user ":" token)}}YWRhOnNlY3JldA==
base64Decode textDecodes base64 back to text.{{base64Decode payload.data}}hello
stripHtml htmlRemoves HTML tags, leaving the text content.{{stripHtml email.bodyHtml}}Your order has shipped
startsWith text prefixTrue when the text begins with the prefix.{{#if (startsWith email "admin@")}}internal{{/if}}internal
endsWith text suffixTrue when the text ends with the suffix.{{#if (endsWith file.name ".pdf")}}pdf{{/if}}pdf
matches text pattern [flags]True when the regular expression matches the text.{{#if (matches phone "^\+1")}}US{{/if}}US
regexExtract text pattern [group]Returns the first regex match, or a capture group of it.{{regexExtract subject "#(\d+)" 1}}4821
regexReplace text pattern replacement [flags]Regex replace; defaults to global. Use "$1" for groups.{{regexReplace sku "[^0-9]" ""}}1024

Numbers

FunctionWhat it doesExampleResult
add a b [c…]Adds numbers together.{{add order.subtotal order.shipping}}42.5
subtract a bSubtracts b from a.{{subtract order.total order.discount}}37.5
multiply a b [c…]Multiplies numbers together.{{multiply item.price item.quantity}}59.97
divide a bDivides a by b; dividing by zero gives an empty value.{{divide total count}}12.5
modulo a bRemainder of a divided by b.{{modulo index 2}}1
round value [decimals]Rounds to the nearest whole number, or to n decimals.{{round score 1}}8.7
floor valueRounds down to a whole number.{{floor cart.weight}}3
ceil valueRounds up to a whole number.{{ceil cart.weight}}4
abs valueAbsolute value.{{abs balance.delta}}19.99
toFixed value decimalsFormats a number with a fixed number of decimals.{{toFixed order.total 2}}37.50
toNumber valueParses text into a number ("1,024" reads as 1024).{{toNumber webhook.body.amount}}1024
min a b [c…] | min arraySmallest of the values, or of an array.{{min prices}}4.99
max a b [c…] | max arrayLargest of the values, or of an array.{{max prices}}29.99
sum array [field]Adds up an array, optionally a field on each item.{{sum order.lineItems "amount"}}59.97
avg array [field]Mean of an array, optionally of a field on each item.{{round (avg reviews "rating") 2}}4.35
clamp value min maxKeeps a number inside a range.{{clamp quantity 1 10}}10

Dates and times

FunctionWhat it doesExampleResult
nowThe current time as an ISO 8601 string. Write it as "(now)" when passing it to another helper.{{dateFormat (now) "date"}}2026-07-27
dateFormat date pattern [timezone]Formats a date. Patterns: date-fns tokens, or "date"/"time"/"datetime"/"iso"/"us"/"eu"/"long"/"human".{{dateFormat order.createdAt "human" "America/New_York"}}27 Jul 2026, 05:15
dateAdd date amount unitAdds time to a date. Units: year, month, week, day, hour, minute, second.{{dateFormat (dateAdd (now) 3 "days") "date"}}2026-07-30
dateSubtract date amount unitSubtracts time from a date. Same units as dateAdd.{{dateFormat (dateSubtract (now) 1 "week") "date"}}2026-07-20
dateDiff from to [unit]Whole units between two dates, counting forward from "from". Defaults to days.{{dateDiff subscription.startedAt (now) "days"}}34
startOf date unitStart of the containing year / month / week / day / hour / minute, in UTC.{{dateFormat (startOf (now) "month") "date"}}2026-07-01
endOf date unitEnd of the containing period, in UTC. Same units as startOf.{{dateFormat (endOf (now) "month") "date"}}2026-07-31
toIso dateConverts any recognisable date into ISO 8601.{{toIso webhook.body.timestamp}}2026-07-27T09:15:00.000Z
toUnix dateUnix timestamp in seconds.{{toUnix (now)}}1785316500
fromUnix secondsTurns a unix timestamp into an ISO date.{{dateFormat (fromUnix stripe.created) "datetime"}}2026-07-27 09:15:00
isBefore a bTrue when date a is earlier than date b.{{#if (isBefore trial.endsAt (now))}}expired{{/if}}expired
isAfter a bTrue when date a is later than date b.{{#if (isAfter invoice.dueAt (now))}}not due yet{{/if}}not due yet

Arrays

FunctionWhat it doesExampleResult
length valueItem count of an array, characters of a string, keys of an object.{{length order.lineItems}}3
first array [count]First item, or the first n items.{{first search.results}}{ id: 1 }
last array [count]Last item, or the last n items.{{last messages}}{ id: 9 }
join array [separator]Joins an array into text. Separator defaults to ", ".{{join (pluck contacts "email") "; "}}ada@example.com; alan@example.com
pluck array fieldPulls one field out of every item into a new array.{{pluck order.lineItems "sku"}}[SKU-1, SKU-2]
where array field valueKeeps the items whose field equals a value.{{length (where issues "state" "open")}}7
sortBy array [field] ["asc"|"desc"]Sorts an array, optionally by a field on each item.{{first (sortBy deals "amount" "desc")}}{ amount: 92000 }
unique array [field]Removes duplicates, comparing a field when given one.{{length (unique orders "customerId")}}12
reverse arrayReverses the order of an array.{{join (reverse steps)}}three, two, one
slice array start [end]A sub-range of an array, by zero-based position.{{slice search.results 0 5}}[…5 items]
flatten array [depth]Flattens nested arrays into one.{{join (flatten (pluck orders "tags"))}}urgent, billing, vip
compact arrayDrops empty, null and undefined items.{{join (compact (pluck contacts "phone"))}}+15551234567
chunk array sizeSplits an array into batches of a fixed size.{{length (chunk recipients 50)}}4
range start end [step]An array of numbers from start up to (not including) end.{{#each (range 1 4)}}{{this}} {{/each}}1 2 3
contains haystack needleTrue when an array holds the item, or text holds the substring.{{#if (contains issue.labels "bug")}}triage{{/if}}triage
indexOf haystack needleZero-based position of an item or substring, or -1.{{indexOf tags "urgent"}}0

Objects

FunctionWhat it doesExampleResult
get object path [fallback]Reads a nested path ("a.b[0].c"), with an optional fallback.{{get webhook.body "customer.address.city" "unknown"}}Lisbon
has object pathTrue when the path exists and is not null.{{#if (has payload "customer.vatId")}}b2b{{/if}}b2b
keys objectThe object's own property names, as an array.{{join (keys webhook.body)}}id, email, plan
values objectThe object's values, as an array.{{join (values counts)}}3, 7, 12
pick object key [key…]A copy of the object with only the named keys.{{json (pick contact "email" "name")}}{ "email": "…", "name": "…" }
omit object key [key…]A copy of the object without the named keys.{{json (omit webhook.body "password")}}{ "email": "…" }
json valuePretty-printed JSON, safe to drop into a request body.{{json webhook.body}}{ "id": 1 }
stringify valueCompact single-line JSON.{{stringify order.lineItems}}[{"sku":"SKU-1"}]
jsonParse text [path]Parses JSON text, optionally reading a path out of it.{{jsonParse http.body "data.0.id"}}42
typeOf valueOne of "string", "number", "boolean", "array", "object", "null".{{typeOf webhook.body.items}}array

Logic and comparison

FunctionWhat it doesExampleResult
eq a bTrue when the values match ("5" and 5 count as equal).{{#if (eq order.status "paid")}}fulfil{{/if}}fulfil
ne a bTrue when the values differ.{{#if (ne user.plan "free")}}priority{{/if}}priority
gt a bTrue when a is greater than b.{{#if (gt order.total 100)}}free shipping{{/if}}free shipping
gte a bTrue when a is greater than or equal to b.{{#if (gte score 90)}}A{{/if}}A
lt a bTrue when a is less than b.{{#if (lt stock 5)}}reorder{{/if}}reorder
lte a bTrue when a is less than or equal to b.{{#if (lte age 17)}}minor{{/if}}minor
and a b [c…]True when every value is truthy.{{#if (and user.email user.consent)}}send{{/if}}send
or a b [c…]True when any value is truthy.{{#if (or issue.urgent issue.escalated)}}page{{/if}}page
not valueInverts a value's truthiness.{{#if (not user.verified)}}remind{{/if}}remind
ifElse condition whenTrue whenFalseInline choice between two values.{{ifElse user.name user.name "there"}}there
coalesce a b [c…]The first value that is not empty, null or undefined.{{coalesce contact.nickname contact.firstName contact.email}}ada@example.com
fallback value replacementUses the replacement when the value is empty.{{fallback lead.company "Unknown company"}}Unknown company
isEmpty valueTrue for empty text, empty arrays, empty objects, null and undefined.{{#if (isEmpty search.results)}}nothing found{{/if}}nothing found