.do
Service TypesAutomation

Workflow Automation Services

Build multi-step workflow automation services with conditional logic, parallel execution, error handling, and orchestration

Build sophisticated multi-step workflow automation services that orchestrate complex business processes with conditional branching, parallel execution, error handling, and state management.

Overview

Workflow automation services coordinate multiple steps into cohesive processes that run automatically. They're ideal for:

  • Order Fulfillment: Process orders from validation to shipping
  • Customer Onboarding: Guide new users through setup steps
  • Approval Workflows: Route requests through approval chains
  • Document Processing: Extract, validate, and route documents
  • Employee Onboarding: Automate HR processes
  • Issue Resolution: Automated troubleshooting and remediation

Core Concepts

Workflow Components

import $, { db, on, send } from 'sdk.do'

// Define workflow structure
const orderFulfillmentWorkflow = await $.Service.create({
  name: 'Order Fulfillment Automation',
  type: $.ServiceType.Automation,
  subtype: 'workflow',

  // Define workflow steps
  steps: [
    {
      id: 'validate',
      name: 'Validate Order',
      type: 'function',
      required: true,
      timeout: 5000,
      retryPolicy: { maxAttempts: 3, backoff: 'exponential' },
    },
    {
      id: 'payment',
      name: 'Process Payment',
      type: 'function',
      required: true,
      timeout: 30000,
      dependsOn: ['validate'],
    },
    {
      id: 'inventory',
      name: 'Reserve Inventory',
      type: 'parallel',
      required: true,
      dependsOn: ['payment'],
      steps: [
        { id: 'check-stock', name: 'Check Stock' },
        { id: 'reserve', name: 'Reserve Items' },
      ],
    },
    {
      id: 'shipping',
      name: 'Schedule Shipping',
      type: 'function',
      required: true,
      dependsOn: ['inventory'],
    },
    {
      id: 'notification',
      name: 'Send Confirmation',
      type: 'function',
      required: false,
      dependsOn: ['shipping'],
    },
  ],

  // Pricing model
  pricing: {
    model: 'per-execution',
    baseRate: 0.5,
    stepRates: {
      payment: 0.2,
      shipping: 0.15,
      notification: 0.05,
    },
  },
})

Building a Complete Workflow Service

Basic Workflow Execution

// Execute workflow with sequential steps
on.ServiceRequest.created(async (request) => {
  if (request.serviceId !== orderFulfillmentWorkflow.id) return

  const workflowState = {
    requestId: request.id,
    currentStep: null,
    completedSteps: [],
    results: {},
    context: request.inputs,
  }

  try {
    for (const step of orderFulfillmentWorkflow.steps) {
      // Check dependencies
      if (step.dependsOn) {
        const dependenciesMet = step.dependsOn.every((dep) => workflowState.completedSteps.includes(dep))
        if (!dependenciesMet) {
          throw new Error(`Dependencies not met for step: ${step.id}`)
        }
      }

      // Update current step
      workflowState.currentStep = step.id
      await send.WorkflowProgress.updated({
        requestId: request.id,
        currentStep: step.id,
        completedSteps: workflowState.completedSteps,
      })

      // Execute step
      const result = await executeStep(step, workflowState)

      // Store result
      workflowState.results[step.id] = result
      workflowState.completedSteps.push(step.id)

      // Pass results to context for next steps
      workflowState.context = {
        ...workflowState.context,
        ...result,
      }
    }

    // Workflow complete
    await send.ServiceResult.deliver({
      requestId: request.id,
      outputs: {
        success: true,
        results: workflowState.results,
        completedSteps: workflowState.completedSteps,
      },
    })

    // Calculate and charge
    const cost = calculateWorkflowCost(orderFulfillmentWorkflow, workflowState.completedSteps)

    await send.Payment.charge({
      customerId: request.customerId,
      amount: cost,
      description: 'Order Fulfillment Workflow',
    })
  } catch (error) {
    await send.ServiceRequest.fail({
      requestId: request.id,
      error: error.message,
      failedStep: workflowState.currentStep,
      completedSteps: workflowState.completedSteps,
      partialResults: workflowState.results,
    })
  }
})

