Rensei docs

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-Friday

Cron 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:

LabelExpression
Every hour0 * * * *
Every day at midnight UTC0 0 * * *
Every Monday at 09:00 UTC0 9 * * 1
Every weekday at 09:00 UTC0 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 IDFires when
github.pushA push lands on any watched branch
github.pr.openedA pull request is opened
github.pr.mergedA pull request is merged
github.pr.review_submittedA review is submitted
github.check_run.completedA CI check run finishes
github_issues.issue.assignedA GitHub Issue is assigned
github_issues.comment.createdA comment is added to a GitHub Issue
Node IDFires when
linear.issue.assignedA Linear issue is assigned
linear.issue.status_changedA Linear issue transitions state
linear.issue.labeledA label is added to a Linear issue
linear.comment.createdA comment is created
linear.agent_session.createdA Linear AgentSession is created
linear.agent_session.promptedA user @-mentions the agent
linear.agent_session.updatedAn AgentSession record is updated
Node IDFires when
agent.exitA 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
}

On this page