

<div class="admonition admonition-important"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

The **Run Code** node can be used only in [business-oriented workflows](/glossary/#business-workflow).

</div></div></div>


The **Run Code** node lets you write and execute custom JavaScript code directly inside a workflow. Use it when you need to transform, enrich, or otherwise process data in a way that isn't covered by the built-in nodes, without building a separate integration. With this node, you can:

- transform or clean up data (for example, convert text to numbers, format dates, adjust field values),
- add new fields calculated from the existing ones,
- filter out rows that shouldn't continue through the workflow,
- split one row into several separate rows.

The code you provide runs in a secure, sandboxed environment with no access to the file system, network, or environment variables. The node receives the data available at its point in the workflow, passes it to your code as the `row` argument, and forwards whatever your code returns to the next node.

## Prerequisites
---
- Knowledge of JavaScript.
- The workflow must be a [business workflow](/glossary/#business-workflow), for example one started with the [Business Event node](/docs/automation/triggers/businees-event-trigger).

## Node configuration
---

1. Click the **Run Code** node.  
    **Result**: The configuration pop-up opens.
2. In the **JavaScript source code** field, write the function that processes the input data and returns the transformed result.  
    The code must be a function with the following signature:
    
   <pre><code class="language-javascript">(row) =&gt; {
     return row;
   }</code></pre>

    - `row` is one row of the data received by the node at this point in the workflow - your function runs once per row. See ["How your code receives data"](#how-your-code-receives-data-the-row-object) for details on what this data looks like and where it comes from.
    - The function must return the value you want to pass to the next node.
    - For the rules your function must follow and the values it can return, see ["Writing your function"](#writing-your-function).
    - To expand the code editor, click **Fullscreen** .
3. Test your code before applying it. For details, see ["Testing your code"](#testing-your-code).
    1. Under **Test execution**, upload a sample file in the **Sample file** field.  
        Supported formats: JSON, XML, CSV. Maximum size: 5 MB.
    2. Click **Preview sample data** to check the content of the uploaded file.  
        The preview shows each row exactly as your code receives it in the `row` argument - nesting and lists included, whatever the file format. This happens automatically, so you can check the field names and structure before writing your function.
    3. Click **Execute test** to run your code against the sample file and review the result.  
        The result shows the data exactly as your function returned it - nothing is added, removed, or restructured.

    
       <div class="admonition admonition-note"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

       Once you upload a sample file, **Apply** stays disabled until you run **Execute test** at least once.

       </div></div></div>

4. Confirm by clicking **Apply**.  
    **Result**: The node runs your function once for every row and passes the returned data to the next node in the workflow.

### Writing your function
---
#### How your code receives data (the row object) 

The `row` argument is a plain JavaScript object with the same structure as one row of your data - you don't need to parse anything. For example, if the node before **Run Code** is [Local File](/docs/automation/operation/local-file-node) and it loads a CSV file with 100 rows of data, the **Run Code** node runs your function 100 times - once for each row - and each time, `row` contains only the fields of that one row, not the whole file. Nested data stays nested: objects contain objects, and lists are real arrays. You access fields directly:


<pre><code class="language-plaintext">// Row data:
// {
//   "sku": "A-1",
//   "details": { "weight": 0.4, "height": 10 },
//   "tags": ["sale", "new"]
// }
row.sku                // "A-1"
row.details.weight     // 0.4
row.tags[0]            // "sale"</code></pre>



<div class="admonition admonition-tip"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" /></svg></div><div class="admonition-body"><div class="admonition-content">

You don't have to guess this structure. Upload a sample file and click **Preview sample data** - the preview shows each row exactly as your code receives it (see ["Testing your code"](#testing-your-code)).

</div></div></div>


`row` contains only the row's data - there is no extra metadata inside it.

**Run Code** must be placed directly after a node that produces row data - for example, [Local File](/docs/automation/operation/local-file-node) or one of the **Get File** nodes (available for integrations such as Amazon S3, Azure Blob Storage, Google Cloud Storage, SFTP, and HTTP).  

If a field name in the incoming data contains a dot, your code sees it as a nested structure - a field named `details.weight` is read as `row.details.weight`, not `row["details.weight"]`. See ["Dots in field names"](#dots-in-field-names).

Do not assume a field is always present. If a row does not contain a field, its value is `null`, and reading a property of a `null` value returns an error (see ["When your code fails"](#when-your-code-fails)). Use optional chaining (`?.`) and defaults (`??`) for fields that may be missing:


<pre><code class="language-javascript">(row) =&gt; {
  return {
    ...row,
    weightKg: row.details?.weight ?? 0
  };
}</code></pre>


- **Input**: `{ "sku": "A-1", "details": null }`
- **Result**: `{ "sku": "A-1", "details": null, "weightKg": 0 }` - no error, even though details is empty.

#### What your function must return

The value your function returns replaces the row completely - it is not merged with the input. Fields you don't include in the returned object are not available to any following node. To keep the existing data and add new fields, spread `row` into the result:


<pre><code class="language-javascript">(row) =&gt; {
  return { ...row, priceNumeric: 19.99 };
}</code></pre>


- **Input**: `{ "sku": "A-1", "price": "$19.99" }`
- **Result**: `{ "sku": "A-1", "price": "$19.99", "priceNumeric": 19.99 }` - the original fields are preserved.  

You can also modify `row` in place and return it: `row.priceNumeric = 19.99; return row;`

The value you return decides what happens to the row:

| Your function returns | What happens |
|-----------------------|-------------|
| An object | One row continues with that data |
| An array of objects | Each element becomes a separate row |
| An empty array `[]` | The row is dropped - nothing continues |
| `null` or `undefined` | Error - the node execution fails |
| A string, number, or boolean | Error - the node execution fails |
| An array containing values that are not objects | Error - the node execution fails |

- **To drop a row, return an empty array `[]`** - The row ends at this node and is not treated as an error. Returning null does not drop the row - it causes an error.
- **Dropping can also happen unintentionally** - If you build the result with `.filter()` or `.map()` and the array comes out empty, the row is dropped silently. Check for empty results if that is not what you want.
- **To split one row into several, return an array of objects** - Each object becomes a separate row, and each one goes through the following nodes independently.

Your result is saved as JSON. Keep these conversions in mind:

- `Date` objects become text in ISO format, for example `"2026-07-13T10:00:00.000Z"`. The following nodes see a text value, not a date.

- Fields set to `undefined`, and functions, are removed from the output without an error.

- `NaN` and `Infinity` become `null`. For example, `parseFloat("abc")` returns `NaN`, which the next node sees as `null` - validate inputs if that matters.

The fields your function returns are available in the following nodes - for example, in filters and mappings. Nested fields appear as dot-separated names: if you return `{ "details": { "weight": 2.5 } }`, select `details.weight` in the next node.

#### Dots in field names


<div class="admonition admonition-warning"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" /></svg></div><div class="admonition-body"><div class="admonition-content">

A dot in a field name always means nesting - it is not an ordinary character. If you return a field whose name contains a dot, the platform expands it into a nested structure on the output.

</div></div></div>



<pre><code class="language-javascript">(row) =&gt; ({ "details.weight": 2.5 })</code></pre>


- **Test result**: `{ "details.weight": 2.5 }` - shown exactly as you returned it.
- **Output**: `{ "details": { "weight": 2.5 } }` - the dot is expanded into nesting, exactly as if you had returned the nested object yourself.

Keep these consequences in mind:

- The test result shows the field name with the dot exactly as you returned it, but on the real output the dot is expanded into nesting.
- There is no way to output a field with a literal dot in its name, and no way to escape the dot.
- If you return both forms at once - for example `{ "details.weight": 2.5, "details": { "weight": 9 } }` - one value silently overwrites the other, without an error.

Unless you intend to create nesting, don't use dots in field names.

#### Good practices

- **Wrap returned object literals in parentheses** - In an arrow function, `{ }` after the arrow is a code block, not an object. `(row) => { total: 1 }` returns nothing and fails with the "returned null" error. Write one of:

    
  <pre><code class="language-javascript">(row) =&gt; ({ total: 1 })            // parentheses make it an object
  (row) =&gt; { return { total: 1 }; }  // explicit return</code></pre>


- **Test with a sample that matches your real data** - the same field names and the same value types. Remember that CSV and XML samples turn every value into text.
- **Expect missing fields** - Use `?.` and `??` rather than assuming every row is complete.
- **Keep the function fast and lightweight** - Combining several transformations in one function is fine - just avoid heavy computation and building very large data structures.
- **Don't use dots in field names** unless you intend to create nesting - a dot is always treated as a nesting separator (see ["Dots in field names"](#dots-in-field-names)).

#### Restrictions

**Allowed**:  
- Standard JavaScript built-ins: `JSON`, `Math`, `Date`, string, number, array, and object methods, regular expressions, `Map` and `Set`.

- Modern syntax: arrow functions, optional chaining (`?.`), nullish coalescing (`??`), spread (`...`), destructuring, template literals.

**Forbidden**:
- Network calls: `fetch` and `XMLHttpRequest` are not available. To use external data, add it to the workflow data before this node.
- `async`/`await`: the function must be synchronous. Async functions are rejected with an error.
- Timers: `setTimeout` and `setInterval` are not available.
- Libraries: `import` and `require` are not available. You cannot use npm packages.
- File system and environment variables.

Execution time and memory are strictly limited. The node is designed for quick transformations of a single row - not for heavy computation. Long loops or building very large data structures fail with a time or memory limit error. The size of a single row must not exceed 1 MB. This applies both to the rows the node receives and to the rows your function returns. Larger rows are not supported.

The **JavaScript source code** field must contain exactly one function. Don't write any statements before or after it. Define helpers inside the function body:


<pre><code class="language-javascript">(row) =&gt; {
  const toNumber = (value) =&gt; parseFloat(String(value).replace(/[^0-9.]/g, ""));
  return { ...row, priceNumeric: toNumber(row.price) };
}</code></pre>


If you need constants or helpers defined once, outside the per-row function, wrap everything in an immediately invoked function that returns your main function:


<pre><code class="language-javascript">(() =&gt; {
  const toNumber = (value) =&gt; parseFloat(String(value).replace(/[^0-9.]/g, ""));
  return (row) =&gt; ({ ...row, priceNumeric: toNumber(row.price) });
})()</code></pre>



### Testing your code
---

You can test your code against a sample file before applying the node and as a result a successful test shows the transformed data your function returned for each sample row. A failed test shows the error message.

Each row of the sample is passed to your function separately, exactly like the rows of real data. How the file becomes rows depends on the format:

| Format | Structure | Value type |
|--------|-----------|------------|
| `JSON` | An array of objects, or one object per line. Each object is one row. Nesting and lists are kept. | Kept as in the file - numbers stay numbers, booleans stay booleans, `null` stays `null`. |
| `CSV` | The first line contains the field names. Each following line is one row. | **Every value is text.**|
| `XML` | Each repeating element is one row. Nested elements become nested fields. Repeated tags become a list. Empty elements become `""`. | **Every value is text.** |


<div class="admonition admonition-important"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

Prepare the sample so that the field names and value types match the real data. If your real data contains `"price": 19.99` (a number), a CSV sample gives you `"price": "19.99"` (text) and your test results will differ from production. For example, `row.price + 1` returns `"19.991"` with the CSV sample, but `20.99` with the real data. When in doubt, use a JSON sample copied from real data.

</div></div></div>


Both views show the data from your code's point of view: the sample preview shows each row as the `row` argument your function will receive, and the test result shows exactly what your function returned.

### When your code fails
---
If your function throws an error (for example, `TypeError: Cannot read properties of null`), returns an invalid value, or exceeds the execution limits, the node execution fails and the data does not continue to the following nodes. A single failing row is enough to stop the execution, so guard against fields that may be missing (see ["How your code receives data"](#how-your-code-receives-data-the-row-object)). For a running workflow, the error and its details are available in the **Transformation logs** tab in the workflow view.

Test your code with **Execute test** before you apply the node.


## Example of use
---
You import product stock updates from a file kept in external storage (for example, an SFTP server or a cloud storage bucket). The price field in the file is text with a currency symbol (for example, `"$19.99"`), and you need a plain number before importing the data further.

1. Add a **Scheduled Run** trigger node and configure when the workflow starts - immediately or on a schedule.
2. Add the node that loads the stock updates file from your external storage and configure the connection.
3. Add the **Run Code** node and connect it to the node that loads the file. In the configuration of the node, enter the following code:
    
   <pre><code class="language-javascript">(row) =&gt; {
     row.price = parseFloat(row.price.replace(/[^0-9.]/g, ""));
     return row;
   }</code></pre>
  
    - **Input**: `{ "sku": "A-1", "price": "$19.99" }`
    - **Result**: `{ "sku": "A-1", "price": 19.99 }`

    The function runs once for every row of the loaded file.

    
      <div class="admonition admonition-tip"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" /></svg></div><div class="admonition-body"><div class="admonition-content">

      If some rows may arrive without a price, guard against the missing field: `row.price = parseFloat((row.price ?? "").replace(/[^0-9.]/g, "")) || 0;` - rows without a price then get 0 instead of failing.

      </div></div></div>


4. Upload a sample file with a price column to test the transformation, then click **Execute test** to confirm the output contains a numeric value.  
5. Click **Apply**.  
6. Connect the **Run Code** node to the node that further processes or imports the transformed data.  
7. Finish the workflow with the **End** node.  

## More examples
---

- **Adding a field calculated from an existing one, keeping the rest of the data**

    
  <pre><code class="language-javascript">(row) =&gt; ({ ...row, upper: row.name.toUpperCase() })</code></pre>

    - **Input**: `{ "name": "anna", "city": "Warsaw" }`
    - **Result**: `{ "name": "anna", "city": "Warsaw", "upper": "ANNA" }` 
    - The `...` row spread keeps the existing fields - without it, only `upper` would reach the next node.

- **Filling in defaults for values that may be missing**  

    
  <pre><code class="language-javascript">(row) =&gt; ({
    ...row,
    price: row.price ?? 0,
    category: row.category ?? "uncategorized"
  })</code></pre>
  

    - **Input**: `{ "sku": "A-1", "price": null, "category": null }`
    - **Result**: `{ "sku": "A-1", "price": 0, "category": "uncategorized" }`

- **Keeping only selected fields, under new names**  

    
  <pre><code class="language-javascript">(row) =&gt; ({
    sku: row.product.id,
    weight: row.details.weight
  })</code></pre>
  

    - **Input**: `{ "product": { "id": "A-1", "name": "Mug" }, "details": { "weight": 0.4, "height": 10 } }` 
    - **Result**: `{ "sku": "A-1", "weight": 0.4 }`
    - This returns only two fields - everything else is intentionally dropped.

- **Filtering out rows that don't meet a condition**

    
  <pre><code class="language-javascript">(row) =&gt; row.total &gt;= 100 ? [row] : []</code></pre>
  

    - **Input**: `{ "orderId": 7, "total": 250 }` → the row continues. 
    - **Input**: `{ "orderId": 8, "total": 40 }` → the row is dropped, without an error.

- **Splitting one row into several rows**

    
  <pre><code class="language-javascript">(row) =&gt; row.items.map((item) =&gt; ({ orderId: row.orderId, ...item }))</code></pre>
  

    - **Input**: `{ "orderId": 7, "items": [ { "sku": "A" }, { "sku": "B" } ] }`
    - **Result**: two separate rows:
        - `{ "orderId": 7, "sku": "A" }`
        - and `{ "orderId": 7, "sku": "B" }`  
        Each one goes through the following nodes independently.