// Execute individual step
async function executeStep(step: any, state: any) {
  switch (step.type) {
    case 'function':
      return await executeFunction(step, state)
    case 'parallel':
      return await executeParallel(step, state)
    case 'condition':
      return await executeCondition(step, state)
    default:
      throw new Error(`Unknown step type: ${step.type}`)
  }
}

Conditional Branching Workflows

// Customer onboarding with conditional paths
const onboardingWorkflow = await $.Service.create({
  name: 'Customer Onboarding',
  type: $.ServiceType.Automation,
  subtype: 'workflow',

  steps: [
    {
      id: 'profile',
      name: 'Create Profile',
      type: 'function',
      required: true,
    },
    {
      id: 'verify-identity',
      name: 'Identity Verification',
      type: 'condition',
      required: true,
      dependsOn: ['profile'],
      condition: 'inputs.accountType === "business"',
      then: {
        id: 'business-verification',
        name: 'Business Verification',
        type: 'function',
      },
      else: {
        id: 'personal-verification',
        name: 'Personal Verification',
        type: 'function',
      },
    },
    {
      id: 'setup-features',
      name: 'Setup Features',
      type: 'condition',
      dependsOn: ['verify-identity'],
      condition: 'results["verify-identity"].verified === true',
      then: {
        id: 'enable-all-features',
        type: 'parallel',
        steps: [
          { id: 'api-access', name: 'Enable API Access' },
          { id: 'integrations', name: 'Setup Integrations' },
          { id: 'team-invite', name: 'Invite Team Members' },
        ],
      },
      else: {
        id: 'limited-access',
        name: 'Enable Basic Features',
        type: 'function',
      },
    },
    {
      id: 'send-welcome',
      name: 'Send Welcome Email',
      type: 'function',
      required: false,
      dependsOn: ['setup-features'],
    },
  ],

  pricing: {
    model: 'per-execution',
    rate: 1.0,
  },
})

// Execute conditional workflow
on.ServiceRequest.created(async (request) => {
  if (request.serviceId !== onboardingWorkflow.id) return

  const state = {
    requestId: request.id,
    inputs: request.inputs,
    results: {},
    completedSteps: [],
  }

  try {
    for (const step of onboardingWorkflow.steps) {
      const shouldExecute = await evaluateStepCondition(step, state)

      if (!shouldExecute && step.required) {
        throw new Error(`Required step skipped: ${step.id}`)
      }

      if (shouldExecute) {
        if (step.type === 'condition') {
          // Evaluate condition
          const conditionMet = await evaluateCondition(step.condition, state)

          // Execute appropriate branch
          const branch = conditionMet ? step.then : step.else
          if (branch) {
            const result = await executeStep(branch, state)
            state.results[step.id] = { branch: conditionMet ? 'then' : 'else', result }
          }
        } else {
          const result = await executeStep(step, state)
          state.results[step.id] = result
        }

        state.completedSteps.push(step.id)
      }
    }

    await send.ServiceResult.deliver({
      requestId: request.id,
      outputs: {
        success: true,
        results: state.results,
        path: determinePath(state.results),
      },
    })
  } catch (error) {
    await handleWorkflowError(error, state)
  }
})

// Evaluate condition dynamically
async function evaluateCondition(condition: string, state: any): Promise<boolean> {
  try {
    // Create safe evaluation context
    const context = {
      inputs: state.inputs,
      results: state.results,
      completedSteps: state.completedSteps,
    }

    // Safe evaluation (in production, use a proper expression evaluator)
    const func = new Function('ctx', `with(ctx) { return ${condition} }`)
    return func(context)
  } catch {
    return false
  }
}

Parallel Execution Workflows

