Custom Cloud Code¶
This block runs a snippet of JavaScript as a step in your flow. You pass values in as named arguments, write your logic, and the value you return becomes the block's result.
How it works¶
Each time the flow reaches this block, your code runs once, on the server, as a single step.
Data flows in through the block's Arguments list: each argument is a name plus a value
expression that reads a previous step's result, and inside the Open Code Editor modal that
name is a ready-to-use top-level variable. The values arrive already typed as the arrays,
objects, and numbers they were upstream - nothing is stringified - so an array stays an array
you can call .filter() on. Whatever your code returns becomes the block's result. Data flows
back out under the alias you set in Reference Result Data As: later blocks pick that alias in
the Expression Editor and read into it, for example Custom Cloud Code Result → openCount.
The code runs in its own isolated sandbox, created fresh on every run. Because it is a brand-new isolate each time, nothing you set persists between runs, and that same isolation is what keeps each run fast and predictable.
When to use it¶
Reach for it, from the Actions group in the block palette, when a few lines of code would be cleaner than wiring up several blocks to do the same thing. Reshaping a payload, computing a value, or combining two blocks' outputs is often a short snippet, where the no-code equivalent might be a chain of Transform Data blocks that clutters the flow. It is also how you do something genuinely custom that no built-in block offers. The one thing it is not for is reaching outside the flow: the code has no network or file access, so let blocks like HTTP Request and the database actions handle input and output, and use your code to work on what they return.
Example¶
Suppose an upstream HTTP Request block fetched a list of orders. In the Arguments list, add one named orders whose value expression is the HTTP Request block's result, then write the code in the Open Code Editor modal to shape that array down to what the next step needs:
// "orders" is the array returned by the HTTP Request block
const open = orders.filter(order => order.status === "open");
return {
openCount: open.length,
openIds: open.map(order => order.id),
};
The block's result is the object you return, here { openCount, openIds }. It is read through the alias set in Reference Result Data As, which for this block is Custom Cloud Code Result. A later step picks that alias in the Expression Editor and reads into the returned object - a Condition might gate on Custom Cloud Code Result → openCount > 0, or a Send Email block might list Custom Cloud Code Result → openIds in its body - so the count and ids the code computed drive the rest of the flow.
As an AI Agent tool¶
Custom Cloud Code can also be handed to an AI Agent as a tool, so the agent runs your snippet on its own when it needs to - to do a calculation or reshape some data mid-task. You attach it from the agent block's tools drawer.
Used this way, and only this way, its arguments gain two extra fields under Prepare for AI Agent:
- Value Type - the data type the agent must supply, such as a string or a number.
- Description - what the argument is for, written for the agent. Together with the type, this is how the agent knows what to pass, and when to reach for the tool.
The value then decides who supplies each argument:
- Leave a value empty and it becomes one of the tool's parameters - the agent fills it at call time from the type and description.
- Set a value, static or an expression, and it is fixed input the agent cannot see or change.
As an ordinary block step, none of this appears: an argument is just a name and a value.
Configuration¶
| Field | Description |
|---|---|
| Arguments | The values your code can use. Each argument has a name and a value, where the value is either static or an expression that references a previous block's result. Inside the code, each argument is available as a top-level variable of the same name. |
| Code | The JavaScript to run. Press Open Code Editor to write it in the full-screen Code Editor modal, where the Arguments panel sits beside the editor. Return the value that becomes this block's result. |
Common settings (available on most blocks):
| Field | Description |
|---|---|
| Name | A label for this block on the canvas. |
| Reference Result Data As | The alias used to reference this block's result in later blocks. |
| Assign to a Variable | Optionally store the result in a Data Bucket variable too; you choose the bucket and the variable name. |
| Skip Block | When on, the block is skipped during execution and the value in Simulated Result is used as its output. |
| Logging | What to log to the Logging panel while the flow is LIVE, both on start and on completion. |
| Notes | Freeform notes for documenting the block; they do not affect execution. |
Behavior¶
- async/await and Promises are supported; the block waits for them before capturing the result.
- If you return a Promise, it is awaited and unwrapped automatically.
- Throwing an Error sets the block's status to Error, using the error's message.
- Throw a real Error object, not a bare string or number; a non-Error throw still fails the block, but its message is lost.
- Arguments arrive as native, typed JavaScript values (arrays, objects, numbers, null); nothing is stringified.
- A floating, un-awaited promise rejection is ignored, and the block returns normally.
Limitations¶
- It is a V8 sandbox (roughly ES2023), not Node. There is no require, fetch, process, Buffer, crypto, setInterval, btoa, or structuredClone.
- The server runs in UTC with the en-US locale; full ICU data is available.
- The result is serialized as JSON. Map, Set, and RegExp become empty objects; NaN and Infinity become null; and undefined, functions, and Symbols are dropped.
- Code runs in non-strict (sloppy) mode in a fresh isolate each time, so nothing persists between runs or instances.
- Execution is capped at 10 seconds. Around 400MB of allocation is fine, and deep recursion raises a RangeError.
- Return an object, not a bare scalar.
return 42(or any bare number) fails with "Invalid status code: 42" - wrap it, for examplereturn { value: 42 }. - A value that cannot be JSON-serialized - a BigInt, or an object with a circular reference - does not error. The block reports Success but silently replaces your result with an error object like
{ message: "Do not know how to serialize a BigInt" }, which downstream blocks then read as if it were valid. Return only JSON-serializable values.

