Automation Flow Nodes
Create visual trigger, condition, and action nodes for Medusa's automation engine.
Medusa features a visual node-based automation flow editor where administrators create reactive event pipelines. Modules can supply custom action and condition nodes that appear in the canvas palette and execute seamlessly within automation workflows.
Automation nodes reside in resources/nodes/ and load from every *.js file located directly in that directory.
Base Class Definition
import { Nodes } from "#medusa/modules";
export class SendAlertNode extends Nodes {
constructor(medusa) {
super(medusa, {
name: "send_alert",
title: "Send Item Alert",
category: "Example",
description: "Dispatches an alert notification to a specified channel.",
icon: "TbBell",
accent: "indigo",
hasTarget: true,
hasSource: true,
fields: [
{
key: "channel",
type: "channel",
label: "Destination Channel",
default: ""
},
{
key: "message",
type: "text",
label: "Alert Message",
placeholder: "Item %item_name% was updated.",
default: ""
}
],
branches: []
});
}
async run(context) {
const channelId = context.text("channel", "");
const template = context.text("message", "Item updated.");
if (!channelId) return { success: false };
const formatted = context.fill(template);
await this.medusa.discord.post(channelId, { content: formatted });
return { success: true };
}
}Conditional Branching Nodes
Nodes that evaluate conditions define branches and return the targeted branch identifier in run():
import { Nodes } from "#medusa/modules";
export class CheckStockNode extends Nodes {
constructor(medusa) {
super(medusa, {
name: "check_stock",
title: "Check Item Stock",
category: "Example",
description: "Evaluates whether an item has available inventory.",
icon: "TbCheck",
accent: "emerald",
fields: [
{ key: "identifier", type: "text", label: "Item Identifier", default: "%item_id%" },
{ key: "minStock", type: "number", label: "Minimum Quantity", default: 1 }
],
branches: [
{ id: "available", label: "In Stock" },
{ id: "empty", label: "Out of Stock" }
]
});
}
async run(context) {
const identifier = context.text("identifier", "");
const minStock = context.number("minStock", 1);
const handler = this.medusa.modules.handlers.get(this.module, "items");
if (!handler) return { success: true, branch: "empty" };
const item = await handler.getByIdentifier(identifier).catch(() => null);
if (item && item.quantity >= minStock) {
return { success: true, branch: "available" };
}
return { success: true, branch: "empty" };
}
}Constructor Options
The configuration object passed to super(medusa, meta) accepts the following properties:
| Property | Type | Default | Description |
|---|---|---|---|
name | string | Required | Unique node identifier. Full type key becomes <module>:<name>. |
title | string | name | Display title shown on the node card in the flow builder. |
category | string | "General" | Palette category where this node appears in the node selector. |
description | string | "" | Subtitle describing what the node does. |
icon | string | "TbSparkles" | Tabler icon component name (e.g. "TbBell", "TbCoins", "TbShield"). |
accent | string | "indigo" | Mantine theme color accent applied to the node header and handles. |
hasTarget | boolean | true | When true, renders an incoming execution handle on the left. |
hasSource | boolean | true | When true, renders an outgoing execution handle on the right. |
fields | Array<object> | [] | List of field configuration inputs rendered inside the node body. |
branches | Array<object> | [] | List of labeled output branches for conditional flows. |
order | number | 0 | Sort weight within the palette category. |
Node Field Types
Fields declared inside fields define controls displayed on the canvas:
| Field Type | Control Rendered | Context Method |
|---|---|---|
"text" | Single-line text input | context.text(key, fallback) |
"number" | Numeric input | context.number(key, fallback) |
"select" | Dropdown menu with options array | context.text(key, fallback) |
"duration" | Numeric input paired with unit dropdown | context.number(key, fallback) |
"boolean" | Toggle switch | context.boolean(key, fallback) |
"channel" | Discord channel picker | context.text(key, fallback) |
"role" | Discord role picker | context.text(key, fallback) |
Execution Context
The context object provided to run(context) supplies runtime data, trigger parameters, and helper methods:
| Property / Method | Return Type | Description |
|---|---|---|
context.guildId | string | Discord snowflake ID of the active server. |
context.data | object | Raw field configuration values configured on this node instance. |
context.text(key, fallback) | string | Evaluates a text field with variable interpolation. |
context.number(key, fallback) | number | Resolves a numeric field value. |
context.boolean(key, fallback) | boolean | Resolves a boolean field value. |
context.resolveMember() | Promise<GuildMember> | Resolves the Discord member triggering the flow. |
context.fill(template) | string | Replaces variable placeholders in arbitrary text. |

