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:
| Field | Type | Required | Description |
|---|---|---|---|
output | Record<string, unknown> | Yes | Data for downstream nodes. Must match the output_schema from the manifest. |
status | "success" | "error" | Yes | Whether the action completed successfully |
error_message | string | No | Human-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
- Config Panel -- build a custom config UI
- Testing -- test your action node locally