defineTrigger
Define event-driven automation rules
The defineTrigger function creates event-driven automation rules that fire when entities are created, updated, or deleted. Each trigger is defined in its own file under the triggers/ directory.
import { defineTrigger } from 'struere'
export default defineTrigger({
name: "Notify on New Session",
slug: "notify-on-session",
on: {
entityType: "session",
action: "created",
condition: { "data.status": "scheduled" },
},
actions: [
{
tool: "entity.get",
args: { id: "{{trigger.data.teacherId}}" },
as: "teacher",
},
{
tool: "event.emit",
args: {
eventType: "session.notification",
entityId: "{{trigger.entityId}}",
payload: { teacher: "{{steps.teacher.data.name}}" },
},
},
],
})
TriggerConfig
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Display name for the trigger |
slug |
string |
Yes | Unique identifier for the trigger |
description |
string |
No | Human-readable description |
on |
object |
Yes | Event configuration that activates the trigger |
on.entityType |
string |
Yes | Entity type slug to watch |
on.action |
'created' | 'updated' | 'deleted' |
Yes | Entity lifecycle event |
on.condition |
object |
No | Optional filter on entity data fields |
schedule |
TriggerSchedule |
No | Delay or schedule execution for a future time |
retry |
TriggerRetry |
No | Retry configuration for failed executions |
actions |
TriggerAction[] |
Yes | Ordered list of steps to execute (at least one required) |
Validation
defineTrigger throws errors if:
name,slug, oronis missingon.entityTypeis missingon.actionis not one of"created","updated","deleted"actionsis empty or missing- Any action is missing
toolor has non-objectargs schedule.delayandschedule.atare both setschedule.delayis not a positive numberretry.maxAttemptsis less than 1retry.backoffMsis not a positive number
TriggerAction
Each action step executes a tool with the given arguments.
interface TriggerAction {
tool: string
args: Record<string, unknown>
as?: string
}
| Field | Type | Description |
|---|---|---|
tool |
string |
Tool name to execute (built-in or custom) |
args |
object |
Arguments passed to the tool, supports template variables |
as |
string |
Optional name for referencing this step's result in later steps |
Actions execute in order. If any action fails, the trigger stops (fail-fast behavior).
Template Variables
Trigger action arguments support {{variable}} template syntax for dynamic value resolution.
Trigger Context Variables
| Variable | Description |
|---|---|
{{trigger.entityId}} |
ID of the entity that triggered the event |
{{trigger.entityType}} |
Entity type slug |
{{trigger.action}} |
The action that occurred ("created", "updated", "deleted") |
{{trigger.data.X}} |
Field X from the entity's current data |
{{trigger.previousData.X}} |
Field X from the entity's data before the update (only for "updated" actions) |
Step Reference Variables
| Variable | Description |
|---|---|
{{steps.NAME.X}} |
Access field X from the result of step named NAME (set via as field) |
Template Example
actions: [
{
tool: "entity.get",
args: { id: "{{trigger.data.teacherId}}" },
as: "teacher",
},
{
tool: "entity.get",
args: { id: "{{trigger.data.guardianId}}" },
as: "guardian",
},
{
tool: "event.emit",
args: {
eventType: "session.booked",
entityId: "{{trigger.entityId}}",
payload: {
teacherName: "{{steps.teacher.data.name}}",
guardianName: "{{steps.guardian.data.name}}",
},
},
},
]
Conditions
The on.condition field filters which entity mutations trigger the actions. Only entities whose data matches all condition fields will activate the trigger:
on: {
entityType: "session",
action: "updated",
condition: { "data.status": "completed" },
}
This trigger only fires when a session is updated and its status field is "completed".
Scheduled Triggers
Triggers can be scheduled to run at a future time instead of executing immediately.
interface TriggerSchedule {
delay?: number
at?: string
offset?: number
cancelPrevious?: boolean
}
| Field | Type | Description |
|---|---|---|
delay |
number |
Delay in milliseconds before execution |
at |
string |
Template expression resolving to an ISO timestamp or Unix timestamp |
offset |
number |
Offset in milliseconds to add to the at time (can be negative) |
cancelPrevious |
boolean |
Cancel any pending scheduled run for the same entity before scheduling |
delay and at are mutually exclusive.
Schedule Examples
Send a reminder 1 hour before a session starts:
export default defineTrigger({
name: "Session Reminder",
slug: "session-reminder",
on: {
entityType: "session",
action: "created",
condition: { "data.status": "scheduled" },
},
schedule: {
at: "{{trigger.data.startTime}}",
offset: -3600000,
cancelPrevious: true,
},
actions: [
{
tool: "entity.get",
args: { id: "{{trigger.data.guardianId}}" },
as: "guardian",
},
{
tool: "event.emit",
args: {
eventType: "session.reminder",
entityId: "{{trigger.entityId}}",
payload: { guardianName: "{{steps.guardian.data.name}}" },
},
},
],
})
Delay notification by 5 minutes:
schedule: {
delay: 300000,
}
Trigger Runs
Scheduled triggers create records in the triggerRuns table with status tracking:
| Status | Description |
|---|---|
pending |
Scheduled but not yet executed |
running |
Currently executing |
completed |
Successfully finished |
failed |
Failed but may be retried |
dead |
Failed and exhausted all retry attempts |
Retry Configuration
Failed triggers can be retried with exponential backoff.
interface TriggerRetry {
maxAttempts?: number
backoffMs?: number
}
| Field | Type | Description |
|---|---|---|
maxAttempts |
number |
Maximum number of retry attempts (minimum 1) |
backoffMs |
number |
Base delay in milliseconds between retries |
retry: {
maxAttempts: 3,
backoffMs: 5000,
}
This retries up to 3 times with 5-second base backoff between attempts.
Execution Behavior
- Triggers execute asynchronously (scheduled after the originating mutation completes)
- Triggers run as the system actor with full permissions
- Actions execute in fail-fast order (first failure stops the chain)
- Successful triggers emit a
trigger.executedevent - Failed triggers emit a
trigger.failedevent - Triggers fire from all mutation sources: dashboard CRUD, agent tool calls, and API mutations
Full Examples
Notify Guardian on Session Completion
import { defineTrigger } from 'struere'
export default defineTrigger({
name: "Notify on Completion",
slug: "notify-on-completion",
on: {
entityType: "session",
action: "updated",
condition: { "data.status": "completed" },
},
actions: [
{
tool: "entity.get",
args: { id: "{{trigger.data.guardianId}}" },
as: "guardian",
},
{
tool: "event.emit",
args: {
eventType: "session.completed",
entityId: "{{trigger.entityId}}",
payload: {
guardianName: "{{steps.guardian.data.name}}",
subject: "{{trigger.data.subject}}",
},
},
},
],
})
Auto-Deduct Credits on Completion
import { defineTrigger } from 'struere'
export default defineTrigger({
name: "Deduct Credits",
slug: "deduct-credits",
on: {
entityType: "session",
action: "updated",
condition: { "data.status": "completed" },
},
retry: {
maxAttempts: 3,
backoffMs: 2000,
},
actions: [
{
tool: "entity.query",
args: {
type: "entitlement",
filters: { "data.studentId": "{{trigger.data.studentId}}" },
limit: 1,
},
as: "entitlements",
},
{
tool: "event.emit",
args: {
eventType: "credits.deducted",
entityId: "{{trigger.entityId}}",
payload: {
studentId: "{{trigger.data.studentId}}",
},
},
},
],
})
Scheduled Follow-Up
import { defineTrigger } from 'struere'
export default defineTrigger({
name: "Post-Session Follow-Up",
slug: "post-session-followup",
on: {
entityType: "session",
action: "updated",
condition: { "data.status": "completed" },
},
schedule: {
delay: 86400000,
},
retry: {
maxAttempts: 2,
backoffMs: 60000,
},
actions: [
{
tool: "event.emit",
args: {
eventType: "session.followup",
entityId: "{{trigger.entityId}}",
payload: {
teacherId: "{{trigger.data.teacherId}}",
studentId: "{{trigger.data.studentId}}",
},
},
},
],
})