Medusa/developer documentation/Automation Flow Nodes

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

modules/example/resources/nodes/sendAlert.js
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: "Bell",
            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():

modules/example/resources/nodes/checkStock.js
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: "Check",
            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:

PropertyTypeDefaultDescription
namestringRequiredUnique node identifier. Full type key becomes <module>:<name>.
titlestringnameDisplay title shown on the node card in the flow builder.
categorystring"General"Palette category where this node appears in the node selector.
descriptionstring""Subtitle describing what the node does.
iconstring"Sparkles"Lucide icon identifier (e.g. "Sparkles", "Bell", "Coins", "Shield").
accentstring"indigo"Tailwind color accent applied to the node header and handles (indigo, violet, blue, emerald, teal, rose, amber, cyan, sky, fuchsia, purple).
hasTargetbooleantrueWhen true, renders an incoming execution handle on the left.
hasSourcebooleantrueWhen true, renders an outgoing execution handle on the right.
fieldsArray<object>[]List of field configuration inputs rendered inside the node body.
branchesArray<object>[]List of labeled output branches for conditional flows.
ordernumber0Sort weight within the palette category.

Node Field Types

Fields declared inside fields define controls displayed on the canvas:

Field TypeControl RenderedRead With
"text"Single-line text input (mono: true for code style)context.text(key, fallback)
"textarea"Multi-line text inputcontext.text(key, fallback)
"number"Numeric inputcontext.number(key, fallback)
"select"Dropdown menu with an options arraycontext.data[key]
"switch"Toggle switchcontext.bool(key)
"duration"Numeric input paired with a unit dropdown (unitKey, unitDefault)context.durationMs(key, unitKey)
"channel"Channel picker for the flow's servercontext.data[key]
"role"Role picker for the flow's servercontext.data[key]
"category"Category picker for the flow's servercontext.data[key]
"server"Picker of the other servers the editor can managecontext.server(context.data[key])
"bot"Picker of the bots that are in the flow's server (empty = the flow's bot)context.useBot(context.data[key])
"flow"Picker of the other flows in the servercontext.invoke(context.data[key])

Pickers never fall back to text inputs; an empty list shows an empty state such as "No roles found".

Execution Context

The context object provided to run(context) supplies runtime data, trigger parameters, and helper methods:

Property / MethodReturn TypeDescription
context.guildIdstringId of the server the node is acting in.
context.guildGuildThe server the node is acting in. A Switch Server node changes it.
context.member, context.userGuildMember, UserWho triggered the flow. Empty for schedules and webhooks.
context.channelTextChannelThe routed channel, or the trigger's channel.
context.messageMessageThe trigger message for message, reaction and message context menu triggers.
context.client, context.botClient, ClientUserThe bot running this part of the flow.
context.flow, context.nodeobjectThe stored flow and this node.
context.dataobjectRaw field values configured on this node instance.
context.tokensobjectAll %tokens% of the run.
context.text(key, fallback)stringReads a field with token interpolation.
context.number(key, fallback)numberReads a numeric field with token interpolation.
context.bool(key)booleanReads a switch field.
context.durationMs(amountKey, unitKey)numberConverts a duration field to milliseconds.
context.resolve(value)stringReplaces tokens in any text.
context.set(key, value)voidStores a token for later nodes.
context.resolveMember(spec)Promise<GuildMember>Resolves a member from an id, mention or token; defaults to the node's target field or the triggering member.
context.resolveMessage(reference)Promise<Message>Resolves a message link or id; empty returns the trigger message.
context.server(serverId)Promise<Guild>Another authorized server as seen by the current bot, or null.
context.route(channel)voidSends later Embed and Send Message nodes to this channel.
context.useBot(key)Promise<Bot>Runs later nodes as another bot that is in the server.
context.invoke(flowId)Promise<any>Runs another enabled flow in the same server and returns its result.

run() returns { success: true }, { branch: "<branch id>" } to follow one branch, or { stop: true } to end the run. Throwing an error marks the run as failed at this node and continues on the node's error handle if it has one.