> ## Documentation Index
> Fetch the complete documentation index at: https://80x.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Execute code

> The Execute code block runs custom JavaScript inside a workflow and exposes the value it returns to later blocks.

The **Execute code** block (Calculations) runs custom JavaScript inside a workflow and exposes the value it returns to later blocks. Reach for it when a [Formula](/reference/calculations/formula) or [Aggregate values](/reference/calculations/aggregate-values) block cannot express the change you need. Common cases are reshaping incoming data into the format a later block expects, building a score from several attributes, or cleaning up inconsistent source data. The block runs your code with a 100-second timeout. That makes it a good fit for short transformations, not long-running jobs.

| | |
| - | - |
| **Category** | Calculations |
| **Credit cost** | Variable (complexity-based) |
| **Engine** | New Workflows engine (June 2026+) |
| **Plan availability** | Not documented. |
| **Limits** | 100-second execution timeout. |

## Inputs

| Input | Type | Required | Default | Notes |
| - | - | - | - | - |
| Code | JavaScript | Yes | None | Must export a function named `main` that receives a single `inputs` object. The function can be synchronous or asynchronous. |
| Variables | Any | No | None | Workflow variables you map into the `inputs` object. Each becomes a key on `inputs`, addressable as `inputs.<key>` inside `main`. |

The block passes every variable you map into it as one `inputs` object. Whatever `main` returns becomes the block's output. A minimal function reads its inputs and returns a plain object:

```js theme={null}
export async function main(inputs) {
  return { score: inputs.signals * 10 };
}
```

## Outputs

| Output | Type | Example | Notes |
| - | - | - | - |
| Result | Any | `{ "score": 70 }` | The exact value returned by `main`. Return an object to expose several named fields downstream. |

Reference the returned value in a later block as `{{ steps.execute_code.result }}`. If `main` returns an object, address each field by its key, such as `{{ steps.execute_code.result.score }}`.

## Behavior

The block runs the exported `main` function once per run and waits for it to finish before continuing. An `async` function is awaited, so you can `await` inside `main`. The work still has to finish within the 100-second timeout. Code that runs past the timeout fails the block instead of returning a partial result.

The return value passes through exactly as written, including `null`, an empty object, or an empty array. Downstream blocks receive exactly what `main` returned, so a missing field reads as `undefined` rather than an error. The credit cost is variable and complexity-based, so heavier code costs more than a light transformation. Keep functions small and single-purpose.

## Gotchas

* **The function must be named `main`.** The block calls `main(inputs)` by name. An unnamed function, a differently named export, or a default export will not run. Export `main` exactly, and read your data through its `inputs` parameter rather than from any outer scope.
* **A thrown error fails the whole block.** Any uncaught exception inside `main` stops the run at this block, so a single bad field can halt the workflow. Wrap risky parsing in a `try`/`catch` and return a marker value, such as `{ error: true }`, that a later [If](/reference/conditions/if) block can branch on.
* **Non-deterministic code makes runs hard to trust.** Non-deterministic means the same input can produce a different result. When the result drives a write, calls to `Date.now()`, `Math.random()`, or an external service can each return a different value on every run and every retry. Keep code that controls writes deterministic. Move randomness to the [Random number](/reference/calculations/random-number) block so the value is recorded in run history.
* **Long jobs hit the 100-second timeout.** The block stops at 100 seconds and fails, so polling loops and large batch loops are the wrong fit. Split the work across a [Loop](/reference/utilities/loop) block, or call an external service with [Send HTTP request](/reference/utilities/send-http-request).

## Examples

### Normalizing a lead source field

This function maps inconsistent source labels onto one canonical value before the workflow writes the record. Canonical means the single agreed-on spelling you store, such as `Paid search` for every variant of that source.

```js theme={null}
export async function main(inputs) {
  const raw = (inputs.source || "").toLowerCase().trim();
  const map = {
    "google ads": "Paid search",
    adwords: "Paid search",
    linkedin: "Social",
    "li ads": "Social",
  };
  return { source: map[raw] || "Other" };
}
```

With `inputs.source` set to `LI Ads`, the block returns `{ "source": "Social" }`. An [Update record](/reference/records/update-record) block then writes that value to the lead's source attribute as `{{ steps.execute_code.result.source }}`.

### Scoring a record from several attributes

This function combines three mapped variables into one explainable score for a deal owned by Priya Sharma.

| Field | Value |
| - | - |
| Code | the `main` function in this example |
| `inputs.employees` | `220` |
| `inputs.uses_competitor` | `true` |
| `inputs.opened_email` | `true` |

```js theme={null}
export async function main(inputs) {
  let score = 0;
  if (inputs.employees >= 200) score += 40;
  if (inputs.uses_competitor) score += 20;
  if (inputs.opened_email) score += 10;
  return { score };
}
```

The block returns `{ "score": 70 }`. A downstream [If](/reference/conditions/if) block reads it as `{{ steps.execute_code.result.score }}` to route deals scoring 50 or higher to a sales-ready stage.

## Credit cost worked example

Execute code is priced as **Variable (complexity-based)**, so there is no single fixed-per-run figure. A heavier function costs more than a light one. To estimate spend, sample a few real runs in run history. Then multiply the cost you observe by your monthly volume. Take a lead-normalization workflow that fires on 2,000 inbound leads a month. Measure the cost of one representative run and multiply by 2,000. Compare that total to your monthly allowance on [Credits & pricing explained](/reference/credits-and-pricing). Logic and lookup blocks elsewhere in the same workflow stay free. Only the Execute code step and any write blocks add to the total.

## Source

[Attio Workflows block library](https://attio.com/help/reference/automations/workflows/workflows-block-library).

Last reviewed against the Attio Help Center, June 2026.

## See also

* [Formula](/reference/calculations/formula)
* [Aggregate values](/reference/calculations/aggregate-values)
* [Parse JSON](/reference/utilities/parse-json)
* [Variables & data passing](/explanation/variables)
* [Credits & pricing explained](/reference/credits-and-pricing)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.