Rills Docs
Workflows

Custom Code Execution

Run JavaScript or Python in your workflows with sandboxed execution, workflow context access, and structured output

Run JavaScript (Node.js 24) or Python 3.13 code inside your workflows. Custom code nodes execute in isolated sandboxes with access to workflow variables and trigger data. Output is captured from stdout and passed to downstream nodes.

Supported runtimes

RuntimeIdentifier
JavaScriptNode.js 24
PythonPython 3.13

Select the runtime in the workflow builder when configuring a custom code node. Only standard library capabilities are available -- the sandbox does not install third-party packages.

Workflow context

Your code receives a context.json file containing the current workflow state:

  • variables -- Current workflow variable values.
  • trigger -- The data that started the workflow (webhook payload, schedule metadata, etc.).
  • env -- Environment variables explicitly configured for the sandbox.

JavaScript:

const fs = require("fs");
const context = JSON.parse(fs.readFileSync("./context.json", "utf8"));

const name = context.variables.customerName;
const payload = context.trigger;

Python:

import json

with open("context.json") as f:
    context = json.load(f)

name = context["variables"]["customerName"]
payload = context["trigger"]

Returning output

Print JSON to stdout. The output is parsed and made available to downstream nodes as structured data.

// Structured output (downstream nodes receive the parsed object)
console.log(JSON.stringify({ total: 42, status: "processed" }));
import json
print(json.dumps({"total": 42, "status": "processed"}))

Non-JSON stdout is returned as { rawOutput: string }. stderr is captured separately for debugging but not passed to downstream nodes.

Empty stdout produces null output.

Resource limits

LimitValue
Maximum code size50 KB
Default timeout120 seconds
Minimum timeout1 second
Maximum timeout300 seconds

Timeout is configurable per node in the workflow builder. If the configured value falls outside the 1--300 second range, it is clamped to the nearest boundary.

Error codes

When execution fails, the node produces an error with one of these codes:

CodeExit codeMeaning
OOM_KILLED137Process exceeded memory limits
TIMEOUT143Execution exceeded the configured timeout
EXECUTION_FAILEDNon-zeroUnhandled exception in user code
SANDBOX_ERROR-1Infrastructure failure (rare)

The error message includes stderr output for debugging EXECUTION_FAILED cases.

Credit cost

Each custom code execution costs 5 Workflow Credits. This applies regardless of runtime, duration, or whether the code succeeds or fails. See billing for credit quotas per plan.

Security model

Code runs in an isolated Vercel Sandbox:

  • No access to the host environment or other workflows.
  • No network access to internal services.
  • Credentials are only available via explicitly configured environment variables.
  • A fresh sandbox is created per execution -- no state persists between runs.
  • The sandbox is destroyed after execution completes, even on failure.

Example: enrich and route

JavaScript -- Read trigger data, compute a result, output structured JSON:

const fs = require("fs");
const context = JSON.parse(fs.readFileSync("./context.json", "utf8"));

const order = context.trigger;
const total = order.items.reduce((sum, item) => sum + item.price * item.qty, 0);
const tier = total > 500 ? "priority" : "standard";

console.log(JSON.stringify({ total, tier, itemCount: order.items.length }));

Python -- Same logic:

import json

with open("context.json") as f:
    context = json.load(f)

order = context["trigger"]
total = sum(item["price"] * item["qty"] for item in order["items"])
tier = "priority" if total > 500 else "standard"

print(json.dumps({"total": total, "tier": tier, "itemCount": len(order["items"])}))

A downstream condition node can branch on tier to route priority orders to a human approval step and standard orders to automatic fulfillment.