// Content production with parallel tasks
const contentProductionWorkflow = await $.Service.create({
  name: 'Content Production Pipeline',
  type: $.ServiceType.Automation,
  subtype: 'workflow',

  steps: [
    {
      id: 'research',
      name: 'Topic Research',
      type: 'function',
      required: true,
      timeout: 120000, // 2 minutes
    },
    {
      id: 'create-assets',
      name: 'Create Content Assets',
      type: 'parallel',
      required: true,
      dependsOn: ['research'],
      strategy: 'all', // wait for all, or 'any', 'first'
      steps: [
        {
          id: 'write-article',
          name: 'Write Article',
          type: 'function',
          timeout: 300000, // 5 minutes
        },
        {
          id: 'generate-images',
          name: 'Generate Images',
          type: 'function',
          timeout: 180000, // 3 minutes
        },
        {
          id: 'create-social',
          name: 'Create Social Posts',
          type: 'function',
          timeout: 120000, // 2 minutes
        },
        {
          id: 'record-video',
          name: 'Generate Video Script',
          type: 'function',
          timeout: 240000, // 4 minutes
        },
      ],
    },
    {
      id: 'quality-check',
      name: 'Quality Assurance',
      type: 'parallel',
      required: true,
      dependsOn: ['create-assets'],
      steps: [
        {
          id: 'grammar-check',
          name: 'Grammar Check',
          type: 'function',
        },
        {
          id: 'seo-check',
          name: 'SEO Analysis',
          type: 'function',
        },
        {
          id: 'plagiarism-check',
          name: 'Plagiarism Check',
          type: 'function',
        },
      ],
    },
    {
      id: 'publish',
      name: 'Publish Content',
      type: 'function',
      required: true,
      dependsOn: ['quality-check'],
    },
  ],

  pricing: {
    model: 'per-execution',
    baseRate: 50.0,
    parallelDiscount: 0.1, // 10% discount for parallel execution efficiency
  },
})

// Execute parallel workflows
async function executeParallel(step: any, state: any) {
  const promises = step.steps.map(async (substep: any) => {
    try {
      const result = await executeStep(substep, state)
      return {
        id: substep.id,
        success: true,
        result,
      }
    } catch (error) {
      return {
        id: substep.id,
        success: false,
        error: error.message,
      }
    }
  })

  // Execute based on strategy
  switch (step.strategy) {
    case 'all':
      // Wait for all steps to complete
      const results = await Promise.all(promises)

      // Check if any failed
      const failures = results.filter((r) => !r.success)
      if (failures.length > 0 && step.required) {
        throw new Error(`Parallel steps failed: ${failures.map((f) => f.id).join(', ')}`)
      }

      return {
        strategy: 'all',
        results,
        successes: results.filter((r) => r.success).length,
        failures: failures.length,
      }

    case 'any':
      // Wait for at least one to succeed
      const anyResult = await Promise.race(promises)
      return {
        strategy: 'any',
        result: anyResult,
      }

    case 'first':
      // Return first completed (success or failure)
      const firstResult = await Promise.race(promises)
      return {
        strategy: 'first',
        result: firstResult,
      }

    default:
      return await Promise.all(promises)
  }
}

Advanced Error Handling

// Invoice processing with retry and compensation
const invoiceWorkflow = await $.Service.create({
  name: 'Invoice Processing',
  type: $.ServiceType.Automation,
  subtype: 'workflow',

  steps: [
    {
      id: 'extract',
      name: 'Extract Invoice Data',
      type: 'function',
      required: true,
      timeout: 30000,
      retryPolicy: {
        maxAttempts: 3,
        backoff: 'exponential',
        baseDelay: 1000,
        maxDelay: 30000,
      },
      onError: 'retry',
    },
    {
      id: 'validate',
      name: 'Validate Invoice',
      type: 'function',
      required: true,
      timeout: 10000,
      retryPolicy: {
        maxAttempts: 2,
        backoff: 'linear',
        baseDelay: 2000,
      },
      onError: 'human-review', // Escalate to human
    },
    {
      id: 'approve',
      name: 'Get Approval',
      type: 'function',
      required: true,
      timeout: 300000, // 5 minutes for approval
      onError: 'notify-manager',
    },
    {
      id: 'process-payment',
      name: 'Process Payment',
      type: 'function',
      required: true,
      dependsOn: ['approve'],
      timeout: 60000,
      retryPolicy: {
        maxAttempts: 3,
        backoff: 'exponential',
        baseDelay: 5000,
      },
      onError: 'rollback',
      compensation: {
        // If payment fails, reverse approval
        steps: [
          { action: 'reverse-approval', stepId: 'approve' },
          { action: 'notify-failure', params: { reason: 'payment-failed' } },
        ],
      },
    },
    {
      id: 'update-accounting',
      name: 'Update Accounting System',
      type: 'function',
      required: true,
      dependsOn: ['process-payment'],
      timeout: 30000,
      onError: 'log-and-continue', // Non-critical, can be retried later
    },
    {
      id: 'notify-completion',
      name: 'Send Completion Notice',
      type: 'function',
      required: false,
      dependsOn: ['update-accounting'],
    },
  ],

  // Global error handling
  errorHandling: {
    defaultRetryPolicy: {
      maxAttempts: 2,
      backoff: 'exponential',
      baseDelay: 1000,
    },
    compensationStrategy: 'reverse-order', // Undo in reverse
    notifyOnFailure: true,
  },

  pricing: {
    model: 'per-execution',
    rate: 2.0,
    failureRefund: 1.0, // Partial refund on failure
  },
})

