Skip to main content
Most business logic in an integration is one sentence: skip the variable salary if the employee had a sick day this month, treat anyone earning over 40 000 as senior, pro-rate the salary by the days worked. An expression lets you write that sentence as one line of text instead of a graph of nodes, and it reads the way a person would. Expressions work in any data engine (a property mapping, a sync condition, an inner graph) and in flows. When a flow’s decision node evaluates an expression to choose what happens next, the node calls it a rule; the language is the same everywhere. Three separate expressions, each a complete decision or calculation on its own:
True when at least one absence in the absences list is sick leave or care of a sick child and falls inside the month held in period. Use it on a Flow Guard to skip the variable-salary sync.
Yields one of two branch names for a Flow Switch, reading salary_amount from the switch’s subject (the employee).
Calculates a pro-rated salary for a Flow Transform: the salary times the days worked, divided by the number of days in the period. Expressions are available in two places, backed by the same language:
  • The Expression node, which brings the language into any data engine: a property mapping, a sync condition, an inner graph.
  • Rule mode on the flow decision nodes. Flow Switch, Flow Filter and Flow Transform have a Decide by setting, Rule or Inner engine; Flow Guard has a rule holds option under Bail when. In rule mode the node’s inner graph is kept but not used, so you can switch back without losing anything.

Names

A rule reads values by name. A name resolves, in this order, to:
  1. it — the current item inside a where (see below).
  2. A field of that item, when inside a where.
  3. A wired port of the Expression node (input, a, b, c, d).
  4. An attribute of the subject — the node’s Subject assign, the guard’s Assign, the filter’s current item (which is also it in a filter rule), or the data engine’s record. salary_amount on an employee subject reads their salary.
  5. A flow assign — employee, absences, period, whatever earlier nodes wrote.
Dotted paths walk into a value: employee.salary_amount, period.from, it.start_date. A custom property has a quoted name: employee."Cost Center", or subject."Cost Center" when you are reading the subject directly. subject always means the subject; today is the date the rule runs on. A name the rule cannot find is an error, never a silent empty value, so a typo cannot pass as “no data”. A field that exists but holds nothing is nil, because missing data is normal.

Dated properties

An employee’s salary has a history. Reading salary_amount gives you the value effective today. To pick another moment, use @: These are the same moments the Assert, Any and Refute nodes offer, so a rule and a guard next to it mean the same thing by the same words.

When an entry starts

Every entry in a history starts on a date. valid_from(x) reads that date instead of the value, and entry_id(x) reads the entry’s id (the id that keeps, for example, two employments apart). Put the @ after the call to choose the entry: An entry without a start date has held since forever, so valid_from gives nothing for it, and nothing is neither before nor after any date. To keep syncing employees for two months after they leave:

Comparing

=, !=, <, <=, >, >=, in, not in, and ~= (a case-insensitive pattern). and, or, not combine them; parentheses group. A ~= pattern reads Swedish and other non-English letters as letters: "måleri" matches “MÅLERI”, and . stands for one letter, so "m.leri" matches “Måleri”. Comparison is forgiving where source data is messy: 123 = "123", 1 = 1.0, "sick_leave" matches the stored code whether it arrived as text or a symbol, and a date compares with its 2026-01-01 text. x = blank is true for nothing, an empty text, or an empty list. Ordering against a missing value is simply false. Ordering a text against a number is an error. in takes a list (x in ["a", "b"]), a date range (it.start_date in period), or a text ("ab" in "cabin"). from .. to builds a date range from two dates.

Lists: where and it

absences where twine_time_type = "sick_leave" keeps the items of a list for which the condition holds. Inside the condition the item’s fields are in scope by their bare names, it is the item itself, and other assigns are still reachable. For a list of employees those bare names are the employee’s properties, the same names the property mapper shows: employees where salary_amount > 40000, employees where employment_terminated = false and "Cost Center" = "1200" (a custom property in quotes). A record’s base fields are reachable too, after its properties: id, active_status, manager_id. The @ selectors work on them as anywhere else: employees where salary_amount @ period.from > 40000. Functions summarise a list: count(...), any(...), all(...), sum(...), min(...), max(...), avg(...), first(...), last(...). A field read on a list reads it on every item, so sum(absences.minutes) adds up every day of every absence. A dotted path also works on a function’s result: first(absences where twine_time_type = "sick_leave").start_date reads the start date of the first sick-leave absence. first and last keep the list’s own order. On an empty list they give nothing, and so does any field read on that nothing.

Choosing and calculating

if condition then value else value picks one of two values; chain with else if. A Flow Switch rule yields the name of the branch to take:
A rule that yields true or false picks the branch named so. Arithmetic uses +, -, *, /. A calculation on a missing value is missing; dividing by zero is an error.

Functions

The editor’s Names and functions panel under every rule field lists them all. The ones you will reach for most:
  • Dates: days_between(a, b), add_days(d, n), add_months(d, n), start_of_month(d), end_of_month(d), year(d), month(d), day(d), weekday(d), date(text).
  • Ranges: range(from, to), days(range), overlaps(a, b), within(x, range), days_in(x, range). A record with start and end dates (an absence, a schedule) counts as a range, so overlaps(it, period) works directly on an absence.
  • Text: trim, lower, upper, starts_with, ends_with, replace, matches, concat, to_string.
  • Numbers: round(n, digits), floor, ceil, abs, div, mod, number(text).
  • Fallbacks: coalesce(a, b, ...) gives the first value that is not blank; is_blank(x).
  • History: valid_from(x) and entry_id(x), see When an entry starts.

Errors

The editor checks a rule as you type and shows the problem with a caret under the offending text. A rule that does not compile still saves; it is listed in the Problems pane, and at run time the node treats it the way it treats a failing inner graph, according to its On inner error setting. Everything is logged; a rule never fails quietly.