Skip to main content
The transformer node reshapes, maps, combines, and converts data as it moves between steps — without writing code. You point it at a source value with a {{node_id.field}} reference, then apply an ordered pipeline of operations (pick fields, map a list, parse JSON, format a date, run arithmetic, and so on). The transformed value is written into run state under the node’s own id. Use a transformer when the shape one node produces does not match the shape the next node expects — for example, picking three fields out of a tool’s response object, mapping a list of records down to a list of ids, or merging two earlier outputs into one object before an LLM node reads them. For branching or looping over data, use a conditional node; for raw HTTP, webhooks, or JSON-schema validation, use a function node.

What the node does

When the engine compiles your workflow, the transformer node becomes a single async step (see workflow engine & nodes). At run time the node:
  1. Resolves its source reference against the current run state to get a starting value.
  2. If the source resolves to null (the reference is missing or out of range), it logs a warning and writes null under the node id — the operations are not run.
  3. Otherwise it applies each operation in operations in order, feeding the result of one operation as the input to the next (a pipeline).
  4. Writes the final value into run state under the node’s id.
Because operations are applied in sequence, order matters: parse_json then pick first turns a JSON string into an object and then selects fields from it; the reverse order would fail to find any fields on a string.
The output of a transformer node is always stored under the node’s id — for example a node with id: "shape_1" writes to {{shape_1}}. The legacy output_key and source_key fields are deprecated and should not be set; use source with a {{node_id.field}} reference instead. See inputs & outputs.

Configuration (TransformerNodeConfig)

These fields live on the node’s transformer_config. In the builder you set them through the detail panel; over the API they appear inside the node definition (the builder also accepts a wrapped {config: {...}} form, which the backend normalizes to transformer_config).
string
The value to transform, written as a single {{node_id.path}} reference (for example {{node_fetch.result.items}}). A reference that is exactly one {{...}} token resolves to the native typed value — a list stays a list, a number stays a number — so the operations receive the real object, not its string form. Optional in the schema, but in practice you set either source or the deprecated source_key; if neither resolves to a value, the node writes null.
TransformOperation[]
required
The ordered list of operations to apply. Required. Each operation runs against the output of the previous one. See the operation catalog for every operation type and its params. An empty list passes the source through unchanged.
string
deprecated
Legacy direct state-key lookup (no {{...}}). Deprecated — use source with a {{node_id.field}} reference. Only consulted when source is unset.
string
deprecated
Deprecated and ignored. Output is always stored under the node id.

The TransformOperation object

Each entry in operations is one operation.
string
required
The operation to perform — one of the values in the operation catalog below (for example pick, map, parse_json, format_date). An unrecognized value raises a validation error when the workflow is saved.
string
Optional dot-path that scopes the operation to one field of the current value when that value is an object — for example field: "user.name" applies the operation only to user.name and returns the (mutated) object. The dot-path navigates nested objects; if the final key is absent, the object is returned unchanged. When field is omitted, the operation applies to the whole current value. field has no effect when the current value is not an object.
object
Operation-specific parameters (for example { "keys": ["id", "title"] } for pick). Optional; defaults to an empty object. Each operation below documents the params it reads. Inside string params, {{node_id.field}} references are resolved against run state for the operations noted below.

Inputs and outputs

reference
The transformer reads exactly one value: whatever source resolves to. It does not take an input_mapping like the tool or function nodes — to combine several earlier outputs, point source at one of them and pull in the others with the merge, set, default, concat, template, or filter operations, which resolve {{node_id.field}} references inside their params (see combining data from multiple nodes).
any
The final value after every operation runs, written to run state under the node’s id. The type depends on your pipeline: a string after to_string, a number after count, an object after pick, a list after map, and so on. Reference it downstream as {{node_id}} (or {{node_id.path}} for a sub-field).
null
If source (and source_key) resolve to null, the node writes null and skips all operations.
object
If any operation raises, the node stops the pipeline and writes an error object instead of the transformed value: {error, op}, where op is the operation type that failed. The run continues — the transformer does not throw on operation errors (see errors).

The operation catalog

Operations are grouped by what they act on. Each one takes the current pipeline value as input and returns the next value. Where an operation can read {{node_id.field}} references inside a param, it is called out — pure single-reference params keep their native type, mixed strings are templated to text.
Most operations are type-guarded: if the current value is not the type an operation expects (for example pick on a non-object, or sum on a non-list), the value is returned unchanged rather than erroring. The exceptions that can produce a fallback value instead are noted per operation.

String operations