// Execute with advanced error handling
on.ServiceRequest.created(async (request) => {
  if (request.serviceId !== invoiceWorkflow.id) return

  const state = {
    requestId: request.id,
    inputs: request.inputs,
    results: {},
    completedSteps: [],
    failedSteps: [],
    compensations: [],
  }

  try {
    for (const step of invoiceWorkflow.steps) {
      let result
      let attempts = 0
      const maxAttempts = step.retryPolicy?.maxAttempts || 1

      while (attempts < maxAttempts) {
        try {
          result = await executeStepWithTimeout(step, state)
          break // Success
        } catch (error) {
          attempts++

          if (attempts >= maxAttempts) {
            // Max attempts reached
            await handleStepFailure(step, error, state)

            if (step.required) {
              throw error // Fail workflow
            }
            break // Continue to next step
          }

          // Wait before retry
          const delay = calculateRetryDelay(
            attempts,
            step.retryPolicy?.backoff || 'exponential',
            step.retryPolicy?.baseDelay || 1000,
            step.retryPolicy?.maxDelay || 30000
          )

          await new Promise((resolve) => setTimeout(resolve, delay))
        }
      }

      if (result) {
        state.results[step.id] = result
        state.completedSteps.push(step.id)
      } else {
        state.failedSteps.push(step.id)
      }
    }

    // Workflow completed (successfully or partially)
    await send.ServiceResult.deliver({
      requestId: request.id,
      outputs: {
        success: state.failedSteps.length === 0,
        results: state.results,
        completedSteps: state.completedSteps,
        failedSteps: state.failedSteps,
      },
    })

    // Charge based on completion
    const cost = state.failedSteps.length > 0 ? invoiceWorkflow.pricing.failureRefund : invoiceWorkflow.pricing.rate

    await send.Payment.charge({
      customerId: request.customerId,
      amount: cost,
      description: `Invoice Processing - ${state.completedSteps.length} steps completed`,
    })
  } catch (error) {
    // Critical failure - execute compensations
    await executeCompensations(state)

    await send.ServiceRequest.fail({
      requestId: request.id,
      error: error.message,
      failedStep: state.failedSteps[state.failedSteps.length - 1],
      compensations: state.compensations,
    })
  }
})

// Execute compensation actions
async function executeCompensations(state: any) {
  // Get steps that need compensation (in reverse order)
  const stepsToCompensate = [...state.completedSteps].reverse()

  for (const stepId of stepsToCompensate) {
    const step = invoiceWorkflow.steps.find((s) => s.id === stepId)

    if (step?.compensation) {
      try {
        for (const compensationAction of step.compensation.steps) {
          await executeCompensationAction(compensationAction, state)
          state.compensations.push({
            step: stepId,
            action: compensationAction.action,
            success: true,
          })
        }
      } catch (error) {
        state.compensations.push({
          step: stepId,
          action: 'compensation',
          success: false,
          error: error.message,
        })
      }
    }
  }
}

