Triggers
Start flows on a schedule, from the UI, or via incoming HTTP events.
Every flow needs an entry point. Triggers are special nodes that sit at the start of a flow and define how it gets started. A flow can have at most one trigger node.
There are three trigger types:
| Trigger | Node ID | How it starts |
|---|---|---|
| Manual | trigger.manual | UI run button, REST API, or startFlowRun() |
| Cron | trigger.cron | Recurring schedule via cron expression |
| Webhook | trigger.webhook | Incoming HTTP POST from an external service |
All trigger nodes expose their data to downstream nodes via template expressions.
Manual Trigger
The Manual Trigger is the default entry point for most flows. It fires when you click Run in the editor, call the POST /flows/:id/run API endpoint, or call core.startFlowRun() programmatically.
Defining input fields
By default, any inputs you pass to the flow are forwarded downstream unchanged. You can optionally define an explicit list of expected fields — this gives downstream nodes a predictable, documented shape and lets you set default values.
[
{ "name": "topic", "defaultValue": "hello world" },
{ "name": "count", "defaultValue": 5 },
{ "name": "userId" }
]When input definitions are present, Invect validates that all required fields (those without a defaultValue) are provided. Missing required fields cause the flow to fail with a clear error.
Accessing trigger data
Downstream nodes access trigger outputs via the manual_trigger key:
{{ manual_trigger.topic }}
{{ manual_trigger.count }}
{{ manual_trigger.userId }}Cron Trigger
The Cron Trigger fires automatically on a recurring schedule. The cron scheduler must be enabled (it is by default) and running as part of your Invect backend process.
Cron expression
Standard 5-field cron syntax:
┌──── minute (0-59)
│ ┌─── hour (0-23)
│ │ ┌── day of month (1-31)
│ │ │ ┌─ month (1-12)
│ │ │ │ ┌ day of week (0-7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *Common examples:
| Expression | Schedule |
|---|---|
0 * * * * | Every hour |
0 9 * * 1-5 | Weekdays at 9am |
*/15 * * * * | Every 15 minutes |
0 0 1 * * | First day of every month at midnight |
Timezone
Cron expressions are evaluated in UTC by default. Set timezone to any valid IANA timezone name:
America/New_York
Europe/London
Asia/TokyoStatic inputs
Pass a fixed JSON object that will be available to downstream nodes on every execution:
{ "environment": "production", "reportType": "daily" }Accessing trigger data
{{ cron_trigger.scheduledTime }} // ISO 8601 timestamp of the scheduled fire timeEnabling/disabling the cron scheduler
The scheduler is enabled by default. Disable it globally (e.g. for worker processes that shouldn't run jobs):
createInvectRouter({
baseDatabaseConfig: {
type: 'sqlite',
connectionString: process.env.DATABASE_URL || 'file:./dev.db',
id: 'main',
},
triggers: {
cronEnabled: false,
},
});Webhook Trigger
The Webhook Trigger fires when an external service sends an HTTP request to a unique URL generated by Invect. Signature verification, rate limiting, and deduplication are handled by the @invect/webhooks plugin.
Webhook triggers require the @invect/webhooks plugin. See the Webhooks plugin docs for setup.
Accessing trigger data
The incoming request body and headers are exposed to downstream nodes:
{{ webhook_trigger.body.pull_request.title }}
{{ webhook_trigger.body.action }}
{{ webhook_trigger.headers.x-github-event }}Starting flows programmatically
When using trigger.manual, pass inputs as the second argument to startFlowRun:
const result = await core.startFlowRun('my-flow-id', {
topic: 'quarterly report',
userId: 'user_123',
});Version pinning
By default, Invect runs the latest published version. Pin to a specific version:
const result = await core.startFlowRun('my-flow-id', inputs, {
version: 3, // specific version number
// version: 'latest', // explicit latest (default)
});Batch processing
Opt in to batch processing for cheaper AI model calls (50% cost reduction). The flow will pause with status PAUSED_FOR_BATCH until the batch completes:
const result = await core.startFlowRun('my-flow-id', inputs, {
useBatchProcessing: true,
});Fire and forget
Use startFlowRunAsync to return immediately without waiting for the flow to complete:
const { id: flowRunId } = await core.startFlowRunAsync('my-flow-id', inputs);
// Flow is running in background — poll getFlowRun(flowRunId) for status