New: the AI firewall watches every module and explains each incident in plain words. See how it works

The script block

Run your own JavaScript in a flow – what the code sees and returns, context, helpers, limits, errors, testing, and worked examples.

The Script (JavaScript) block runs your code on each message. Use it when the other blocks cannot express what you need: decoding an unusual binary format, looping over a list, building a complex object. Prefer the ready blocks when they fit: they are easier to read, need no script allowance and run faster.

Settings

SettingMeaningDefaultValues
codeThe scriptan examplerequired; up to 64 KiB
outputsNumber of outputs11 to 4 (o0 to o3)
labelsOutput names, comma-separatedemptyletters, digits, _, space and -; one per output, all different

Each script block in a deployed flow counts toward your plan’s Script blocks (3, 6 or 29 by IoT tier). A deploy that contains a script is a Code-risk deploy: it asks for the flow’s name to be typed and is audited (“Script changed”).

What the code sees

The editor shows the signature over the code field: msg, node, context, flow, global, env → return msg | [a, b] | [[a, b]] | "label" | nothing.

NameWhat it is
msgThe message, a copy you may change. msg.metadata is writable except the system fields; msg.msgType and msg.originator are read-only.
node.send(msg)Send a message on output 1 at once (node.send([a, b]) picks outputs as a return does)
node.status({ fill, shape, text })The status shown under the block
node.warn(value), node.error(text)Write to the block’s log
context.get("key"), context.set("key", value)A store of this block
flow.get(…), flow.set(…)The flow’s variables, shared with flow.* paths in other blocks
global.get(…), global.set(…)The organisation’s variables (global.*)
env.get(name)An environment value
console.log(value)The same log as node.warn

Use the key persist:name for the persistent store, for example flow.set("persist:total", 12). Setting a key to undefined deletes it.

Context writes are applied after the script has run. The first write over a store’s limit stops the rest, and the message leaves on error with limit; earlier writes stay.

What it may return

ReturnEffect
return msgSends the message on output 1
return [a, b]a on output 1, b on output 2 (up to 4); null sends nothing on that output
return [[a, b]]Both on output 1
return "alarm"The incoming message on the output labelled alarm
Nothing (undefined, null, [])Nothing is sent; the message stops here

At most 64 messages per run. A fresh object such as return { payload: 1 } is completed from the input; one that sets an engine field to something else fails with readonly_field.

Helpers

NamespaceFunctions
bytesfromHex, toHex, fromBase64, toBase64, sha256, toText, fromText, readUint, readInt, readFloat, writeUint, writeInt, writeFloat; also TextEncoder, TextDecoder
numround(value, digits), toHex(value, { bytes, bigEndian, prefix })
bitsfromBytes, fromInt, toInt, get
objflatten(value, { separator, exclude, pathInKey })
timeparse, format, add, fields, with patterns such as yyyy-MM-dd HH:mm:ss and IANA zones

Not available: require, import(), eval, Function, fetch, timers (use a Delay block), process, Buffer, WebAssembly. The script has no network, no files and no credentials. Date is the real clock.

Limits

LimitValue
CPU time per message50 ms (your plan may allow up to 200 ms)
Memory32 MiB
Code64 KiB
Messages returned64
Message size, in or out256 KiB
Context passed in64 KiB

Three kills in a row (timeout or memory) block the script for 60 seconds: messages then fail with blocked without running. The CPU time of every run counts toward the month’s Script CPU on the usage card.

Errors

The error output carries msg.error.code:

CodeMeaning
scriptThe code threw; line and col say where
timeout, memoryOver the CPU time or the memory
limitMore than 64 messages returned, or a context write over its limit
msg_too_bigA message over 256 KiB
unknown_labelReturned a label no output has
bad_returnReturned something that is not a message, a list or a label (or a promise)
readonly_fieldChanged msgType, originator or a system field
blockedPaused after three kills
host_busyThe sandbox stayed busy after a few tries
script_unavailableThe script service is not available

Test the script

Test the script in the block panel runs the code once on the real sandbox: “Nothing is deployed; Save puts the code into the block.” Fill in Payload (JSON), Metadata (JSON object), Message type, Originator type and Originator id, optionally Context (optional JSON) as { "node": {…}, "flow": {…}, "global": {…} }, and select Run. The result shows each output, the log, the context written and the CPU time. Save into the block keeps the code.

Examples

Decode a 4-byte float from a raw hex payload:

const data = bytes.fromHex(msg.payload.raw_hex);
msg.payload = { level_m: num.round(bytes.readFloat(data, 0, 4, { bigEndian: true }), 3) };
return msg;

Split one message with a list of sensors into one reading per sensor key:

// in: { "sensors": [ { "id": "t1", "v": 21.5 }, { "id": "t2", "v": 19.0 } ] }
const out = {};
for (const s of msg.payload.sensors || []) out[`temp_${s.id}`] = s.v;
msg.payload = out; // { temp_t1: 21.5, temp_t2: 19 }
return msg;

Route by a computed class to labelled outputs (labels ok,warn,crit):

const t = msg.payload.temperature;
if (typeof t !== 'number') return null;
return t > 8 ? 'crit' : t > 6 ? 'warn' : 'ok';

Keep a running total across messages:

const total = (flow.get('persist:litres_total') || 0) + (msg.payload.litres || 0);
flow.set('persist:litres_total', total);
msg.payload.litres_total = total;
return msg;

When not to use a script

NeedUse instead
A formulaCalculate
Rename, copy or delete keysChange
Binary fieldsBytes
Branching on a valueSwitch or If / else
Consumption from a counterDelta
WaitingDelay, Trigger, Timer

See Data transform blocks and Flow limits.