Custom code nodes run JavaScript (Node.js 24) or Python (3.13) in an isolated sandbox. Use them when no built-in node fits, bespoke transformations, calls to obscure APIs, or one-off data work.
#Runtimes
- JavaScript: Node.js 24
- Python: 3.13
Standard library only. Third-party packages are not installed in the sandbox, if you need a library, use an HTTP Request node against a service that does the work.
#Workflow context
The sandbox writes a context.json file you can read at runtime. It contains:
variables, current workflow variable valuestrigger, the data that started the workflow (webhook payload, schedule metadata, etc.)secrets, credentials you’ve referenced on this node, keyed by alias
JavaScript
const fs = require("fs");
const ctx = JSON.parse(fs.readFileSync("./context.json", "utf8"));
const customer = ctx.variables.customerName;
const apiKey = ctx.secrets.STRIPE_KEY;
const payload = ctx.trigger;
Python
import json
with open("context.json") as f:
ctx = json.load(f)
customer = ctx["variables"]["customerName"]
api_key = ctx["secrets"]["STRIPE_KEY"]
payload = ctx["trigger"]
#Returning data
Print JSON to stdout. Downstream nodes get the parsed object.
console.log(JSON.stringify({ total: 42, status: "processed" }));
import json
print(json.dumps({"total": 42, "status": "processed"}))
Non-JSON stdout becomes { rawOutput: string }. Empty stdout produces null. stderr is captured for debugging but not passed downstream.
#Output declarations
You can declare typed output fields in the node config so downstream nodes get tag suggestions in the variable picker. Each declaration is a { field, type, description? } where type is one of String, Number, Boolean, Object, or Array.
This is purely for UX, your code can output whatever JSON it wants; declarations just teach the variable picker in the builder what to suggest.
#Secrets
Reference workspace secrets by adding them to the node’s credentials list. Each entry is a credential ID plus an alias (must be a valid identifier). The alias becomes the key in context.secrets. Don’t paste keys into the code body, secrets are encrypted at rest and never logged.
#Limits
| Limit | Value |
|---|---|
| Max code size | 50 KB |
| Default timeout | 120 seconds |
| Max timeout | 300 seconds (5 minutes) |
| Min timeout | 1 second |
Each execution spins up a fresh sandbox. No state persists between runs.
#When something goes wrong
Common failures:
- Out of memory: your code allocated more than the sandbox allows. Process data in chunks or filter before loading.
- Timeout: execution went past the configured timeout. Increase it (up to 300s) or break the work into multiple steps.
- Crash: your code threw an unhandled exception. Check the run detail view, stderr is captured there.
- Sandbox failure: rare infrastructure issue. Retry; contact support if it persists.
#Cost
Each execution is 5 Workflow Credits regardless of runtime, duration, or success. See billing .
#Security
Code runs in an isolated Vercel Sandbox :
- No host environment access
- No internal network access
- A fresh sandbox per run, destroyed afterwards
- Secrets only available via the credentials list you configured
#Example: classify and route
const fs = require("fs");
const ctx = JSON.parse(fs.readFileSync("./context.json", "utf8"));
const order = ctx.trigger;
const total = order.items.reduce((sum, i) => sum + i.price * i.qty, 0);
const tier = total > 500 ? "priority" : "standard";
console.log(JSON.stringify({ total, tier, itemCount: order.items.length }));
A downstream Condition node can branch on tier to route priority orders through extra approval and standard orders straight to fulfillment.