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
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | JavaScript code containing a handle(request) function (max 50 KB) |
response_variable | string | No | Chain 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
| Property | Type | Description |
|---|---|---|
method | string | HTTP method ("GET", "POST", etc.) |
path | string | URL path after the endpoint slug |
headers | object | Request headers as key-value pairs |
query | object | Parsed query parameters |
body | any | Parsed JSON body, or raw string if not JSON |
Action object
Each action in the returned array describes an HTTP request for Hooklistener to send:
| Property | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Target URL |
method | string | No | HTTP method (defaults to "POST") |
headers | object | No | Custom headers |
body | any | No | Request 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 modules —
requireandimportare 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
| Limit | Value |
|---|---|
| Execution timeout | 5 seconds |
| Memory per execution | 16 MB |
| Max script size | 50 KB |
| Max actions returned | 10 per execution |
| Max body per action | 1 MB |
Error handling
When a script fails, the error is recorded on the action execution log.
Common errors
| Error | Cause |
|---|---|
| Script execution timed out | Script exceeded the 5-second time limit |
| Script must return an array | handle returned a non-array value |
| Script returned invalid JSON | Return value could not be serialized |
| Invalid action: missing url | An 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.