// Calculate retry delay with backoff
function calculateRetryDelay(attempt: number, backoff: string, baseDelay: number, maxDelay: number): number {
  let delay: number

  switch (backoff) {
    case 'exponential':
      delay = baseDelay * Math.pow(2, attempt - 1)
      break
    case 'linear':
      delay = baseDelay * attempt
      break
    case 'constant':
      delay = baseDelay
      break
    default:
      delay = baseDelay
  }

  return Math.min(delay, maxDelay)
}

Human-in-the-Loop Workflows

// Expense approval workflow with human review
const expenseApprovalWorkflow = await $.Service.create({
  name: 'Expense Approval Workflow',
  type: $.ServiceType.Automation,
  subtype: 'workflow',

  steps: [
    {
      id: 'extract',
      name: 'Extract Expense Data',
      type: 'function',
      required: true,
    },
    {
      id: 'policy-check',
      name: 'Check Against Policy',
      type: 'function',
      required: true,
      dependsOn: ['extract'],
    },
    {
      id: 'approval-routing',
      name: 'Route for Approval',
      type: 'condition',
      required: true,
      dependsOn: ['policy-check'],
      condition: 'results["policy-check"].autoApprove === true',
      then: {
        id: 'auto-approve',
        name: 'Auto Approve',
        type: 'function',
      },
      else: {
        id: 'human-approval',
        name: 'Human Approval Required',
        type: 'human-task',
        timeout: 86400000, // 24 hours
        assignTo: 'results["policy-check"].approver',
        escalationPolicy: {
          timeout: 43200000, // 12 hours
          escalateTo: 'manager',
        },
      },
    },
    {
      id: 'process',
      name: 'Process Expense',
      type: 'function',
      required: true,
      dependsOn: ['approval-routing'],
    },
  ],

  pricing: {
    model: 'per-execution',
    rates: {
      automated: 0.25,
      humanReview: 1.0,
    },
  },
})

// Handle human tasks
on.HumanTask.created(async (task) => {
  // Notify assigned user
  await send.Notification.send({
    userId: task.assignedTo,
    type: 'approval-required',
    data: {
      taskId: task.id,
      workflowId: task.workflowId,
      requestId: task.requestId,
      details: task.details,
      dueDate: new Date(Date.now() + task.timeout),
    },
  })

  // Set up escalation timer
  if (task.escalationPolicy) {
    setTimeout(async () => {
      const currentTask = await db.HumanTask.get({ id: task.id })

      if (currentTask.status === 'pending') {
        await send.HumanTask.escalate({
          taskId: task.id,
          escalateTo: task.escalationPolicy.escalateTo,
          reason: 'timeout',
        })
      }
    }, task.escalationPolicy.timeout)
  }
})

// Resume workflow after human task completion
on.HumanTask.completed(async (task) => {
  // Update workflow state
  const workflowState = await db.WorkflowState.get({
    requestId: task.requestId,
  })

  // Store human task result
  workflowState.results[task.stepId] = {
    approved: task.decision === 'approved',
    decision: task.decision,
    comments: task.comments,
    reviewedBy: task.completedBy,
    reviewedAt: task.completedAt,
  }

  // Mark step as completed
  workflowState.completedSteps.push(task.stepId)

  await db.update(workflowState, workflowState)

  // Resume workflow execution
  await send.Workflow.resume({
    requestId: task.requestId,
    stepId: task.stepId,
    result: workflowState.results[task.stepId],
  })
})

Workflow Orchestration Patterns

State Machine Workflows

