Skip to main content

Run Script Action

The run_script action executes JavaScript code in a sandboxed QuickJS runtime. Use it for complex transformation logic that goes beyond what template interpolation can handle.

Configuration

{
"type": "run_script",
"name": "Transform payload for Slack",
"config": {
"code": "function handle(request) {\n return [{\n url: \"https://hooks.slack.com/services/T00/B00/xxx\",\n method: \"POST\",\n body: { text: \"New event: \" + request.body.type }\n }];\n}",
"response_variable": "script_result"
}
}

Fields

FieldTypeRequiredDescription
codestringYesJavaScript code containing a handle(request) function (max 50 KB)
response_variablestringNoChain variable name to store the script result

The handle function

Every script must define a handle function that receives the incoming request and returns an array of actions.

Request object

PropertyTypeDescription
methodstringHTTP method ("GET", "POST", etc.)
pathstringURL path after the endpoint slug
headersobjectRequest headers as key-value pairs
queryobjectParsed query parameters
bodyanyParsed JSON body, or raw string if not JSON

Action object

Each action in the returned array describes an HTTP request for Hooklistener to send:

PropertyTypeRequiredDescription
urlstringYesTarget URL
methodstringNoHTTP method (defaults to "POST")
headersobjectNoCustom headers
bodyanyNoRequest body (string or object — objects are JSON-serialized)

Return an empty array [] to skip forwarding for this webhook.

Response variable

When response_variable is set:

On success:

{"ok": true, "result": [{"url": "...", "method": "POST", "body": {...}}]}

On error:

{"ok": false, "error": "Script execution timed out"}

Runtime environment

Scripts run in a QuickJS JavaScript engine compiled to WebAssembly. Each execution gets a fresh, isolated runtime with:

  • No filesystem access — scripts cannot read or write files
  • No network access — scripts cannot make HTTP requests (use returned actions instead)
  • No host access — scripts cannot access the server environment
  • No external modulesrequire and import are not available

Only vanilla JavaScript (ES2020) is supported. This includes arrow functions, destructuring, template literals, Map, Set, Promise (sync only), and other standard built-ins.

Limits

LimitValue
Execution timeout5 seconds
Memory per execution16 MB
Max script size50 KB
Max actions returned10 per execution
Max body per action1 MB

Error handling

When a script fails, the error is recorded on the action execution log.

Common errors

ErrorCause
Script execution timed outScript exceeded the 5-second time limit
Script must return an arrayhandle returned a non-array value
Script returned invalid JSONReturn value could not be serialized
Invalid action: missing urlAn action object was missing the required url field
Script error: ...A JavaScript runtime error (e.g., TypeError, ReferenceError)

Debugging tips

  • Use the Test button — test your script against the latest captured request in the Dashboard editor
  • Start simple — begin with a script that returns the request body unchanged, then add logic incrementally
  • Validate JSON — make sure constructed objects are valid JSON-serializable values
  • Return an empty array to filter — use return [] to intentionally skip events

Example: Route by event type

function handle(request) {
var eventType = request.body.type;

var routes = {
"payment_intent.succeeded": "https://billing.example.com/webhook",
"customer.created": "https://crm.example.com/webhook",
"invoice.paid": "https://accounting.example.com/webhook"
};

var url = routes[eventType];
if (!url) {
return [];
}

return [
{
url: url,
method: "POST",
headers: { "Content-Type": "application/json" },
body: request.body
}
];
}

Example: Transform and fan-out

function handle(request) {
var body = request.body;

return [
{
url: "https://api.example.com/webhook",
method: "POST",
headers: { "Content-Type": "application/json" },
body: body
},
{
url: "https://hooks.slack.com/services/T00/B00/xxx",
method: "POST",
body: {
text: "New " + body.type + " event from " + (body.customer || "unknown")
}
}
];
}

FAQ

Can I make HTTP requests from within a script? No. The sandbox has no network access. Return HTTP requests as actions — Hooklistener executes them for you.

Can I use npm packages or import modules? No. Scripts run in a sandboxed QuickJS runtime with no module system. Only vanilla JavaScript is available.

What JavaScript features are supported? ES2020 via QuickJS — arrow functions, destructuring, template literals, Map, Set, JSON, Date, Math, Array methods, and other standard built-ins.

Can I use async/await? No. The handle function runs synchronously. There is no event loop or asynchronous I/O.

Is there a way to log output? No. There is no console.log or equivalent. Use the Test button in the Dashboard to verify your script's return value.

Can I access the Datastore from a script? Not directly. Scripts run in an isolated sandbox. Use a Store Variable action before or after the script to interact with the Datastore.