Skip to main content

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:

  1. Steps - States an entity can be in (e.g., "Pending", "Approved", "Rejected")
  2. Actions - Transitions between steps
  3. Outcomes - Final states (Success or Failure)
  4. Conditions - Requirements that must be met before transitions
  5. Ownership - User groups that can perform actions
  6. 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

PropertyTypeRequiredDescription
keystringYesUnique identifier (snake_case)
namestringYesDisplay name
descriptionstringNoStep description
colorstringNoUI color (yellow, blue, green, red, etc.)
ordernumberYesSequence number (unique)
positionobjectYesx,y coordinates for visual designer
ownershipDetailsobjectYesWho 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 access
  • userFields - 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

PropertyTypeRequiredDescription
keystringYesUnique action identifier
namestringYesDisplay name for action button
defaultbooleanNoAuto-execute if no other action taken
preventUserInteractionbooleanNoHide from UI (automation only)
fromobjectYesSource steps or outcomes
toobjectYesDestination 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/comment
  • fullValidationRequired - 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

PropertyTypeRequiredDescription
keystringYesUnique condition identifier
namestringYesDisplay name
descriptionstringYesCondition description
actionstringYesAction key this condition applies to
requiredbooleanYesWhether 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 Progress
  • blue - Requires Attention
  • green - Completed Successfully
  • red - Error/Failed
  • orange - On Hold/Deferred
  • darkgrey - 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:

  1. Check from.step includes current step
  2. Verify user has permission (ownershipDetails)
  3. Check conditions are met
  4. Ensure preventUserInteraction: false or not set
  5. Check user is workflow owner if required

Cannot Move to Step

Problem: Transition fails with error

Solutions:

  1. Verify action exists in workflow definition
  2. Check user has permission
  3. Ensure required conditions are met
  4. Check reasonRequired if reason not provided
  5. Validate entity passes validation if fullValidationRequired: true

Workflow Stuck in Step

Problem: Entity not progressing

Solutions:

  1. Check default actions are configured
  2. Verify automations are triggering
  3. Check condition values are being set
  4. Review workflow logs for errors
  5. Ensure all paths lead to outcomes

Circular Workflow Reference

Problem: Workflow validation fails on startup

Solutions:

  1. Ensure every step path leads to an outcome
  2. Check for steps that only point to themselves
  3. Verify at least one action has default: true from each step
  4. Review workflow diagram for cycles

Next Steps

Congratulations! You've mastered Stack9 workflows. Continue learning:

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!