Workflows
Learn how to build powerful multi-step approval workflows in Stack9. This guide covers workflow definitions, states and transitions, conditional logic, user permissions, automation triggers, and real-world production examples.
What You'll Learn
- Workflow definition structure in entities
- States, steps, and outcomes
- Actions and transitions
- Conditional transitions and workflow conditions
- User permissions and ownership per state
- Workflow automation triggers
- Integration with automations
- Real production workflow examples
Time Required: 60-75 minutes
Prerequisites
- Completed Creating Entities guide
- Completed Automations guide
- Understanding of entity definitions
- Familiarity with user groups and permissions
Understanding Stack9 Workflows
Workflows in Stack9 enable multi-step approval processes for entities. They define:
- Steps - States an entity can be in (e.g., "Pending", "Approved", "Rejected")
- Actions - Transitions between steps
- Outcomes - Final states (Success or Failure)
- Conditions - Requirements that must be met before transitions
- Ownership - User groups that can perform actions
- Visual Designer - Drag-and-drop workflow editor
Workflows are defined within entity JSON files and automatically create:
- Workflow UI components
- State transition validation
- Permission checks
- Audit trails
- Visual workflow diagrams
Workflow Structure
Workflows are defined in the workflowDefinition property of an entity:
{
"head": {
"name": "Sales Order",
"key": "sales_order"
},
"fields": [...],
"workflowDefinition": {
"steps": [...],
"actions": [...],
"outcome": {...},
"conditions": [...]
}
}
Workflow Steps
Steps represent different states in your workflow.
Step Structure
{
"key": "pending_validation",
"name": "Pending Validation",
"description": "Order is awaiting validation",
"color": "yellow",
"order": 1,
"position": {
"x": 100,
"y": 0
},
"ownershipDetails": {
"userGroups": ["sales_team", "managers"],
"userFields": ["assigned_to"]
}
}
Step Properties
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique identifier (snake_case) |
name | string | Yes | Display name |
description | string | No | Step description |
color | string | No | UI color (yellow, blue, green, red, etc.) |
order | number | Yes | Sequence number (unique) |
position | object | Yes | x,y coordinates for visual designer |
ownershipDetails | object | Yes | Who can work on this step |
Ownership Details
Controls who can perform actions on entities in this step:
{
"ownershipDetails": {
"userGroups": ["admin", "sales_manager"],
"userFields": ["assigned_user_id", "reviewer_id"]
}
}
userGroups- Array of user group codes that have accessuserFields- Array of entity field keys (must be SingleDropDown to user entity)
Workflow Actions
Actions define transitions between steps or to outcomes.
Action Structure
{
"key": "approve",
"name": "Approve Order",
"default": false,
"preventUserInteraction": false,
"from": {
"step": ["pending_validation", "pending_review"]
},
"to": {
"step": "approved",
"reasonRequired": false,
"fullValidationRequired": false
}
}
Action Properties
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique action identifier |
name | string | Yes | Display name for action button |
default | boolean | No | Auto-execute if no other action taken |
preventUserInteraction | boolean | No | Hide from UI (automation only) |
from | object | Yes | Source steps or outcomes |
to | object | Yes | Destination step or outcome |
From Configuration
Define where the action can be triggered from:
{
"from": {
"step": ["pending_validation", "pending_review"]
}
}
Or from outcomes:
{
"from": {
"outcome": [1, 2] // 1 = Success, 2 = Failure
}
}
To Configuration
Define the destination:
To a step:
{
"to": {
"step": "approved",
"reasonRequired": true,
"fullValidationRequired": false
}
}
To an outcome:
{
"to": {
"outcome": 1, // 1 = Success, 2 = Failure
"reasonRequired": true
}
}
Properties:
reasonRequired- User must provide a reason/commentfullValidationRequired- Run full entity validation before transition
Workflow Outcomes
Every workflow must have two outcomes: Success and Failure.
{
"outcome": {
"success": {
"label": "Approved",
"position": {
"x": 1600,
"y": 0
},
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
"failure": {
"label": "Rejected",
"position": {
"x": 100,
"y": 450
},
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
}
}
}
Outcome values:
- Success =
1 - Failure =
2
Workflow Conditions
Conditions are requirements that must be met before certain actions can be taken.
Condition Structure
{
"conditions": [
{
"key": "customer_required",
"name": "Customer is required",
"description": "Subscription must have a linked customer",
"action": "subscription_confirmed",
"required": true
},
{
"key": "payment_verified",
"name": "Payment has been verified",
"description": "Payment must be processed successfully",
"action": "approve_order",
"required": true
}
]
}
Condition Properties
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique condition identifier |
name | string | Yes | Display name |
description | string | Yes | Condition description |
action | string | Yes | Action key this condition applies to |
required | boolean | Yes | Whether condition must be met |
Setting Condition Values
Conditions are evaluated and set via action types or automations:
// In an action type
await this.context.db.entityWorkflowCondition.upsert({
entityKey: 'subscription',
entityId: subscriptionId,
stepConditionKey: 'customer_required',
value: true // Condition is now met
});
Or via automation:
{
"actions": [
{
"name": "Update workflow condition",
"key": "update_condition",
"actionTypeKey": "workflow_step_condition_upsert",
"params": {
"entityKey": "subscription",
"entityId": "{{trigger.entity.id}}",
"conditions": {
"customer_required": true,
"payment_verified": true
}
}
}
]
}
Complete Workflow Example: Subscription
Real production workflow for managing subscription lifecycle:
{
"workflowDefinition": {
"steps": [
{
"key": "pending_validation",
"name": "Pending validation",
"description": "Customer subscription details need to be verified and confirmed",
"color": "yellow",
"order": 1,
"position": { "x": 100, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "pending_customer_matching",
"name": "Pending customer matching",
"description": "System is finding or creating customer record",
"color": "darkgrey",
"order": 2,
"position": { "x": 400, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "send_welcome_pack",
"name": "Send welcome pack",
"description": "Sending welcome materials to customer",
"color": "darkgrey",
"order": 3,
"position": { "x": 700, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "active",
"name": "Active",
"description": "Customer subscription is active",
"color": "green",
"order": 4,
"position": { "x": 1000, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "validation_failed",
"name": "Validation failed",
"description": "Subscription validation errors occurred",
"color": "red",
"order": 5,
"position": { "x": 100, "y": 300 },
"ownershipDetails": {
"userGroups": ["css"],
"userFields": []
}
},
{
"key": "customer_matching_failed",
"name": "Customer matching failed",
"description": "Unable to match or create customer",
"color": "blue",
"order": 6,
"position": { "x": 400, "y": 300 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "send_welcome_failed",
"name": "Send welcome failed",
"description": "Unable to send welcome pack",
"color": "blue",
"order": 7,
"position": { "x": 700, "y": 300 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "deferred",
"name": "Deferred",
"description": "Customer subscription has been deferred",
"color": "orange",
"order": 8,
"position": { "x": 1000, "y": -150 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "suspended",
"name": "Suspended",
"description": "Subscription is suspended due to payment failures",
"color": "blue",
"order": 9,
"position": { "x": 1000, "y": 300 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
}
],
"outcome": {
"success": {
"label": "Ended",
"position": { "x": 1300, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
"failure": {
"label": "Cancelled",
"position": { "x": 550, "y": 450 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
}
},
"actions": [
{
"key": "pending_customer_match",
"name": "Pending customer match",
"default": true,
"preventUserInteraction": true,
"from": {
"step": ["pending_validation"]
},
"to": {
"step": "pending_customer_matching"
}
},
{
"key": "reprocess_validation",
"name": "Reprocess Validation",
"default": false,
"from": {
"step": ["validation_failed"]
},
"to": {
"step": "pending_validation"
}
},
{
"key": "validation_failed",
"name": "Validation failed",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["pending_validation"]
},
"to": {
"step": "validation_failed",
"reasonRequired": true
}
},
{
"key": "matching_completed",
"name": "Matching completed",
"default": true,
"from": {
"step": ["pending_customer_matching"]
},
"to": {
"step": "send_welcome_pack",
"reasonRequired": false
}
},
{
"key": "customer_matching_failed",
"name": "Customer matching failed",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["pending_customer_matching"]
},
"to": {
"step": "customer_matching_failed",
"reasonRequired": true
}
},
{
"key": "reprocess_matching",
"name": "Reprocess Matching",
"default": false,
"from": {
"step": ["customer_matching_failed"]
},
"to": {
"step": "pending_customer_matching"
}
},
{
"key": "sent",
"name": "Sent",
"default": true,
"from": {
"step": ["send_welcome_pack"]
},
"to": {
"step": "active",
"reasonRequired": false
}
},
{
"key": "send_welcome_failed",
"name": "Send welcome failed",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["send_welcome_pack"]
},
"to": {
"step": "send_welcome_failed",
"reasonRequired": true
}
},
{
"key": "retry_sending_welcome",
"name": "Retry sending welcome",
"default": false,
"from": {
"step": ["send_welcome_failed"]
},
"to": {
"step": "send_welcome_pack"
}
},
{
"key": "set_active",
"name": "Set as active",
"default": false,
"from": {
"step": ["send_welcome_failed"]
},
"to": {
"step": "active"
}
},
{
"key": "defer",
"name": "Defer",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["active"]
},
"to": {
"step": "deferred"
}
},
{
"key": "deferral_ended",
"name": "Deferral ended",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["deferred"]
},
"to": {
"step": "active"
}
},
{
"key": "after_month_of_failure",
"name": "After month of payment failures",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["active"]
},
"to": {
"step": "suspended"
}
},
{
"key": "reactivate",
"name": "Re-activate",
"default": false,
"from": {
"step": ["suspended"]
},
"to": {
"step": "active"
}
},
{
"key": "end_subscription",
"name": "End subscription",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["suspended", "active", "deferred"]
},
"to": {
"outcome": 1,
"reasonRequired": true
}
},
{
"key": "cancel",
"name": "Cancel",
"default": false,
"from": {
"step": [
"validation_failed",
"customer_matching_failed",
"send_welcome_failed"
]
},
"to": {
"outcome": 2
}
}
],
"conditions": [
{
"key": "customer_required",
"name": "Customer is required",
"description": "Subscription must have a linked customer",
"action": "matching_completed",
"required": true
}
]
}
}
Workflow Automation Integration
Workflows integrate seamlessly with automations via the afterWorkflowMove trigger.
Triggering Automations on Workflow Moves
File: src/automations/after_wf_move_subscription.json
{
"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}}"
}
}
],
"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": "matching_completed",
"operator": "equals"
},
{
"field": "{{trigger.actionKey}}",
"value": "sent",
"operator": "equals"
}
],
"combinator": "or"
},
"actions": [
{
"name": "Execute send welcome pack step",
"key": "handle_step",
"actionTypeKey": "handle_subscription_send_welcome_pack_wf_step",
"params": {
"subscriptionId": "{{trigger.entityId}}"
}
}
]
},
{
"condition": {
"rules": [
{
"field": "{{trigger.actionKey}}",
"value": "deferral_ended",
"operator": "equals"
},
{
"field": "{{trigger.actionKey}}",
"value": "reactivate",
"operator": "equals"
}
],
"combinator": "or"
},
"actions": [
{
"name": "Update next debit run date",
"key": "handle_step",
"actionTypeKey": "handle_subscription_active_wf_step",
"params": {
"subscriptionId": "{{trigger.entityId}}"
}
}
]
}
]
}
Programmatic Workflow Transitions
Move entities through workflow programmatically:
// Via API
await workflowService.move('subscription', entityId, 'approve', {
outcome_reason: 'All checks passed'
});
// Via action type
export class ApproveSubscription extends S9AutomationActionType {
async exec(params: any) {
const { db, services } = this.context;
// Perform business logic
const subscription = await db('subscription')
.where({ id: params.subscriptionId })
.first();
// Move workflow
await services.workflow.move(
'subscription',
params.subscriptionId,
'matching_completed',
{}
);
return { success: true };
}
}
Queue-Based Workflow Transitions
Trigger workflow moves asynchronously via message queues:
Automation: src/automations/move_wf_subscription.json
{
"key": "move_wf_subscription",
"name": "Move WF Subscription",
"entityKey": "subscription",
"triggerType": "mqHandler",
"triggerParams": {
"queueName": "move_wf_subscription",
"timeoutMs": 30000
},
"actions": [
{
"name": "Handle workflow action",
"key": "handle_action",
"actionTypeKey": "workflow_move",
"params": {
"entityKey": "subscription",
"entityId": "{{trigger.message.body.entityId}}",
"actionKey": "{{trigger.message.body.actionKey}}",
"body": "{{{}}}"
}
}
]
}
Usage:
// Add message to queue
await messageQueue.add('move_wf_subscription', {
entityId: 123,
actionKey: 'approve'
});
Complete Example: Sales Order Workflow
Complex multi-step workflow with payment processing:
{
"workflowDefinition": {
"steps": [
{
"key": "pendingprocessing",
"name": "Pending processing",
"description": "Order received, awaiting processing",
"color": "orange",
"order": 1,
"position": { "x": 100, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "pendingvalidation",
"name": "Pending validation",
"description": "Validating order details",
"color": "darkgrey",
"order": 2,
"position": { "x": 400, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "pendingcustomermatching",
"name": "Pending customer matching",
"description": "Finding or creating customer record",
"color": "darkgrey",
"order": 3,
"position": { "x": 700, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "pendingpayment",
"name": "Pending payment",
"description": "Processing payment",
"color": "darkgrey",
"order": 4,
"position": { "x": 1000, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "pendingreceipt",
"name": "Pending receipt",
"description": "Generating receipt",
"color": "darkgrey",
"order": 5,
"position": { "x": 1300, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "validationfailed",
"name": "Validation failed",
"description": "Order validation errors",
"color": "blue",
"order": 6,
"position": { "x": 400, "y": 300 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "paymentfailed",
"name": "Payment failed",
"description": "Payment processing failed",
"color": "blue",
"order": 7,
"position": { "x": 1000, "y": 300 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
{
"key": "pending_bank_run",
"name": "Pending bank run",
"description": "Awaiting batch payment processing",
"color": "darkgrey",
"order": 8,
"position": { "x": 850, "y": -150 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
}
],
"outcome": {
"success": {
"label": "Success",
"position": { "x": 1600, "y": 0 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
},
"failure": {
"label": "Cancelled",
"position": { "x": 100, "y": 450 },
"ownershipDetails": {
"userGroups": [],
"userFields": []
}
}
},
"actions": [
{
"key": "sendForValidation",
"name": "Send for validation",
"default": true,
"from": {
"step": ["pendingprocessing"]
},
"to": {
"step": "pendingvalidation"
}
},
{
"key": "sendForCustomerMatching",
"name": "Send for customer matching",
"default": true,
"preventUserInteraction": true,
"from": {
"step": ["pendingvalidation"]
},
"to": {
"step": "pendingcustomermatching"
}
},
{
"key": "sendForPayment",
"name": "Send for payment",
"default": true,
"preventUserInteraction": true,
"from": {
"step": ["pendingcustomermatching"]
},
"to": {
"step": "pendingpayment"
}
},
{
"key": "sendForPendingReceipt",
"name": "Send for pending receipt",
"default": true,
"preventUserInteraction": true,
"from": {
"step": ["pendingpayment", "pending_bank_run"]
},
"to": {
"step": "pendingreceipt"
}
},
{
"key": "validationFailed",
"name": "Validation failed",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["pendingvalidation"]
},
"to": {
"step": "validationfailed",
"reasonRequired": true
}
},
{
"key": "reprocessValidation",
"name": "Reprocess validation",
"default": false,
"from": {
"step": ["validationfailed"]
},
"to": {
"step": "pendingvalidation"
}
},
{
"key": "require_bank_run",
"name": "Require bank run",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["pendingpayment", "pendingcustomermatching"]
},
"to": {
"step": "pending_bank_run"
}
},
{
"key": "paymentFailed",
"name": "Payment failed",
"default": false,
"preventUserInteraction": true,
"from": {
"step": ["pendingpayment", "pending_bank_run"]
},
"to": {
"step": "paymentfailed",
"reasonRequired": true
}
},
{
"key": "reprocessPayment",
"name": "Reprocess payment",
"default": false,
"from": {
"step": ["paymentfailed"]
},
"to": {
"step": "pendingpayment"
}
},
{
"key": "success",
"name": "Success",
"default": true,
"preventUserInteraction": true,
"from": {
"step": ["pendingreceipt"]
},
"to": {
"outcome": 1
}
},
{
"key": "cancel",
"name": "Cancel sales order",
"default": false,
"from": {
"step": [
"pendingprocessing",
"validationfailed",
"paymentfailed"
]
},
"to": {
"outcome": 2
}
}
],
"conditions": []
}
}
Workflow Patterns
Pattern 1: Linear Workflow
Simple sequential flow:
[New] → [Review] → [Approved] → [Success]
↓
[Rejected] → [Failure]
{
"steps": [
{ "key": "new", "order": 1 },
{ "key": "review", "order": 2 },
{ "key": "approved", "order": 3 }
],
"actions": [
{
"key": "submit",
"from": { "step": ["new"] },
"to": { "step": "review" }
},
{
"key": "approve",
"from": { "step": ["review"] },
"to": { "step": "approved" }
},
{
"key": "reject",
"from": { "step": ["review"] },
"to": { "outcome": 2 }
},
{
"key": "complete",
"from": { "step": ["approved"] },
"to": { "outcome": 1 }
}
]
}
Pattern 2: Multi-Level Approval
Hierarchical approval process:
[Submitted] → [Manager Review] → [Director Review] → [Approved]
↓ ↓ ↓
[Rejected] [Rejected] [Success]
{
"steps": [
{
"key": "submitted",
"ownershipDetails": {
"userGroups": ["employee"]
}
},
{
"key": "manager_review",
"ownershipDetails": {
"userGroups": ["manager"]
}
},
{
"key": "director_review",
"ownershipDetails": {
"userGroups": ["director"]
}
},
{ "key": "approved" }
],
"actions": [
{
"key": "submit_to_manager",
"from": { "step": ["submitted"] },
"to": { "step": "manager_review" }
},
{
"key": "manager_approve",
"from": { "step": ["manager_review"] },
"to": { "step": "director_review" }
},
{
"key": "director_approve",
"from": { "step": ["director_review"] },
"to": { "step": "approved" }
},
{
"key": "final_approve",
"from": { "step": ["approved"] },
"to": { "outcome": 1 }
}
]
}
Pattern 3: Parallel Processing
Multiple simultaneous checks:
┌→ [Legal Review] ─┐
[Submitted] ─────┼→ [Finance Review]─┼→ [Approved]
└→ [Tech Review] ───┘
Implement using workflow conditions that all must be met.
Pattern 4: Retry/Reprocess Loop
Allow fixing errors and retrying:
[Pending] → [Validation] → [Failed] ←┐
↓ │
[Reprocess] ────┘
↓
[Success]
{
"actions": [
{
"key": "validate",
"from": { "step": ["pending"] },
"to": { "step": "validation" }
},
{
"key": "validation_failed",
"from": { "step": ["validation"] },
"to": { "step": "failed" }
},
{
"key": "reprocess",
"from": { "step": ["failed"] },
"to": { "step": "pending" }
}
]
}
Best Practices
1. Use Meaningful Step Names
// Good
{
"key": "pending_manager_approval",
"name": "Pending Manager Approval"
}
// Bad
{
"key": "step1",
"name": "Step 1"
}
2. Set Appropriate Colors
Use colors to indicate step status:
yellow- Pending/In Progressblue- Requires Attentiongreen- Completed Successfullyred- Error/Failedorange- On Hold/Deferreddarkgrey- Processing
3. Always Define Both Outcomes
Every workflow must have success and failure outcomes:
{
"outcome": {
"success": { "label": "Approved" },
"failure": { "label": "Rejected" }
}
}
4. Use preventUserInteraction for Automated Actions
Hide actions that should only be triggered by automations:
{
"key": "auto_process",
"preventUserInteraction": true
}
5. Require Reasons for Critical Actions
{
"key": "reject",
"to": {
"outcome": 2,
"reasonRequired": true
}
}
6. Design for Audit Trail
Workflows automatically create audit trails. Design steps that make sense in history:
✅ Good Audit Trail:
- Created → Pending Validation → Approved → Success
❌ Bad Audit Trail:
- Step 1 → Step 2 → Step 3 → Done
7. Use User Groups for Permissions
Assign ownership to groups, not individual users:
{
"ownershipDetails": {
"userGroups": ["sales_team", "managers"],
"userFields": ["assigned_to"]
}
}
8. Validate Workflow Definition
Stack9 validates workflows on startup. Check for:
- All steps lead to an outcome (no dead ends)
- Action keys are unique
- Step orders are unique
- Referenced steps exist
Accessing Workflow Data
In TypeScript/Action Types
// Get current workflow state
const workflow = await this.context.services.workflow.current(
'subscription',
entityId
);
// Access workflow properties
const currentStep = workflow.currentStep;
const outcome = workflow.outcome;
const availableActions = workflow.availableActions;
const history = workflow.historyGrouped;
In UI Components
Stack9 provides React components for workflows:
import { S9EntityWorkflowActions } from '@april9/stack9-ui';
<S9EntityWorkflowActions
entityKey="subscription"
entityId={123}
onWorkflowMove={handleWorkflowMove}
/>
Troubleshooting
Workflow Action Not Appearing
Problem: Expected action button doesn't show
Solutions:
- Check
from.stepincludes current step - Verify user has permission (ownershipDetails)
- Check conditions are met
- Ensure
preventUserInteraction: falseor not set - Check user is workflow owner if required
Cannot Move to Step
Problem: Transition fails with error
Solutions:
- Verify action exists in workflow definition
- Check user has permission
- Ensure required conditions are met
- Check
reasonRequiredif reason not provided - Validate entity passes validation if
fullValidationRequired: true
Workflow Stuck in Step
Problem: Entity not progressing
Solutions:
- Check default actions are configured
- Verify automations are triggering
- Check condition values are being set
- Review workflow logs for errors
- Ensure all paths lead to outcomes
Circular Workflow Reference
Problem: Workflow validation fails on startup
Solutions:
- Ensure every step path leads to an outcome
- Check for steps that only point to themselves
- Verify at least one action has
default: truefrom each step - Review workflow diagram for cycles
Next Steps
Congratulations! You've mastered Stack9 workflows. Continue learning:
- Automations - Integrate workflows with automations
- Entity Hooks - Add business logic to workflow transitions
- Building Custom Actions - Create workflow-specific actions
- User Management - Configure user groups and permissions
Summary
You now understand:
- Workflow definition structure within entities
- Steps, actions, and outcomes
- Conditional transitions and workflow conditions
- User permissions and ownership
- Workflow automation integration
- Real production workflow patterns
- Best practices for workflow design
- Troubleshooting common issues
Workflows enable powerful multi-step approval processes in Stack9. Use them to build complex business processes with full audit trails and permission controls!