// Order state machine
const orderStateMachine = await $.Service.create({
  name: 'Order State Machine',
  type: $.ServiceType.Automation,
  subtype: 'state-machine',

  states: {
    pending: {
      on: {
        VALIDATE: 'validating',
        CANCEL: 'cancelled',
      },
    },
    validating: {
      on: {
        VALIDATION_SUCCESS: 'validated',
        VALIDATION_FAILED: 'validation-failed',
      },
      timeout: 30000,
      timeoutTransition: 'validation-failed',
    },
    validated: {
      on: {
        PROCESS_PAYMENT: 'processing-payment',
      },
    },
    'processing-payment': {
      on: {
        PAYMENT_SUCCESS: 'paid',
        PAYMENT_FAILED: 'payment-failed',
      },
      retry: {
        maxAttempts: 3,
        on: 'PAYMENT_FAILED',
      },
    },
    paid: {
      on: {
        FULFILL: 'fulfilling',
      },
    },
    fulfilling: {
      on: {
        SHIPPED: 'shipped',
        FULFILLMENT_FAILED: 'fulfillment-failed',
      },
    },
    shipped: {
      on: {
        DELIVERED: 'delivered',
        RETURN_REQUESTED: 'returning',
      },
    },
    delivered: {
      terminal: true,
    },
    cancelled: {
      terminal: true,
    },
    'validation-failed': {
      terminal: true,
    },
    'payment-failed': {
      terminal: true,
    },
    'fulfillment-failed': {
      on: {
        RETRY: 'fulfilling',
      },
    },
  },

  initialState: 'pending',
})

// State machine execution
class StateMachineExecutor {
  async execute(machine: any, initialContext: any) {
    let currentState = machine.initialState
    const context = { ...initialContext }
    const history = []

    while (true) {
      const state = machine.states[currentState]

      // Check if terminal state
      if (state.terminal) {
        break
      }

      // Execute state entry actions
      if (state.onEntry) {
        await this.executeActions(state.onEntry, context)
      }

      // Wait for event or timeout
      const event = await this.waitForEvent(state, context)

      // Record transition
      history.push({
        from: currentState,
        event: event.type,
        at: new Date(),
      })

      // Transition to next state
      const nextState = state.on[event.type]
      if (!nextState) {
        throw new Error(`No transition for event ${event.type} in state ${currentState}`)
      }

      // Execute state exit actions
      if (state.onExit) {
        await this.executeActions(state.onExit, context)
      }

      currentState = nextState

      // Update context with event data
      Object.assign(context, event.data)
    }

    return {
      finalState: currentState,
      history,
      context,
    }
  }

  async waitForEvent(state: any, context: any) {
    // Implement event waiting logic
    // Could use event emitters, message queues, etc.
    return new Promise((resolve) => {
      // Wait for next event or timeout
    })
  }

  async executeActions(actions: any[], context: any) {
    for (const action of actions) {
      await this.executeAction(action, context)
    }
  }

  async executeAction(action: any, context: any) {
    // Execute action based on type
    switch (action.type) {
      case 'function':
        return await action.handler(context)
      case 'emit':
        return await send(action.event, context)
      default:
        throw new Error(`Unknown action type: ${action.type}`)
    }
  }
}

Pricing Models for Workflows

Per-Execution Pricing

const workflow = await $.Service.create({
  name: 'Document Processing',
  pricing: {
    model: 'per-execution',
    baseRate: 0.5, // Base cost per execution
    stepRates: {
      ocr: 0.25,
      classify: 0.1,
      extract: 0.15,
      validate: 0.05,
    },
    discounts: {
      volume: [
        { min: 100, max: 1000, discount: 0.1 },
        { min: 1001, max: 10000, discount: 0.2 },
        { min: 10001, max: Infinity, discount: 0.3 },
      ],
    },
  },
})

// Calculate execution cost
function calculateExecutionCost(workflow: any, completedSteps: string[], volume: number) {
  let cost = workflow.pricing.baseRate

  // Add step costs
  for (const stepId of completedSteps) {
    const stepCost = workflow.pricing.stepRates[stepId] || 0
    cost += stepCost
  }

  // Apply volume discount
  const discount = workflow.pricing.discounts.volume.find((tier) => volume >= tier.min && volume <= tier.max)

  if (discount) {
    cost *= 1 - discount.discount
  }

  return cost
}

Time-Based Pricing

const workflow = await $.Service.create({
  name: 'Video Processing Pipeline',
  pricing: {
    model: 'time-based',
    rate: 0.1, // Per second
    minimum: 1.0,
    maximum: 100.0,
    stepMultipliers: {
      transcode: 1.5,
      analyze: 1.2,
      'generate-thumbnails': 0.8,
    },
  },
})

