Complete Examples

This section provides complete, real-world examples of workflow implementations showing design, ETL integration, and REST API usage.

Example 1: Leave Request Approval Workflow

A complete leave request approval system with email notifications, approval rules, and timeout handling.

Business Requirements

  • Employees submit leave requests
  • Manager approval required for all requests
  • Senior manager approval required for > 5 days
  • Auto-escalate if no response within 3 days
  • Email notifications at each stage
  • Audit trail for compliance

Workflow Design

States: 1. Start - Entry point 2. Draft - Employee creating request 3. Pending Manager Approval - Waiting for manager 4. Pending Senior Approval - Waiting for senior manager (>5 days only) 5. Approved - Request approved 6. Rejected - Request rejected 7. Cancelled - Employee cancelled 8. Stop - Exit point

Transitions: - submit - Draft → Pending Manager Approval - approve_manager - Pending Manager Approval → Approved (≤5 days) - approve_manager - Pending Manager Approval → Pending Senior Approval (>5 days) - reject_manager - Pending Manager Approval → Rejected - approve_senior - Pending Senior Approval → Approved - reject_senior - Pending Senior Approval → Rejected - cancel - Draft/Pending → Cancelled - escalate - Pending → Pending (loop for escalation) - close - Approved/Rejected/Cancelled → Stop

State Configuration

Draft State: - OnEntry: None - OnExit: Validate leave request data - OnAfter: None

Pending Manager Approval State: - OnEntry: Send notification to manager - OnExit: None - OnAfter: 3 days → escalate event

Pending Senior Approval State: - OnEntry: Send notification to senior manager - OnExit: None - OnAfter: 2 days → escalate event

Approved State: - OnEntry: - Send approval notification to employee - Update HR system - Set redirect to confirmation page - OnExit: None - OnAfter: None

Rejected State: - OnEntry: - Send rejection notification to employee - Set redirect to rejection page - OnExit: None - OnAfter: None

Transition Configuration

submit Transition: - Event: submit - Guard: None - OnTransition: Calculate leave days and validate dates

approve_manager Transition: - Event: approve - Guard: Check if days ≤ 5 OR user is senior manager - OnTransition: Record approval details

approve_manager (to Senior) Transition: - Event: approve - Guard: Check if days > 5 AND user is manager (not senior) - OnTransition: Record manager pre-approval

reject_manager Transition: - Event: reject - Guard: Check user is manager - OnTransition: Record rejection details

escalate Transition: - Event: escalate - Guard: None - OnTransition: Send escalation email, increment counter

ETL Chains

Chain: ValidateLeaveRequest

Purpose: Validate leave request data before submission
Used in: Draft state OnExit

Steps:
1. GetCurrentState
2. Transform: Extract dates
   - startDate from elxPublic.startDate
   - endDate from elxPublic.endDate
3. Validate: Check dates
   - startDate must be future date
   - endDate must be after startDate
   - No overlapping leave requests
4. Conditional: If invalid
   - Add error to elxPublic.errors
   - Fail chain (blocks transition)
5. If valid: Continue

Chain: CalculateLeaveDays

Purpose: Calculate working days between dates
Used in: submit transition OnTransition

Steps:
1. GetCurrentState
2. Transform: Calculate Days
   - Get startDate and endDate
   - Calculate working days (exclude weekends/holidays)
   - Calculate total calendar days
3. workflow.MergeToElxPublic
   - Add workingDays field
   - Add calendarDays field

Chain: NotifyManager

Purpose: Send email to manager for approval
Used in: Pending Manager Approval OnEntry

Steps:
1. GetCurrentState
2. Transform: Get Manager Email
   - Query employee record for manager
   - Get manager email address
3. Transform: Format Email
   - Subject: "Leave Request Pending Approval"
   - Body: Include employee, dates, days, reason
   - Include approval/reject links
4. SendEmail
   - To: manager email
   - Include HTML formatted body
5. workflow.AddToElxPrivate
   - field: "managerNotified"
   - value: current timestamp

Chain: CheckApprovalAuthority

Purpose: Verify approver has authority
Used in: approve_manager transition Guard

Steps:
1. GetCurrentState
2. Transform: Get Leave Days
   - Extract workingDays from elxPublic
3. Transform: Get User Role
   - Get current user from context
   - Query user roles
