Transforms are pure, deterministic, local, and free. Parameters sit flat on
the step alongside id, transform, skip_when, and fail_on — there is
no wrapper object. Use the parameter names below exactly. A trailing ?
marks an optional parameter; {{item.*}} is valid only inside map
expressions and foreach iterations.
{ "id": "days", "transform": "date_diff",
"from": "{{step.deal.last_activity_date}}", "to": "now", "unit": "days" }#Collections
| Transform | Parameters | Returns |
|---|---|---|
filter |
array, field, operator, value? — or conditions[] with mode? (all/any) |
array |
sort |
array, fields[] of { field, direction } |
array |
map |
array, expression — or apply + the applied transform's params; output_field? |
array |
reduce |
array, field, operator, initial? |
value |
find |
array, field, operator, value |
first matching item |
pluck |
array, field |
array of values |
sum |
array, field |
number |
min_by / max_by |
array, field |
single item |
unique |
array, field |
array |
flatten |
array, depth |
array |
slice |
array, start, end |
array |
group_by |
array, field, aggregate? |
object — or array with aggregate |
partition |
same parameters as filter |
{ matched, unmatched } |
to_list |
input (an object) |
array of { key, value } |
count |
input |
number |
join |
array, separator |
string |
enrich_by |
array, with, key, with_key?, fields?, prefix? |
array — each item joined to its match in with on the key |
reduce operators: sum, count, avg, min, max, concat, or,
and. filter conditions default to AND; mode: "any" makes them OR.
map has three expression forms — a single ref (extracts, preserving type),
a math expression over {{item.*}} (+ - * / %, min, max, abs,
ceil, floor, round), or an object template. output_field attaches the
result to each item instead of replacing it:
{ "id": "weighted", "transform": "map", "array": "{{step.deals}}",
"expression": "{{item.amount}} * {{item.stage_probability}} / 100",
"output_field": "weighted_amount" }group_by with aggregate computes per group and returns an array:
{ "id": "by_stage", "transform": "group_by",
"array": "{{step.deals}}", "field": "stage",
"aggregate": {
"count": { "operator": "count" },
"total": { "field": "amount", "operator": "sum" }
} }Without aggregate it returns { key: [items] } — chain to_list then
map to iterate groups.
#Objects
| Transform | Parameters | Returns |
|---|---|---|
merge |
sources[], strategy (shallow / deep / concat) |
object |
pick |
source (ref) + fields[] — or source as an object template |
object |
omit |
source, fields[] |
object |
rename |
source, mapping (old → new) |
object |
set |
source, field, value |
object |
prefix_keys |
source, prefix |
object with every key prefixed |
project |
object, expression (an object template) |
one record reshaped — the single-object analogue of map |
#Strings
| Transform | Parameters | Returns |
|---|---|---|
template |
template (a string with {{refs}}) |
string |
concat |
values[] |
string |
split |
input, delimiter |
array |
replace |
input, pattern, replacement, all |
string |
truncate |
input, max_length, suffix |
string |
strip_html |
input |
plain-text string |
lowercase / uppercase / trim |
input |
string |
starts_with / ends_with |
input, prefix / suffix |
boolean |
contains_any |
input, terms[], case_sensitive |
boolean — true if any term appears |
string_length |
input |
number of characters |
sha256 |
input |
hex digest — use it to derive a stable id from inputs |
encode_base64 / decode_base64 |
input |
string |
#Encoding and size
Useful when an operation has a payload limit, or when you need a deterministic id so that re-running is a replay rather than a duplicate.
| Transform | Parameters | Returns |
|---|---|---|
json_stringify |
input |
JSON string |
json_parse |
input |
parsed value |
json_byte_length |
input |
byte length of the value as JSON |
utf8_byte_length |
input |
byte length of a string in UTF-8 |
#Numbers
| Transform | Parameters | Returns |
|---|---|---|
to_number |
input |
number |
math |
left, operator, right? |
number |
round |
input, precision |
number |
clamp |
input, min, max |
number |
percent |
value, total, precision |
decimal (display with :percent) |
weighted_score |
scores[] of { value, weight }, clamp? [min, max], precision? |
number |
math operators: add, subtract, multiply, divide, modulo, and the
unary abs, ceil, floor.
#Dates
| Transform | Parameters | Returns |
|---|---|---|
date_diff |
from, to (accepts "now"), unit |
number |
date_format |
date, format |
string |
date_add |
date, amount, unit |
ISO 8601 string |
date_parse |
input, input_format |
ISO 8601 string |
is_past / is_future |
date |
boolean |
date_period |
period |
{ start, end } ISO strings |
Units: days, hours, minutes, seconds. date_period names: today,
yesterday, this_week, last_week, this_month, last_month,
this_quarter, last_quarter, this_year, last_year, last_7_days,
last_30_days, last_90_days, next_7_days, next_30_days — inclusive
UTC day bounds, ISO weeks, computed from the run clock so scheduled runs stay
deterministic.
#Timestamps
| Transform | Parameters | Returns |
|---|---|---|
to_recent_date |
input (a number) |
The value normalised to Unix milliseconds, or null when it is not a recent timestamp. Gate the result with is_null; it never guesses |
#Logic
| Transform | Parameters | Returns |
|---|---|---|
compare |
left, operator (any of the 14), right? |
boolean |
any / all |
values[] (booleans) — or conditions[] (condition strings) |
boolean |
coalesce |
values[] |
first non-null |
default |
value, fallback |
value, or fallback when null/empty-string |
not |
input |
inverted boolean |
ternary |
if, then, else? |
one of two values |
switch |
input, cases (map), default |
mapped value |
pluralize |
count, zero?, one, many |
selected branch string |
default treats the empty string as missing but preserves 0 and false —
use it over coalesce when a templated value may interpolate to "".
#Privacy
| Transform | Parameters | Returns |
|---|---|---|
hash_replace |
data, fields[] |
{ data, mapping } |
hash_restore |
data, mapping |
restored object/string |
redact |
data, fields[] (paths, [*] allowed), marker |
redacted object |
hash_replace before an AI step and hash_restore after it keeps chosen
fields out of the model's sight; the mapping never leaves the run.
#Display
| Transform | Parameters | Returns |
|---|---|---|
to_checklist |
title, items[] |
checklist object |
to_table |
array, columns[] of { field, label, format? } |
table object |
to_summary |
fields[] of { label, value } |
summary object |
to_csv |
array, columns?, header?, delimiter? |
CSV string |
to_slack_blocks |
blocks[] |
Slack Block Kit payload, for a notification step that posts to Slack |
Checklist items are { label, issue, detail_ok, detail_issue, detail_null }
where issue is a condition string — true renders as a problem, false as
fine, null as unknown. Table column formats: text, currency, number,
date, relative_date, boolean, percent. Checklist items and table
columns (type: "action") may carry action descriptors; actions always
render as owner-clicked controls that open a normal, still-governed recipe
run — never a silent execution. to_csv escapes per RFC 4180; pair it with a
file write to produce a spreadsheet-openable artifact.
#Warehouse
enrichment-or-fetch is the read-through helper for precomputed facts: it
returns the stored enrichment when one exists, is fresh, and meets the trust
floor — and signals your fallback step to fetch fresh otherwise.
| Parameter | Meaning |
|---|---|
ref |
Dotted enrichment ref, such as data.contact.{{step.email}}.company_summary |
trust_min? |
Minimum confidence to accept (default 0.8) |
max_age_ms? |
Reject rows older than this |
fallback_step? |
Step to advance to on a miss |
Returns { value, source, confidence?, fallback? } where source is
enrichment, miss, stale, low_trust, unparseable_ref, or
no_runtime.
#Related
- Recipe schema — the step model these run inside.
- Authoring recipes — when to reach for a transform versus an AI step.