Automations
Learn how to build powerful event-driven automations in Stack9. This guide covers automation triggers, action chaining, conditional logic, error handling, and real-world implementation patterns from production systems.
What You'll Learn
- Automation structure and configuration
- All trigger types (entity lifecycle, webhooks, scheduled, queues)
- Event-driven automation patterns
- Conditional logic and action chaining
- Template expressions and dynamic parameters
- Error handling strategies
- Testing and debugging automations
- Real production examples
Time Required: 45-60 minutes
Prerequisites
- Completed Building Custom Actions guide
- Understanding of Action Types
- Basic knowledge of JSON and template expressions
- Familiarity with entity definitions
Understanding Stack9 Automations
Automations in Stack9 are configuration-driven workflows that respond to events and execute actions automatically. They consist of:
- Trigger - Event that starts the automation (afterCreate, webhook, cron, etc.)
- Actions - Sequence of operations to execute
- Conditional Actions - Actions that run only when conditions are met
- Parameters - Dynamic data passed using template expressions
Automations are defined as JSON files in src/automations/ and automatically registered when your Stack9 application starts.
Automation File Structure
Automations live in your project's automations directory:
src/
└── automations/
├── when_customer_created.json
├── after_wf_move_subscription.json
├── webhook_resend_receipt.json
├── sync_customer_data.json
└── trigger_send_deferral_reminder.json
Basic Automation Structure
Every automation follows this structure:
{
"key": "unique_automation_key",
"name": "Human-readable name",
"entityKey": "entity_name",
"app": "module_name",
"triggerType": "afterCreate",
"triggerParams": {},
"actions": [
{
"name": "Action description",
"key": "action_key",
"actionTypeKey": "action_type_to_execute",
"params": {
"param1": "{{trigger.entity.field}}",
"param2": "static_value"
}
}
],
"conditionalActions": []
}
Required Properties
For the complete schema reference including all properties, validation rules, and enum values, see the Automation Schema Reference.
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier (token format) |
name | string | Display name for the automation |
entityKey | string | Entity this automation relates to (required for entity-related triggers) |
app | string | Application/module identifier |
triggerType | string | Type of trigger (see Trigger Types) |
triggerParams | object | Configuration for the trigger (required, schema varies by trigger) |
actions | array | List of actions to execute (optional) |
conditionalActions | array | Actions with conditions (optional) |
module | string | Module identifier (optional) |
Trigger Types
Stack9 supports several trigger types:
1. afterCreate - Entity Creation
Fires after a new entity record is created.
{
"key": "when_customer_created",
"name": "When Customer Created",
"entityKey": "customer",
"app": "crm",
"triggerType": "afterCreate",
"triggerParams": {},
"actions": [
{
"name": "Send welcome email",
"key": "send_welcome",
"actionTypeKey": "send_welcome_email",
"params": {
"customerId": "{{trigger.entity.id}}",
"email": "{{trigger.entity.email}}",
"firstName": "{{trigger.entity.first_name}}"
}
}
]
}
Available trigger data:
trigger.entity- The created entity recordtrigger.entityId- The ID of the created entitytrigger.userId- User who created the record
2. afterUpdate - Entity Update
Fires after an entity record is updated.
{
"key": "when_subscription_updated",
"name": "When Subscription Updated",
"entityKey": "subscription",
"triggerType": "afterUpdate",
"triggerParams": {},
"actions": [
{
"name": "Add to queue subscription",
"key": "add_to_queue_subscription",
"actionTypeKey": "add_message_queue",
"params": {
"queueName": "index_subscription",
"message": "{{{subscriptionId: trigger.entity.id}}}",
"priority": "long"
}
},
{
"name": "After subscription updated",
"key": "after_subscription_updated",
"actionTypeKey": "after_subscription_updated",
"params": {
"entityId": "{{trigger.entity.id}}"
}
},
{
"name": "Check Subscription Condition",
"key": "check_subscription_condition",
"actionTypeKey": "check_subscription_workflow_conditions",
"params": {
"subscriptionId": "{{trigger.entity.id}}"
}
},
{
"name": "Update Subscription WF Condition",
"key": "update_subscription_wf_condition",
"actionTypeKey": "workflow_step_condition_upsert",
"params": {
"entityKey": "subscription",
"entityId": "{{runbook.outputs.check_subscription_condition.subscriptionId}}",
"conditions": "{{runbook.outputs.check_subscription_condition.conditions}}"
}
}
]
}
Available trigger data:
trigger.entity- The updated entity (new values)trigger.oldEntity- The entity before update (original values)trigger.entityId- The ID of the updated entitytrigger.userId- User who updated the record
Use case: Detect field changes, sync to external systems, update related records
3. afterDelete - Entity Deletion
Fires after an entity record is deleted.
{
"key": "when_customer_deleted",
"name": "When Customer Deleted",
"entityKey": "customer",
"triggerType": "afterDelete",
"triggerParams": {},
"actions": [
{
"name": "Clean up related data",
"key": "cleanup",
"actionTypeKey": "cleanup_customer_data",
"params": {
"customerId": "{{trigger.entityId}}"
}
}
]
}
4. afterWorkflowMove - Workflow State Change
Fires after an entity moves to a different workflow step.
{
"key": "after_wf_move_subscription",
"name": "After WF Move Subscription",
"entityKey": "subscription",
"triggerType": "afterWorkflowMove",
"triggerParams": {},
"actions": [
{
"name": "Update customer flags",
"key": "update_customer_flags",
"actionTypeKey": "update_customer_flags_from_subscription",
"params": {
"subscriptionId": "{{trigger.entityId}}"
}
},
{
"name": "Get subscription",
"key": "get_subscription",
"actionTypeKey": "entity_find",
"params": {
"entityKey": "subscription",
"query": "{{{$select: ['id', 'customer_id', '_workflow_outcome'], $where: {id: trigger.entityId}}}}"
}
}
],
"conditionalActions": [
{
"condition": {
"rules": [
{
"field": "{{trigger.actionKey}}",
"value": "reprocess_validation",
"operator": "equals"
}
],
"combinator": "or"
},
"actions": [
{
"name": "Execute validation step",
"key": "handle_step",
"actionTypeKey": "handle_subscription_validation_wf_step",
"params": {
"subscriptionId": "{{trigger.entityId}}"
}
}
]
},
{
"condition": {
"rules": [
{
"field": "{{trigger.actionKey}}",
"value": "pending_customer_match",
"operator": "equals"
},
{
"field": "{{trigger.actionKey}}",
"value": "reprocess_matching",
"operator": "equals"
}
],
"combinator": "or"
},
"actions": [
{
"name": "Execute customer match step",
"key": "handle_step",
"actionTypeKey": "handle_subscription_customer_match_wf_step",
"params": {
"subscriptionId": "{{trigger.entityId}}"
}
}
]
}
]
}
Available trigger data:
trigger.entityId- Entity IDtrigger.entity- Current entity statetrigger.actionKey- Workflow action that was takentrigger.workflowData- Workflow transition details
Use case: Execute logic based on workflow steps, implement multi-stage processes
5. webhook - HTTP Endpoint
Creates an HTTP endpoint that triggers the automation.
{
"key": "webhook_resend_receipt",
"name": "Resend Receipt",
"entityKey": "sales_order",
"triggerType": "webhook",
"triggerParams": {
"method": "post",
"path": "/resend-receipt"
},
"actions": [
{
"name": "Resend receipt",
"key": "resend_receipt",
"actionTypeKey": "resend_receipt",
"params": {
"outbound": "{{trigger.body}}"
}
}
]
}
triggerParams for webhook:
method- HTTP method: "post", "get", "put", "all"path- URL path (e.g., "/resend-receipt")
Available trigger data:
trigger.body- Request body (JSON)trigger.query- Query parameterstrigger.params- URL parameterstrigger.headers- HTTP headers
Webhook URL: https://your-app.com/webhooks/{path}
Use case: External integrations, API endpoints, third-party webhooks
6. cronJob - Scheduled Execution
Runs on a schedule (requires registration in app.json).
{
"key": "scheduled_cleanup",
"name": "Daily Cleanup Job",
"triggerType": "cronJob",
"triggerParams": {
"cronExpression": "0 2 * * *",
"timeoutMs": 300000
},
"actions": [
{
"name": "Clean old records",
"key": "cleanup",
"actionTypeKey": "cleanup_old_records",
"params": {}
}
]
}
triggerParams for cronJob:
cronExpression- Cron schedule expressiontimeoutMs- Maximum execution time in milliseconds
Cron schedule examples:
0 * * * *- Every hour0 9 * * *- Every day at 9 AM0 9 * * 1- Every Monday at 9 AM*/15 * * * *- Every 15 minutes0 0 1 * *- First day of every month
Use case: Batch processing, cleanup jobs, reports, synchronization
7. mqHandler - Message Queue Consumer
Processes messages from a queue.
{
"key": "sync_customer_data",
"name": "Sync Customer Data",
"entityKey": "customer",
"triggerType": "mqHandler",
"triggerParams": {
"queueName": "index_customer",
"timeoutMs": 5000
},
"actions": [
{
"name": "Sync customer data",
"key": "sync_customer_data",
"actionTypeKey": "sync_customer_data",
"params": {
"customerId": "{{trigger.message.body.customerId}}"
}
}
]
}
triggerParams for mqHandler:
queueName- Name of the queue to consume fromtimeoutMs- Processing timeout in milliseconds
Available trigger data:
trigger.message.body- Message payloadtrigger.automationKey- Automation key
Use case: Asynchronous processing, background jobs, scalable processing
Actions
Actions are the operations executed when an automation triggers. They run sequentially in the order defined.
Action Structure
{
"name": "Human-readable description",
"key": "unique_action_key",
"actionTypeKey": "action_type_to_execute",
"params": {
"param1": "{{dynamic_value}}",
"param2": "static_value"
}
}
Accessing Previous Action Results
Actions can reference outputs from previous actions:
{
"actions": [
{
"name": "Fetch customer",
"key": "fetch_customer",
"actionTypeKey": "entity_find",
"params": {
"entityKey": "customer",
"query": "{{{$where: {id: trigger.entity.customer_id}}}}"
}
},
{
"name": "Send email to customer",
"key": "send_email",
"actionTypeKey": "send_email",
"params": {
"to": "{{runbook.outputs.fetch_customer.email}}",
"subject": "Hello {{runbook.outputs.fetch_customer.first_name}}!"
}
}
]
}
Pattern: Use runbook.outputs.{action_key}.{field} to access action results.
Conditional Actions
Conditional actions only execute when specific conditions are met.
Simple Condition
{
"conditionalActions": [
{
"condition": {
"rules": [
{
"field": "{{trigger.entity.status}}",
"value": "active",
"operator": "equals"
}
],
"combinator": "and"
},
"actions": [
{
"name": "Send activation email",
"key": "send_activation",
"actionTypeKey": "send_email",
"params": {
"to": "{{trigger.entity.email}}"
}
}
]
}
]
}
Multiple Conditions (OR Logic)
{
"condition": {
"rules": [
{
"field": "{{trigger.entity.status}}",
"value": "pending",
"operator": "equals"
},
{
"field": "{{trigger.entity.status}}",
"value": "review",
"operator": "equals"
}
],
"combinator": "or"
},
"actions": [...]
}
Multiple Conditions (AND Logic)
{
"condition": {
"rules": [
{
"field": "{{trigger.entity.status}}",
"value": "active",
"operator": "equals"
},
{
"field": "{{trigger.entity.subscription_tier}}",
"value": "premium",
"operator": "equals"
}
],
"combinator": "and"
},
"actions": [...]
}
Supported Operators
equals- Exact matchnotEquals- Not equalcontains- String contains substringstartsWith- String starts withendsWith- String ends withgreaterThan- Numeric greater thanlessThan- Numeric less thanin- Value in arraychanged- Field changed (afterUpdate only)
Template Expressions
Use double curly braces {{expression}} for dynamic values.