4. Conditional: Check Authority
   - If days <= 5 AND user is manager: Success
   - If days > 5 AND user is senior manager: Success
   - Else: Fail
5. workflow.GuardEndPoint

Chain: UpdateHRSystem

Purpose: Update external HR system with approved leave
Used in: Approved state OnEntry

Steps:
1. GetCurrentState
2. Transform: Prepare HR Data
   - Extract employee ID
   - Extract leave dates
   - Extract leave type
3. HTTP Request: POST to HR API
   - Endpoint: /api/leave/approve
   - Body: Leave details
   - Auth: Service account token
4. Conditional: Check Response
   - If success: Continue
   - If failure: Log error, notify admin
5. workflow.MergeToElxPrivate
   - Add hrConfirmationNumber
   - Add hrSyncTimestamp
6. workflow.SetElxRedirect
   - redirectUrl: "/leave-requests/confirmation"

Chain: SendEscalation

Purpose: Send escalation email
Used in: escalate transition OnTransition

Steps:
1. GetCurrentState
2. Transform: Get Escalation Count
   - Get current count from elxPrivate.escalationCount
   - Increment by 1
3. Transform: Get Email Recipients
   - If count = 1: Manager + Senior Manager
   - If count = 2: Senior Manager + Director
   - If count >= 3: Director + HR
4. Transform: Format Escalation Email
   - Subject: "ESCALATION: Leave Request Overdue"
   - Body: Include all details + overdue duration
5. SendEmail
   - To: Recipients based on count
   - Mark as high priority
6. workflow.MergeToElxPrivate
   - Update escalationCount
   - Add escalationTimestamp
7. Conditional: Auto-Action
   - If count >= 3: Auto-approve or escalate to HR

REST API Integration

Create Leave Request (Web Form):

async function submitLeaveRequest(formData) {
  const response = await fetch('/api/workflow/create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getToken()}`
    },
    body: JSON.stringify({
      workflowId: 'leave-request',
      data: {
        elxPublic: {
          employeeId: getCurrentUser().id,
          employeeName: getCurrentUser().name,
          employeeEmail: getCurrentUser().email,
          leaveType: formData.leaveType,
          startDate: formData.startDate,
          endDate: formData.endDate,
          reason: formData.reason,
          status: 'Draft'
        }
      }
    })
  });
  
  const result = await response.json();
  
  // Now submit the request
  await sendEvent(result.instanceId, 'submit');
}

