{{ ... }} (callback URL, HTTP body, email subject, transform output, validation rule), you can reference an upstream value, index into arrays, transform it with filters, or insert a built-in helper.
Basic syntax
A template expression has three parts: a path, optional filters, and the surrounding{{ }} markers.
{{ ... }} token.
Path
A path is a dotted identifier, optionally with array indices.
The first segment of every path must be the name of an upstream node (or a built-in placeholder or helper, see below). Subsequent segments index into that node’s output, which has the shape
{ success, payload, metadata, error }. Paths are case sensitive and may use letters, digits, and underscores.
Each node in the editor has a Name field. Default names are auto-assigned (
extract, extract2, vars, etc.) but you can rename a node to anything that’s unique within the pipeline. Whatever name you pick is the identifier used at the head of every template expression.Built-in placeholders
These resolve from the run context:Source document file
The$document head resolves from the run’s source document: the original file that triggered the run, whether it was uploaded or emailed. It is a global head, valid in any template field, because a pipeline can have several triggers but only one fires per run.
The metadata paths (
name, contentType, size, extension) are always cheap to resolve. base64 is different: the file bytes are fetched and encoded lazily, only when a template actually references {{$document.file.base64}}, and they are never persisted. The encoded value never enters run output, step history, or the live preview. Anywhere it would otherwise appear, Ingestly shows a [binary N bytes, type] placeholder instead.
Use {{$document.file.base64}} to send the source file as a binary body on a callback or HTTP node, or to attach it to a record.
A node name can never start with
$, so the $document head never collides with a node you have named document.Built-in helpers
These produce a value at the time the template is evaluated.
You can chain filters onto helpers:
{{now | formatDate: 'yyyy-MM-dd'}}.
Filters
Add a filter with the pipe character:{{ value | filter }}. Filters with arguments use a colon: {{ value | filter: 'arg1', 'arg2' }}. You can chain as many filters as you need, applied left to right.
Filter arguments
Each argument is one of:
Path references must satisfy the same shape as the head path (
identifier(.identifier|[N])*). The runtime resolves them at filter-application time, so they can reference upstream node fields (extract.payload.divisor), built-in helpers ({{end | dateDiff: 'days', start}}), or array elements ({{items[0] | concat: items[1]}}). If the path resolves to a type that doesn’t match what the filter expects (e.g., div argument resolves to a string), the editor flags it before the run starts.
String filters
truncate counts the postfix toward the limit; padStart and padEnd pad with a space when no pad string is given.
Number filters
Format filters
Turn a raw number into a display string.formatCurrency takes an ISO 4217 currency code (for example USD, EUR, GBP) and pins the currency symbol from that code.
Array filters
slice uses Python-style bounds (negative indices count from the end). groupBy groups an array of objects by the string value at key, returning an array of { key, items } groups. reverse also reverses a string’s characters.
Date filters
Operate on ISO 8601 strings (or anything that can be parsed as one). The add/subtract and start/end filters return an ISO 8601 UTC string.formatDate uses .NET-style format strings. Common patterns: yyyy-MM-dd, yyyy-MM-ddTHH:mm:ssZ, MM/dd/yyyy, HH:mm. Negative arguments to the add* filters subtract.
Encoding filters
General filters
Array constructs
Beyond the filters above, four constructs transform arrays and pick branches. They use the same| syntax but are written by name.
Live preview
When you edit a template field in a node editor, Ingestly shows a live preview of the resolved value. Select a document from the run toolbar so the preview evaluates against that document’s most recent run data. This lets you confirm a path and its filter chain produce the value you expect before you run the pipeline. Without a selected document, the editor still validates the path and filter names but cannot show a resolved value.Type-aware autocomplete
When you type inside a{{ ... }} field in a node editor, Ingestly suggests:
- Upstream node fields at the start of the expression (after
{{) - Built-in helpers (
now,today,uuid,nowUnix) alongside fields - Filters after a
|, scoped to the type of the preceding value (an array path showslength,first,join; a string path showsupper,lower,trim) - Filter arguments as snippet placeholders you can tab through
Inside JSON bodies
When you write a template inside a JSON value position, like"qty": "{{payload.qty}}", Ingestly emits the resolved value as raw JSON so types are preserved. A number stays unquoted, an object stays structured, an array stays an array.
Filter arguments inside a JSON-position template should use single quotes (
'a') rather than double quotes, to avoid clashing with the surrounding JSON string.Row expansion in JSON bodies (when)
A JSON body can fan out over a runtime array. Inside any JSON array, an object that carries a$each key is a row template: it is repeated once per element of the referenced array, and the $each key itself is removed from the output.
{{item}}is the current array element, and{{item.field}}reads a field from it{{index}}is the zero-based position- Values that are exactly one
{{item...}}or{{index}}token keep their type (a number stays a number, an object stays structured) - Tokens that mix text and bindings, like
"row {{index}}: {{item.mark}}", substitute as text - Other
{{node.path}}tokens are left for normal substitution
$when key to filter rows. The condition supports ==, !=, a bare truthiness test, and ! for negation:
__label) become item lines and reconcile-classified surcharge rows become a different line shape. A reconcile node stamps classified extra rows with __label, so this pairs naturally with Expected extras.
An object with only $when (no $each) is included or dropped as a whole. Row templates nest: a field inside a row can hold another array with its own $each over {{item.subArray}}, and the inner template’s item refers to the inner element.
Row expansion applies to the HTTP action and callback body fields when they parse as JSON. A $each reference that does not resolve to an array contributes no rows.
Common recipes
Last item from an extracted line items array, with a fallback:yyyy-MM-dd string:
When a template doesn’t resolve
If the path can’t be resolved (the field is missing, the upstream node didn’t run, the index is out of bounds), Ingestly substitutes an empty string. Usedefault: '...' to provide a fallback.
If the filter name is unknown or a filter call is malformed (wrong arg count, bad argument type), the substitution yields an empty string and the node surfaces a validation error.