Triggers
Manual, cron, and webhook triggers.
A workflow starts when a trigger fires. Rensei supports three trigger kinds - manual API calls, cron schedules, and inbound webhook events - all normalised to a CloudEvent envelope before the DAG executor runs.
Trigger kinds
Manual trigger
Any published workflow can be started on demand via the REST API. Params supplied in the request body become the trigger's data object, accessible inside the workflow as {{ $trigger.data.fieldName }}.
curl -X POST https://app.rensei.ai/api/workflows/{workflowId}/trigger \
-H "Authorization: Bearer rsk_live_..." \
-H "Content-Type: application/json" \
-d '{ "issueId": "ENG-123", "priority": "high" }'The platform returns the new instance ID immediately; execution proceeds asynchronously.
Cron (schedule) trigger
Set spec.trigger.kind to schedule and provide a cron expression. A built-in preset picker plus a custom builder are available in the editor.
spec:
trigger:
kind: schedule
cron: "0 9 * * 1-5" # 09:00 UTC, Monday-FridayCron instances fire at minute-granularity (Vercel Cron). Sub-minute schedules are not supported; for higher-frequency workloads use a webhook trigger paired with an external scheduler.
Common presets:
| Label | Expression |
|---|---|
| Every hour | 0 * * * * |
| Every day at midnight UTC | 0 0 * * * |
| Every Monday at 09:00 UTC | 0 9 * * 1 |
| Every weekday at 09:00 UTC | 0 9 * * 1-5 |
Webhook (event) trigger
Workflow trigger nodes subscribe to normalised CloudEvents emitted by integrations. The 15 built-in trigger nodes cover GitHub, GitHub Issues, and Linear:
| Node ID | Fires when |
|---|---|
github.push | A push lands on any watched branch |
github.pr.opened | A pull request is opened |
github.pr.merged | A pull request is merged |
github.pr.review_submitted | A review is submitted |
github.check_run.completed | A CI check run finishes |
github_issues.issue.assigned | A GitHub Issue is assigned |
github_issues.comment.created | A comment is added to a GitHub Issue |
| Node ID | Fires when |
|---|---|
linear.issue.assigned | A Linear issue is assigned |
linear.issue.status_changed | A Linear issue transitions state |
linear.issue.labeled | A label is added to a Linear issue |
linear.comment.created | A comment is created |
linear.agent_session.created | A Linear AgentSession is created |
linear.agent_session.prompted | A user @-mentions the agent |
linear.agent_session.updated | An AgentSession record is updated |
| Node ID | Fires when |
|---|---|
agent.exit | A donmai agent session completes |
linear.agent_session.prompted fires for @-mention events. If the project has no active subscription to this workflow, the event is silently dropped - the Linear activity queue receives no error.
Each trigger node defines an outputSchema that describes the fields available downstream via {{ $trigger.data.* }}. The schema is enforced by per-trigger tests (WEFT M2 contract) so the editor's SchemaPicker can offer auto-complete.
Trigger node YAML shape
Every trigger node in a workflow definition has this shape:
steps:
- id: on-issue-assigned
type: trigger
name: "Issue Assigned"
config:
nodeId: linear.issue.assigned
# provider-specific filter config
labelFilter: "bug"The nodeId field selects the trigger handler. Config keys vary by node; consult the Trigger Nodes reference.
Accessing trigger data
Inside downstream nodes, the trigger envelope is available under $trigger:
config:
issueId: "={{ $trigger.data.issueId }}"
title: "={{ $trigger.data.title }}"
# shorthand - $trigger.X aliases $trigger.data.X
assignee: "={{ $trigger.assigneeId }}"Envelope-level fields (type, source, subject) are also accessible:
config:
eventType: "={{ $trigger.type }}"See Expressions for the full syntax reference.
Trigger output contracts
All trigger outputs share the NormalizedEvent envelope (id, type, timestamp, data). Access fields via $trigger.data.* (or the shorthand $trigger.* for top-level data keys). The contracts below show the data payload shape only.
// $trigger.data.*
{
id: string // Linear issue UUID
identifier: string // "ENG-123"
title: string
url: string
state: {
id: string
name: string // e.g. "In Progress"
type: string // e.g. "started"
}
team: {
id: string
key: string // e.g. "ENG"
name: string
}
assignee: { // may be null
id: string
name: string
} | null
assigneeId: string // merged flat convenience field
assigneeName: string // merged flat convenience field
labels: Array<object> // raw Linear label objects
project: object | null
}Filter config fields: assigneeId (match a specific user), isBot (match/exclude bot assignees), project (match a specific project).
// $trigger.data.*
{
id: string // Linear issue UUID
identifier: string // "ENG-123"
title: string
url: string
state: {
id: string
name: string // current (new) state name
type: string
}
team: { id: string; key: string; name: string }
assignee: { id: string; name: string } | null
labels: Array<object>
project: object | null
toStatus: string // name of state transitioned INTO - use in lifecycle trigger.when.to
fromStatusId: string // ID of prior state (not name - use toStatus for lifecycle matching)
}Filter config fields: toStatus[] (match by destination state name), fromStatus[] (match by source state name), project.
toStatus is the field to use in spec.lifecycle trigger conditions (when.to: "In Progress"). fromStatusId is an ID, not a name - do not use it for lifecycle state matching.
// $trigger.data.*
{
prNumber: number
title: string
body: string | null
headBranch: string
baseBranch: string
authorLogin: string
repoFullName: string // "org/repo"
installationId: number
prUrl: string
}Related pages
- Expressions -
{{ $trigger.data.* }}syntax - Trigger Nodes reference - all 15 trigger nodes
- Validation - how trigger output schema is enforced
- Runtime: Gates - suspending on an inbound signal