> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spojit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transform

> Reshape and map data between workflow steps.

The **Transform** node is a deterministic, AI-free utility for shaping data as it flows between steps. It has two modes: **Structured** for field-by-field output and **Plaintext** for free-form templates, with an optional **Iterate over** field that runs the transform once per element of an input array.

Use it when the shape you need is known and predictable. For shaping that requires LLM judgment (summarising, classifying, inferring missing fields), reach for an AI Connector node instead.

## When to use

* Rename or remap fields between connectors (e.g. `firstName` → `first_name`).
* Extract a subset of fields from a larger object.
* Build a JSON payload for a downstream HTTP/connector call.
* Transform every element of an array in one step (map mode).
* Combine values from multiple upstream nodes into a single object.

## Templates

Field values are written using `{{ var.path }}` template syntax, the same syntax used everywhere else in the workflow editor. See [Passing Data Between Nodes](/workflow-editor/passing-data) for the full reference.

| Template                             | What it references                                                                         |
| ------------------------------------ | ------------------------------------------------------------------------------------------ |
| `{{ input.field }}`                  | A field from the trigger payload.                                                          |
| `{{ stepOutput.field }}`             | A field from an upstream step's output (whatever you set as its **Output Variable**).      |
| `{{ item.field }}` and `{{ index }}` | Only available when **Iterate over** is set: the current element and its zero-based index. |

A template that is the *entire value* of a field preserves the original type (numbers stay numbers, arrays stay arrays). Mixed templates like `"Hi {{ name }}"` are stringified.

## Structured mode

Define your output shape field by field. Each leaf field carries a template that resolves at execution time.

1. Click **Add Field** to add an output property.
2. Pick a name and type (`string`, `number`, `boolean`, `array`, `object`).
3. For leaves, fill in the **template** with `{{ … }}` references.
4. For `object`, add nested children. For `array`, choose the item type; for arrays of objects, define the per-item children.
5. Toggle **JSON** (top right) to edit the schema as raw JSON when you'd rather paste a structure than click through it.

**Example.** Given an upstream connector with output variable `order`:

```json theme={null}
{
  "order": {
    "id": "12345",
    "customer": { "firstName": "Jane", "email": "jane@example.com" },
    "totalPrice": 99.95
  }
}
```

Structured fields:

* `id` (string) → `{{ order.id }}`
* `customer_email` (string) → `{{ order.customer.email }}`
* `amount` (number) → `{{ order.totalPrice }}`

Resolves to:

```json theme={null}
{
  "id": "12345",
  "customer_email": "jane@example.com",
  "amount": 99.95
}
```

`amount` stays a number because the template is the entire field value.

### Field types and value coercion

The declared field type controls how literal values you type are persisted:

* **String**: the value is sent as-is.
* **Number**: a literal like `42` is sent as the number `42`. Non-numeric literals fall back to a string.
* **Boolean**: `true` and `false` (case-insensitive) become the actual booleans `true` / `false`. Anything else falls back to a string.
* **Object / Array**: shape is built from the children you define.

For **template values** (anything containing `{{ … }}`), the field type does not coerce; the engine returns whatever type the resolved expression produced. So a String field with template `{{ item }}` (where `item` is a user object) outputs the *object*, not a stringified one. Match the declared field type to what your template actually resolves to.

## Plaintext mode

Paste any text or JSON template and substitute `{{ … }}` references inline. Use this when you'd rather write the payload as a literal block, useful for HTTP request bodies, Slack message text, or any sufficiently large object.

The **Output as JSON** toggle controls how the rendered output is treated:

* **Off**: the result is the rendered string verbatim.
* **On**: the rendered string is parsed as JSON; the parsed object/array is what downstream nodes receive. If the rendered output isn't valid JSON, the workflow fails with a clear error so the bug is caught at the transform, not three steps downstream.

**Example.** Template (with **Output as JSON** on):

```json theme={null}
{
  "summary": "Order {{ order.id }} for {{ order.customer.firstName }} totalling ${{ order.totalPrice }}",
  "orderId": "{{ order.id }}"
}
```

With the same `order` input as above, resolves to:

```json theme={null}
{
  "summary": "Order 12345 for Jane totalling $99.95",
  "orderId": "12345"
}
```

## Iterate over (map mode)

Set **Iterate over** to a template expression that resolves to an array; the transform then runs once per element. Inside templates, `{{ item }}` is the current element and `{{ index }}` is its zero-based position. The transform's **Output Variable** receives the array of all per-iteration results.

**Example.** With `users` set to:

```json theme={null}
[
  { "first_name": "Jane", "email": "jane@example.com" },
  { "first_name": "Bob",  "email": "bob@example.com" }
]
```

Set **Iterate over** to `{{ users }}` and a structured field `mapped` (Object type) with the children `name` → `{{ item.first_name }}` and `email` → `{{ item.email }}`. The output variable holds:

```json theme={null}
[
  { "mapped": { "name": "Jane", "email": "jane@example.com" } },
  { "mapped": { "name": "Bob",  "email": "bob@example.com" } }
]
```

This is equivalent to wrapping the transform in a Loop node, but it's a single node and doesn't require Loop's per-item connection wiring.

### Drill into the upstream object to find the array

A common surprise: most upstream steps don't return a bare array; they return an object that *wraps* the array. Common shapes:

* **AI Connector (agent mode) with a Response Schema** → the step's value is the schema object, e.g. `{ "users": [ … ] }` → use `{{ result_step1.users }}`.
* **HTTP / direct connector** → typically `{ "data": [ … ], "status": 200, "headers": { … } }` (e.g. JSONPlaceholder) → use `{{ result_step1.data }}`.

If you point **Iterate over** at the wrapper instead of the inner array, the workflow fails fast with a clear error: `forEach 'items' template "{{ result_step1 }}" must resolve to an array, got object (resolved object has keys: data, status, headers)`. So you can spot the missing path segment immediately.

## Output

The resolved value is bound to the **Output Variable** name (defaulting to `result_<nodeId>` when blank). Reference it from downstream nodes the same as any other step result, e.g. `{{ shapedUser.email }}`. For map mode, this name holds the full collected array.

## Tips

* Chain multiple transforms when reshaping in stages; it's cheaper to read than one giant template.
* For advanced operations like JSONPath queries, deep flattening, or merging nested objects, see the [JSON Tools](/connectors/utilities/json) connector.
* A template that points at nothing (e.g. a typoed variable name) resolves to `null`. Use the structured editor's JSON view when debugging; it shows exactly the templates being sent to the engine.
