Skip to main content

Action Nodes

Action nodes perform work: call an external API, transform data, send a message, or write to a database. They receive input data from the upstream node and produce output data for downstream nodes.

Create a project

lumio init my-action-node

When prompted:

  • Category: automation_node
  • Node type: Action

Handler

Define your action handler in server/handler.ts:

import { defineAutomationAction } from "@zaflun/lumio-sdk/server";

defineAutomationAction(async (ctx) => {
const response = await ctx.fetch("https://api.example.com/data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: ctx.nodeConfig.query }),
});
const data = await response.json();

return {
output: { result: data.result, count: data.items.length },
status: "success",
};
});

Return value

The handler must return an ActionResult:

FieldTypeRequiredDescription
outputRecord<string, unknown>YesData for downstream nodes. Must match the output_schema from the manifest.
status"success" | "error"YesWhether the action completed successfully
error_messagestringNoHuman-readable error message (only when status: "error")

When status is "error", the automation engine stops the current execution branch and records the error in the execution history.

Input/output schemas

The manifest declares what data the node accepts and produces:

{
"node": {
"input_schema": {
"type": "object",
"properties": {
"message": { "type": "string", "title": "Message to send" },
"channel": { "type": "string", "title": "Channel name" }
},
"required": ["message"]
},
"output_schema": {
"type": "object",
"properties": {
"message_id": { "type": "string" },
"sent_at": { "type": "string" }
}
}
}
}

The input_schema drives the auto-generated fallback config UI when no custom editor surface is provided. The output_schema defines which fields downstream nodes can reference via template syntax (\{\{ext_abc12.message_id\}\}).

Output template references

Downstream nodes reference your action output using template syntax:

{{ext_abc12.message_id}}
{{ext_abc12.sent_at}}

Format: ext_{short_id}.{output_field}. Fields are derived from the output_schema properties.

Example: Send a Discord webhook

import { defineAutomationAction } from "@zaflun/lumio-sdk/server";

defineAutomationAction(async (ctx) => {
const webhookUrl = ctx.installConfig.discord_webhook_url;
const message = ctx.inputData.message ?? ctx.nodeConfig.default_message;

const res = await ctx.fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content: message }),
});

if (!res.ok) {
return {
output: {},
status: "error",
error_message: `Discord webhook failed: ${res.status}`,
};
}

return {
output: { sent: true, status_code: res.status },
status: "success",
};
});

Next steps