operation
Uppercases the value as a string. No params.
operation
Lowercases the value as a string. No params.
operation
Strips leading and trailing whitespace from the value as a string. No params.
operation
Joins values into one string. params.values is a list of additional values; params.separator (default "") joins them. The current value is placed first. A values entry that starts with $ is read from state by name; one containing {{...}} is template-resolved. Everything is coerced to a string.
operation
Splits the value (as a string) into a list on params.separator (default ","). The separator may contain {{...}} references.
operation
Replaces every occurrence of params.old with params.new in the value as a string. Both old and new may contain {{...}} references.
operation
Renders params.template. First any {{node_id.field}} references in the template are resolved against state; then, if the current value is an object, each {key} placeholder is replaced with that object’s matching field. Use this to build a string from an object’s fields.
operation
Returns str(value)[start:end] using params.start (default 0) and optional params.end.

Object operations

operation
Keeps only the keys in params.keys (a list). Missing keys are skipped. Non-object values pass through unchanged.
operation
Removes the keys in params.keys (a list), keeping everything else.
operation
Renames keys using params.mapping, an {old_key: new_key} map. Keys not in the map keep their name.
operation
Shallow-merges another object into the current object. params.with is the object (or a $state_key) to merge in; its string values that are {{...}} references are resolved (pure references keep their type). When the current value is an object and with is a non-object value, params.as_key nests it under that key.
operation
Flattens a nested object into a single level, joining nested keys with params.separator (default "_") — for example {a: {b: 1}} becomes {a_b: 1}.
operation
Wraps the value under a path. params.path (or params.key) like "user.profile" produces {user: {profile: <value>}}.
operation
Fills in missing or null fields of an object from params.defaults (an {key: value} map); existing non-null fields are kept. Default values may be {{...}} references. When the current value is not an object, returns the value if it is non-null, otherwise params.value.
operation
Sets params.key to params.value on the object (overwriting). The value may be a $state_key or a {{...}} reference (pure references keep their type). No-ops when the current value is not an object or key is empty.

Array operations

operation
Applies a nested operation to every item of a list. params.operation is itself an operation object — {type, params} — for example { "type": "pick", "params": { "keys": ["id"] } } to reduce each record to its id.
operation
Keeps list items whose field matches a condition. params.condition has a field plus one matcher: equals, not_equals, not_null, is_null, contains, greater_than, less_than, in, not_in, or exists. Condition values may be {{...}} references. Only the first matcher present is applied; items that are not objects are dropped.
operation
Sorts a list. params.key sorts a list of objects by that field; without a key it sorts scalars. params.reverse (default false) reverses the order.
operation
Returns the first item of a list, or null if the list is empty.
operation
Returns the last item of a list, or null if the list is empty.
operation
Returns the length of a list, object, or string; 0 for other types.
operation
Sums a list of numbers. With params.key, sums that field across a list of objects.
operation
Averages a list of numbers (or params.key across objects). Returns 0 for an empty list.
operation
Groups a list of objects into an object keyed by params.key’s value — {group_value: [items...]}.
operation
De-duplicates a list. With params.key, keeps the first item per distinct field value; without a key, de-duplicates scalars.
operation
Reverses a list, or reverses a string character by character.
operation
Returns value[start:end] of a list or string using params.start (default 0) and optional params.end.

Type conversions

operation
Converts the value to its string form ("" for null). No params.
operation
Parses the value to a number (int when a string has no decimal point, else float). Returns 0 if it cannot be parsed.
operation
Converts to a boolean. A string is true when it is one of true, 1, yes, or on (case-insensitive); otherwise standard truthiness applies.
operation
Wraps the value in a list. A list passes through; an object yields its values; a string yields its characters; any other value is wrapped as [value].
operation
Parses a JSON string into an object or list. If the string is not valid JSON, the original string is returned unchanged.
operation
Serializes the value to a JSON string (non-serializable values fall back to their string form).

Date operations

operation
Formats a datetime or a Unix timestamp (seconds) using params.format (a strftime pattern, default %Y-%m-%d %H:%M:%S). The format may contain {{...}} references. Other value types pass through unchanged.
operation
Parses a date string into an ISO-8601 string using params.format (default %Y-%m-%d). The format may contain {{...}} references. If the string does not match, it is returned unchanged.

Math operations

operation
Evaluates params.expression with the current value bound to x (and math available) — for example "x * 1.2" or "math.sqrt(x)". The expression may contain {{...}} references. On any evaluation error the current value is returned unchanged.
operation
Rounds a number to params.decimals decimal places (default 0). Non-numbers pass through.
operation
Returns the absolute value of a number. Non-numbers pass through.
operation
Returns the smallest item of a list (null for an empty list). Non-lists pass through.
operation
Returns the largest item of a list (null for an empty list). Non-lists pass through.
The math operation and the filter matchers evaluate expressions in a sandbox that removes Python built-ins, but they still use eval. Treat operation params as part of your workflow definition — author them yourself; do not build a math expression or a filter condition out of untrusted run input.