async function sendEvent(instanceId, eventName, data = {}) {
  const response = await fetch('/api/workflow/event', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getToken()}`
    },
    body: JSON.stringify({
      workflowId: 'leave-request',
      instanceId: instanceId,
      event: {
        name: eventName,
        data: data
      }
    })
  });
  
  return await response.json();
}

Approve/Reject Actions:

async function approveLeaveRequest(instanceId, comments) {
  return await sendEvent(instanceId, 'approve', {
    approver: getCurrentUser().id,
    approverName: getCurrentUser().name,
    approvedAt: new Date().toISOString(),
    comments: comments
  });
}

async function rejectLeaveRequest(instanceId, reason) {
  return await sendEvent(instanceId, 'reject', {
    rejectedBy: getCurrentUser().id,
    rejectedByName: getCurrentUser().name,
    rejectedAt: new Date().toISOString(),
    reason: reason
  });
}

Manager Dashboard:

async function loadPendingApprovals() {
  const response = await fetch(
    '/api/workflow/instances/leave-request',
    {
      headers: {
        'Authorization': `Bearer ${getToken()}`
      }
    }
  );
  
  const result = await response.json();
  
  // Filter for pending manager approvals
  const pending = result.instances.filter(instance => {
    const state = instance.states[0].nodeId;
    return state === 'PendingManagerApproval' || 
           state === 'PendingSeniorApproval';
  });
  
  // Sort by submission date
  pending.sort((a, b) => 
    new Date(a.elxPublic.submittedAt) - new Date(b.elxPublic.submittedAt)
  );
  
  return pending;
}

Data Structure

elxPublic (visible to user):

{
  "employeeId": "EMP001",
  "employeeName": "Jane Doe",
  "employeeEmail": "jane.doe@company.com",
  "leaveType": "Annual",
  "startDate": "2026-03-01",
  "endDate": "2026-03-07",
  "workingDays": 5,
  "calendarDays": 7,
  "reason": "Family vacation",
  "status": "Approved",
  "submittedAt": "2026-02-28T09:00:00Z",
  "approvedAt": "2026-02-28T10:30:00Z",
  "approvedBy": "Manager Smith"
}

elxPrivate (server-side only):

{
  "managerId": "MGR001",
  "managerEmail": "manager.smith@company.com",
  "managerNotified": "2026-02-28T09:01:00Z",
  "escalationCount": 0,
  "hrConfirmationNumber": "HR-2026-001234",
  "hrSyncTimestamp": "2026-02-28T10:31:00Z",
  "approvalNotes": "Standard approval, no issues",
  "budgetImpact": 2500.00
}

Complete Workflow JSON

{
  "_id": "leave-request",
  "name": "Leave Request",
  "owner": "admin",
  "database": "main",
  "collection": "leaveRequests",
  "chainsetId": "leave-request-chains",
  "stateMachines": [
    {
      "id": "main",
      "name": "Leave Request Flow",
      "initZoom": 100,
      "graph": {
        "nodes": [
          {
            "id": "Start",
            "label": "Start",
            "type": "start"
          },
          {
            "id": "Draft",
            "label": "Draft",
            "onEntry": null,
            "onExit": "ValidateLeaveRequest",
            "onAfter": null
          },
          {
            "id": "PendingManagerApproval",
            "label": "Pending Manager Approval",
            "onEntry": "NotifyManager",
            "onExit": null,
            "onAfter": {
              "units": "Days",
              "time": 3,
              "event": "escalate"
            }
          },
          {
            "id": "PendingSeniorApproval",
            "label": "Pending Senior Approval",
            "onEntry": "NotifySeniorManager",
            "onExit": null,
            "onAfter": {
              "units": "Days",
              "time": 2,
              "event": "escalate"
            }
          },
          {
            "id": "Approved",
            "label": "Approved",
            "onEntry": "ApprovalActions",
            "onExit": null,
            "onAfter": null
          },
          {
            "id": "Rejected",
            "label": "Rejected",
            "onEntry": "RejectionActions",
            "onExit": null,
            "onAfter": null
          },
          {
            "id": "Stop",
            "label": "Stop",
            "type": "stop"
          }
        ],
        "edges": [
          {
            "from": "Start",
            "to": "Draft",
            "label": "create",
            "event": "",
            "guard": null,
            "onTransition": null
          },
          {
            "from": "Draft",
            "to": "PendingManagerApproval",
            "label": "submit",
            "event": "submit",
            "guard": null,
            "onTransition": "CalculateLeaveDays"
          },
          {
            "from": "PendingManagerApproval",
            "to": "Approved",
            "label": "approve_manager",
            "event": "approve",
            "guard": "CheckApprovalAuthority",
            "onTransition": "RecordApproval"
          },
          {
            "from": "PendingManagerApproval",
            "to": "PendingSeniorApproval",
            "label": "escalate_to_senior",
            "event": "approve",
            "guard": "RequiresSeniorApproval",
            "onTransition": "RecordManagerPreApproval"
          },
          {
            "from": "PendingManagerApproval",
            "to": "Rejected",
            "label": "reject_manager",
            "event": "reject",
            "guard": null,
            "onTransition": "RecordRejection"
          },
          {
            "from": "PendingSeniorApproval",
            "to": "Approved",
            "label": "approve_senior",
            "event": "approve",
            "guard": null,
            "onTransition": "RecordApproval"
          },
          {
            "from": "PendingSeniorApproval",
            "to": "Rejected",
            "label": "reject_senior",
            "event": "reject",
            "guard": null,
            "onTransition": "RecordRejection"
          },
          {
            "from": "PendingManagerApproval",
            "to": "PendingManagerApproval",
            "label": "escalate",
            "event": "escalate",
            "guard": null,
            "onTransition": "SendEscalation"
          },
          {
            "from": "Approved",
            "to": "Stop",
            "label": "close",
            "event": "",
            "guard": null,
            "onTransition": null
          },
          {
            "from": "Rejected",
            "to": "Stop",
            "label": "close",
            "event": "",
            "guard": null,
            "onTransition": null
          }
        ]
      }
    }
  ]
}

Example 2: Issue Tracking System

A complete issue tracking workflow with SLA monitoring using multiple state machines.

Business Requirements

  • Track issues from creation to resolution
  • Support assignment, reassignment, and escalation
  • Monitor SLA compliance
  • Allow reopening of closed issues
  • Maintain complete audit trail

Workflow Design

This workflow uses two state machines: 1. Issue Lifecycle - Main issue states 2. SLA Tracker - Parallel SLA monitoring

State Machine 1: Issue Lifecycle

States: - Start - New - Open - In Progress - Resolved - Closed - Stop

Transitions: - create - Start → New - assign - New → Open - start_work - Open → In Progress - reassign - Open → Open (loop) - resolve - In Progress → Resolved - reopen - Resolved → Open - close - Resolved → Closed - reopen_closed - Closed → Open - archive - Closed → Stop

State Machine 2: SLA Tracker

States: - Start - On Time - Warning - Overdue - Stop

Transitions: - start_tracking - Start → On Time - warn - On Time → Warning (after 80% of SLA time) - overdue - Warning → Overdue (after 100% of SLA time) - complete - Any → Stop (when issue closed)

State Configuration

Issue Lifecycle - In Progress State: - OnEntry: Notify assignee, start timer - OnExit: Calculate time spent - OnAfter: 24 hours → check_progress event

SLA Tracker - Warning State: - OnEntry: Send warning email to assignee and manager - OnExit: None - OnAfter: Remaining SLA time → overdue event

SLA Tracker - Overdue State: - OnEntry: Send escalation email, create escalation ticket - OnExit: None - OnAfter: None

Guard Example: Prevent Closing Without Resolution

Guard Chain for “close” Transition:

1. GetCurrentState
2. workflow.LookupStates
   - Check if in "Resolved" state
3. Conditional
   - If in Resolved: Success
   - Else: Fail with message "Must resolve before closing"
4. workflow.GuardEndPoint

Multiple State Machine Coordination

Guard for Issue Lifecycle using SLA State:

Purpose: Only allow certain actions if SLA is not overdue

Guard Type: State Machine Guard
- State Machine: SLA Tracker
- State: Not "Overdue"
- Action: If overdue, require manager approval

REST API Usage

Create Issue:

const result = await fetch('/api/workflow/create', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    workflowId: 'issue-tracker',
    data: {
      elxPublic: {
        title: 'Login page not loading',
        description: 'Users unable to access login page',
        priority: 'High',
        category: 'Bug',
        reporter: 'john.doe',
        reportedAt: new Date().toISOString()
      },
      elxPrivate: {
        slaHours: 24, // High priority = 24 hour SLA
        slaDeadline: calculateDeadline(24)
      }
    }
  })
});

Query Overdue Issues:

const response = await fetch(
  '/api/workflow/instances/issue-tracker',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const result = await response.json();

// Filter for overdue issues
const overdue = result.instances.filter(instance => {
  // Check SLA Tracker state machine
  const slaState = instance.states.find(s => 
    s.stateMachineId === 'sla-tracker'
  );
  return slaState && slaState.nodeId === 'Overdue';
});

Example 3: Document Approval with Parallel Reviews

A document approval workflow where multiple reviewers must approve in parallel.

Business Requirements

  • Multiple reviewers approve simultaneously
  • All reviewers must approve before proceeding
  • Any reviewer can reject
  • Track individual review status
  • Support revision and resubmission

Implementation Strategy

Use super states to model parallel reviews:

States: - Start - Draft - Under Review (Super State) - Review 1 (sub-state) - Review 2 (sub-state) - Review 3 (sub-state) - Approved - Rejected - Stop

Logic: - When entering “Under Review”, all sub-states become active - Each reviewer sends approve/reject event - Use guard to check if all reviews complete - Transition to Approved only when all approve - Transition to Rejected if any reject

ETL Chain: Check All Reviews Complete

Purpose: Guard to check if all reviewers have responded
Used in: Transition to Approved

Steps:
1. GetCurrentState
2. Transform: Extract Review Status
   - Get review1Status from elxPrivate
   - Get review2Status from elxPrivate
   - Get review3Status from elxPrivate
3. Conditional: Check All Approved
   - If all = "approved": Success
   - If any = "rejected": Fail
   - If any = "pending": Fail
4. workflow.GuardEndPoint

This pattern allows flexible parallel approval workflows.

See Also