Skip to main content

Trigger Nodes

Trigger nodes are entry points for automations. They fire when an external event occurs (webhook) or when a periodic check finds new data (polling).

Trigger modes

ModeDescriptionUse case
webhookExternal service sends HTTP POST to a generated URLShopify orders, GitHub events, Stripe payments
pollingWorker periodically calls your handler to check for eventsRSS feeds, API endpoints without webhooks
bothSupport both webhook and pollingServices that offer webhooks but need a fallback

Create a project

lumio init my-trigger

When prompted:

  • Category: automation_node
  • Node type: Trigger
  • Trigger mode: Choose webhook, polling, or both

Handler

Trigger handlers use defineAutomationTrigger with onWebhook and/or onPoll methods:

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

defineAutomationTrigger({
onWebhook: async (ctx) => {
const payload = ctx.webhookBody;
if (payload.event !== "order.created") {
return { fired: false };
}
return {
fired: true,
output: {
order_id: payload.id,
customer_name: payload.customer.name,
total: payload.total_price,
currency: payload.currency,
},
};
},
onPoll: async (ctx) => {
const lastCheck = await ctx.cache.get("last_check_ts");
const res = await ctx.fetch(
`https://api.example.com/events?since=${lastCheck}`
);
const events = await res.json();

if (events.length === 0) return null;

await ctx.cache.set("last_check_ts", Date.now().toString());
return {
fired: true,
output: events[0],
};
},
});

Return value

Both onWebhook and onPoll return a TriggerResult (or null):

FieldTypeRequiredDescription
firedbooleanYesWhether the trigger should start the automation
outputRecord<string, unknown>Only when fired: trueEvent data. Must match the output_schema from the manifest.

Returning null or { fired: false } silently ignores the event. No automation runs.

Webhook flow

When a user saves an automation that contains your webhook trigger node (trigger_mode webhook or both):

  1. Lumio registers the trigger and generates a unique webhook URL plus a random URL-safe secret. The secret is minted once and preserved across later edits — re-saving the automation never rotates it, so a URL already configured in an external service keeps working. Removing the trigger node disables its registration (the secret is restored if the node is re-added).
  2. The user reads the URL + secret from the Automation Builder (backed by the automationWebhookUrl GraphQL query / GET /v1/automation/webhooks/{install_id}/{automation_id}) and configures the external service with them.
  3. External service sends POST /v1/automation/webhooks/{extension_id}/{install_id} with X-Webhook-Secret header
  4. The API validates the secret (constant-time) and forwards to the Automation Worker
  5. Your onWebhook handler runs in V8
  6. If fired: true, the automation engine runs the full flow

The webhook URL is displayed in the Automation Builder when the user selects a webhook trigger node.

Polling flow

For polling triggers, the Automation Worker runs your onPoll handler at the configured interval:

  1. User enables the automation
  2. The Worker starts a poll loop at poll_interval_seconds (10-300, default 30)
  3. Each iteration calls your onPoll handler
  4. If fired: true, the automation engine runs the full flow
  5. On disable, the poll loop stops

Use ctx.cache to track the last-checked timestamp and avoid re-processing events.

Manifest configuration

{
"node_type": "trigger",
"node": {
"trigger_mode": "webhook",
"poll_interval_seconds": null,
"input_schema": {
"type": "object",
"properties": {
"shop_domain": { "type": "string", "title": "Shop Domain" }
},
"required": ["shop_domain"]
},
"output_schema": {
"type": "object",
"properties": {
"order_id": { "type": "string" },
"customer_name": { "type": "string" },
"total": { "type": "number" },
"currency": { "type": "string" }
}
}
}
}

For polling triggers, set poll_interval_seconds to a value between 10 and 300.

Next steps