Referencing data: {{node_id.field}}

The transformer’s source is the main place you wire in an earlier node’s output, and it follows the standard reference rules from variables & references:
  • A source that is exactly one reference (for example {{node_fetch.result.items}}) resolves to the native typed value, so a list arrives as a list and an object as an object.
  • The path supports dot and bracket access: {{node_fetch.result.items[0].id}}.
  • An unresolved or out-of-range source resolves to null, which makes the node skip its operations and write null.
Beyond source, several operations resolve {{...}} references inside their params — merge (the with object), set and default (their values), concat (its values), template (the template body), replace (old/new), split (the separator), filter (condition values), and the date/math format and expression strings. This is how a single transformer can pull in data from more than one earlier node.

Combining data from multiple nodes

A transformer reads one source, but its operation params can reference any other node. To merge two earlier outputs into one object, start from one of them and merge (or set) the other in by reference.
Merge two node outputs into one object
After this node runs, {{node_combine}} holds the profile object plus the two pulled-in order fields. Because latest_order_id and order_total are pure single references, they keep their native types (a string id and a numeric total).

Worked example: shape a tool response for an LLM

This transformer sits between a tool node that returns a list of support tickets and an LLM node that summarizes them. It filters to open tickets, sorts them newest-first, reduces each ticket to the three fields the prompt needs, and counts them — turning a verbose API payload into a compact, prompt-ready structure.
1

A tool node fetches tickets

node_tickets calls a tool and writes its unwrapped result under node_tickets, including a result.tickets array of ticket objects.
2

The transformer reshapes the list

node_shape reads {{node_tickets.result.tickets}}, then runs an ordered pipeline: filter to open tickets, sort by created_at descending, map each ticket down to {id, subject, priority}, and finally a parallel count is captured in a second transformer (or you read {{node_shape}} and its length downstream).
3

An LLM node summarizes the shaped data

node_summary reads {{node_shape}} into its prompt and writes the summary under node_summary.
Transformer node (pipeline of four operations)
Given a source of:
Resolved source — {{node_tickets.result.tickets}}
the node writes this under node_shape:
Output — {{node_shape}}
A later LLM node then reads it directly:
LLM node reading the shaped list

Run it

Run the workflow over the API or an SDK and stream it to watch each node_update arrive — one per node, keyed by node with its per-node output — followed by done. The transformer’s node_update carries the shaped value under its node id. See run via API and SSE run streaming for the full event taxonomy.
The run surface is behind the billing gate. When an organization is out of credits or over a limit, the run endpoint returns a flat denial envelope — {code, layer, key, current, limit, reason} — as 402 / 403 / 429, not the plain {detail} shape used by CRUD routes. The transformer node itself never triggers the gate (it consumes no credits), but an LLM, agent, or managed knowledge node elsewhere in the same workflow can. See errors & status codes for all three envelope shapes.

Errors

The transformer is resilient by design: most operations fall back to passing the value through rather than failing, and any operation that does raise is caught.
warning
If source (and the deprecated source_key) resolve to null, the node logs a warning and writes null — it does not raise. Downstream references like {{node_id.field}} then resolve to null.
object
If an operation raises mid-pipeline, the node stops and writes {error, op} (the message and the failing operation type) under its node id. The run continues to the next node — the transformer does not propagate the exception, so its retry policy is not triggered by operation errors.
passthrough
An operation applied to the wrong type (for example sum on a non-list, pick on a non-object) returns the value unchanged rather than erroring. Parse-style operations have their own fallbacks: to_number returns 0, parse_json returns the original string, and math returns the input on an evaluation error.
ValueError
A transformer node with no transformer_config raises Transformer node <id> missing transformer_config when the workflow is compiled — caught at validation time, before the run starts.
Because operation errors surface as data ({error, op}) rather than as a thrown exception, guard against them downstream: branch on the presence of an error key with a conditional node, or validate the shape with a guardrails node. For how node failures surface and how the run-level retry policy works for nodes that do throw, see error handling & retries.

Credit impact

A transformer node consumes no credits. It runs entirely inside the engine — there is no model call, no managed retrieval, and no external request — so it is never metered and never hits the billing gate. Use transformers freely to keep model prompts small and well-shaped, which lowers the token cost of the LLM and agent nodes that read their output.

Node types reference

All nine node types and the output-to-state convention.

Variables & references

The full {{node_id.field}} resolution rules and the run state model.

Conditional node

Branch on a transformer’s output, or loop over a transformed list.

Function node

HTTP requests, webhooks, and JSON-schema validation as built-ins.

Error handling & retries

How node failures surface and how the retry policy behaves.

Workflow engine & nodes

How nodes compile into a graph, the run state, edges, and loops.