# Transform

Execute a short **JavaScript** or **Python** snippet on the previous node's output and forward the result to downstream nodes.

## Overview

Transform is a sandboxed code node. It runs in an isolated sandbox (no network, no host filesystem, ephemeral), so you can write arbitrary code without leaking the host environment.

Use it whenever you need a deterministic, free-form transformation that a pure template can't express:

- Reshape JSON (rename fields, flatten, restructure)
- Filter / map / sort arrays
- Arithmetic (taxes, percentages, totals)
- Date / string parsing
- Combine fields from `input`, `state` and `event` into a single payload

For LLM-driven decisions (classify, summarise, translate…), prefer an **Agent** node.

## Configuration

| Field | Description |
| --- | --- |
| **Language** | `JavaScript (Node.js)` or `Python 3`. Switchable per node. |
| **Code** | The snippet. **Assign the output to a variable named `result`.** If you don't, the node passes `last_output` through unchanged. The editor shows the runtime variables as comments and autocompletes their names and known paths as you type; press **Ctrl+Space** to open all suggestions. |

## Variables in scope

The wrapper injects these for you before your code runs:

| Variable | Content |
| --- | --- |
| `input` | Initial workflow input |
| `last_output` | Output of the previous node |
| `state` | All Set state variables — `state.customer_email`, `state.page`… |
| `event` | Trigger payload — `event.body.foo`, `event.headers.X`… |
| `result` | **You assign this.** Whatever you put here becomes the node's output. |

In Python, injected objects are dictionaries: use `state["key"]`, `event["body"]["email"]`, or `input.get("field")`. Attribute syntax such as `state.key` is JavaScript-style and raises `AttributeError` in Python. `state["key"]` raises `KeyError` when the key is absent; use `state.get("key")` for optional state. The autocomplete inserts the correct notation for the selected language.

When testing a Transform from its **Run** button, choose **Previous** to execute its upstream Set state nodes. **Node** mode deliberately runs the Transform in isolation, so `state` is empty unless you provide it in the Input ctx JSON.

## Examples

### Reshape JSON (JS)

```javascript
result = {
  name: input.user.full_name,
  email: input.user.email.toLowerCase(),
  signed_up: input.created_at,
}
```

### Filter an array (JS)

```javascript
result = input.tickets.filter(t => t.priority === "urgent")
```

### Math + percentage (JS)

```javascript
result = {
  total: input.subtotal + input.tax + input.shipping,
  discount_pct: Math.round((input.discount / input.subtotal) * 100),
}
```

### Flatten + deduplicate (JS)

```javascript
result = [...new Set(input.flatMap(group => group.tags))]
```

### List comprehension (Python)

```python
result = [item["name"].upper() for item in input if item.get("active")]
```

### Combine state + event into a payload (Python)

```python
result = {
    "to": state["customer_email"],
    "subject": f"Re: ticket #{event['body']['id']}",
    "body": last_output,
}
```

### JSON regroup (Python)

```python
from collections import defaultdict
grouped = defaultdict(list)
for row in input:
    grouped[row["category"]].append(row)
result = dict(grouped)
```

## Combo Transform + Set state

The two often work together: **Transform does the calculation**, **Set state keeps the memory**.

```
HTTP Request (paginated API)
   ↓
Transform   (extract rows + has_more flag)
            code: result = { rows: input.items, more: !!input.next_page }
   ↓
Set state   (page_count = result of an arithmetic Transform you ran earlier)
```

Set state can't do math like `{state.x}+1` — pass through Transform first.

## Gotchas

| Symptom | Cause | Fix |
| --- | --- | --- |
| Output is `null` or unchanged | You didn't assign to `result` | Add `result = ...` |
| Python `KeyError` on `state["key"]` | The key was not created, or the Transform was tested alone | Run in **Previous** mode, provide `{"state":{"key":"value"}}`, or use `state.get("key")` when optional |
| `Cannot read property X of undefined` | The path doesn't exist in `input` | JS: `input?.user?.email ?? ""` — Python: `input.get("user", {}).get("email", "")` |
| Timeout error | Script took over 20 seconds | Move heavy work to your own backend via HTTP Request |
| `fetch is not defined` / `urlopen failed` | Sandbox has **no network** | Use the HTTP Request node for outbound calls |
| Native module not found | Sandbox only ships stdlib + a small set of packages | Stay within the standard library |
| Output looks like JSON-stringified text | Your `result` is a string that happens to be valid JSON | Parse it explicitly (`JSON.parse(result)` / `json.loads(result)`) before assigning |

## Security

The script runs in a fresh, isolated sandbox:

- No network access (default)
- No access to host filesystem, env vars, or other tenant data
- Hard 20-second timeout
- Container is destroyed after each call
- Same infrastructure as the **Code interpreter** and **Shell** agent tools

## Transform vs Agent (JSON mode)

| Task | Use |
| --- | --- |
| Mechanical reshape, math, filter, format conversion | **Transform** — fast, free, deterministic |
| Fuzzy / interpretive work (classify, summarise, translate) | **Agent** — slower, paid, creative |

> If you can write the rule down, use Transform. If you need to interpret natural language, use Agent.

---

*Source: https://agentbuilder.systalink.sn/docs/node-transform — human documentation.*
*Other language: [/docs-md/fr/node-transform.md](/docs-md/fr/node-transform.md).*
*Machine-readable index: [/llms.txt](/llms.txt).*