// Calculate time-based cost
function calculateTimeCost(workflow: any, execution: any) {
  const durationSeconds = (execution.endTime - execution.startTime) / 1000
  let cost = durationSeconds * workflow.pricing.rate

  // Apply step multipliers
  for (const [stepId, duration] of Object.entries(execution.stepDurations)) {
    const multiplier = workflow.pricing.stepMultipliers[stepId] || 1.0
    cost += ((duration as number) / 1000) * workflow.pricing.rate * (multiplier - 1.0)
  }

  // Apply bounds
  cost = Math.max(cost, workflow.pricing.minimum)
  cost = Math.min(cost, workflow.pricing.maximum)

  return cost
}

Best Practices

1. Idempotency

Ensure workflows can be safely retried:

on.ServiceRequest.created(async (request) => {
  // Check for existing execution
  const existing = await db.WorkflowExecution.get({
    requestId: request.id,
  })

  if (existing) {
    if (existing.status === 'completed') {
      // Return cached result
      return await send.ServiceResult.deliver({
        requestId: request.id,
        outputs: existing.result,
        cached: true,
      })
    } else if (existing.status === 'running') {
      // Return execution status
      return await send.ServiceProgress.status({
        requestId: request.id,
        status: 'running',
        currentStep: existing.currentStep,
      })
    }
  }

  // Start new execution
  const execution = await db.create($.WorkflowExecution, {
    requestId: request.id,
    status: 'running',
    startedAt: new Date(),
  })

  // Execute workflow...
})

2. Progress Tracking

Keep users informed of workflow progress:

// Real-time progress updates
async function updateWorkflowProgress(requestId: string, step: any, state: any) {
  const totalSteps = state.workflow.steps.length
  const completedSteps = state.completedSteps.length
  const progress = completedSteps / totalSteps

  await send.WorkflowProgress.updated({
    requestId,
    currentStep: step.id,
    currentStepName: step.name,
    completedSteps: state.completedSteps,
    totalSteps,
    progress,
    estimatedTimeRemaining: estimateTimeRemaining(state),
  })
}

3. Error Recovery

Implement robust error handling:

// Checkpoint workflow state
async function checkpointWorkflowState(state: any) {
  await db.upsert($.WorkflowCheckpoint, {
    where: { requestId: state.requestId },
    data: {
      requestId: state.requestId,
      currentStep: state.currentStep,
      completedSteps: state.completedSteps,
      results: state.results,
      context: state.context,
      checkpointedAt: new Date(),
    },
  })
}

// Resume from checkpoint
async function resumeFromCheckpoint(requestId: string) {
  const checkpoint = await db.WorkflowCheckpoint.get({
    requestId,
  })

  if (!checkpoint) {
    throw new Error('No checkpoint found')
  }

  return {
    requestId: checkpoint.requestId,
    currentStep: checkpoint.currentStep,
    completedSteps: checkpoint.completedSteps,
    results: checkpoint.results,
    context: checkpoint.context,
  }
}

Real-World Examples

Customer Support Ticket Workflow

const supportTicketWorkflow = await $.Service.create({
  name: 'Support Ticket Routing',
  steps: [
    { id: 'classify', name: 'Classify Ticket' },
    { id: 'priority', name: 'Assign Priority' },
    { id: 'route', name: 'Route to Team', type: 'condition' },
    { id: 'auto-respond', name: 'Auto Respond' },
    { id: 'monitor', name: 'Monitor Resolution' },
  ],
  pricing: { model: 'per-execution', rate: 0.15 },
})

E-commerce Order Fulfillment

const ecommerceWorkflow = await $.Service.create({
  name: 'E-commerce Fulfillment',
  steps: [
    { id: 'validate-order', name: 'Validate Order' },
    { id: 'check-inventory', name: 'Check Inventory' },
    { id: 'process-payment', name: 'Process Payment' },
    { id: 'pick-pack', name: 'Pick and Pack', type: 'parallel' },
    { id: 'ship', name: 'Ship Order' },
    { id: 'track', name: 'Track Shipment' },
    { id: 'confirm', name: 'Confirm Delivery' },
  ],
  pricing: { model: 'per-execution', rate: 1.5 },
})

Next Steps