.do
API Reference

Workflows API

Orchestrate multi-step business processes with event-driven workflows

Orchestrate multi-step business processes with event-driven workflows. Workflows compose multiple functions into coordinated processes that can be triggered manually, by events, on schedules, or via webhooks.

Overview

Business workflows are orchestrated sequences of steps that:

  • Compose multiple functions into coordinated processes
  • Support conditional branching and parallel execution
  • Handle errors and retries automatically
  • Track execution state and history
  • Can be triggered by events, schedules, or webhooks
import { createBusinessApi } from 'business-as-code'

const api = createBusinessApi({
  apiKey: process.env.APIS_DO_KEY,
})

// Trigger a workflow
const execution = await api.workflows.trigger('onboard-customer', {
  customerId: 'cust-123',
  planId: 'plan-pro',
})

// Check execution status
const status = await api.workflows.status(execution.executionId)
console.log('Status:', status.status) // 'completed'
console.log('Result:', status.result)

trigger()

Trigger a workflow execution with optional input data.

Signature

trigger(
  workflowId: string,
  input?: any
): Promise<{ executionId: string }>

Parameters

ParameterTypeRequiredDescription
workflowIdstringYesUnique identifier of the workflow to trigger
inputanyNoInput data passed to the workflow

Returns

Promise that resolves to an object containing the executionId.

Example

// Simple workflow trigger
const execution = await api.workflows.trigger('send-welcome-email', {
  email: '[email protected]',
  name: 'Jane Smith',
})

console.log('Execution ID:', execution.executionId)

Complete Example: Customer Onboarding

// Trigger customer onboarding workflow
const execution = await api.workflows.trigger('onboard-customer', {
  customer: {
    id: 'cust-123',
    name: 'Acme Corp',
    email: '[email protected]',
    plan: 'enterprise',
  },
  options: {
    sendWelcomeEmail: true,
    assignAccountManager: true,
    scheduleOnboardingCall: true,
  },
})

console.log('Onboarding started:', execution.executionId)

// Poll for completion
let status = await api.workflows.status(execution.executionId)

while (status.status === 'running' || status.status === 'pending') {
  await new Promise((resolve) => setTimeout(resolve, 2000))
  status = await api.workflows.status(execution.executionId)
  console.log('Status:', status.status)
}

if (status.status === 'completed') {
  console.log('Onboarding completed:', status.result)
} else {
  console.error('Onboarding failed:', status.error)
}

Error Handling

import { ApiError, ValidationError } from 'business-as-code'

try {
  const execution = await api.workflows.trigger('process-order', {
    orderId: 'order-123',
  })
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Invalid ${error.field}: ${error.message}`)
  } else if (error instanceof ApiError) {
    if (error.statusCode === 404) {
      console.error('Workflow not found')
    } else if (error.statusCode === 422) {
      console.error('Invalid workflow input:', error.body)
    } else {
      console.error('Failed to trigger workflow:', error.message)
    }
  }
}

status()

Check the execution status of a workflow.

Signature

status(
  executionId: string
): Promise<{ status: string; result?: any; error?: any }>

Parameters

ParameterTypeRequiredDescription
executionIdstringYesUnique identifier of the workflow execution

Returns

Promise that resolves to an object containing:

  • status: Current execution status ('pending', 'running', 'completed', 'failed')
  • result: Workflow result (if completed)
  • error: Error information (if failed)

Example

const status = await api.workflows.status('exec-abc123')

console.log('Status:', status.status)

if (status.status === 'completed') {
  console.log('Result:', status.result)
} else if (status.status === 'failed') {
  console.error('Error:', status.error)
}

Status Values

StatusDescription
pendingWorkflow is queued but not yet started
runningWorkflow is currently executing
completedWorkflow completed successfully
failedWorkflow failed with an error

Polling for Completion

async function waitForWorkflow(executionId: string, options = { pollInterval: 2000, timeout: 60000 }) {
  const startTime = Date.now()

  while (true) {
    const status = await api.workflows.status(executionId)

    if (status.status === 'completed') {
      return status.result
    }

    if (status.status === 'failed') {
      throw new Error(`Workflow failed: ${status.error}`)
    }

    if (Date.now() - startTime > options.timeout) {
      throw new Error('Workflow timed out')
    }

    await new Promise((resolve) => setTimeout(resolve, options.pollInterval))
  }
}

// Use it
const execution = await api.workflows.trigger('process-data', { data })
const result = await waitForWorkflow(execution.executionId)
console.log('Result:', result)

list()

Retrieve a list of all workflows.

Signature

list(): Promise<BusinessWorkflow[]>

Returns

Promise that resolves to an array of BusinessWorkflow objects.

Example

const workflows = await api.workflows.list()

workflows.forEach((workflow) => {
  console.log(`${workflow.name} (${workflow.id})`)
  console.log(`  Steps: ${workflow.steps.length}`)
  console.log(`  Triggers: ${workflow.triggers?.length || 0}`)
})

Response Type

interface BusinessWorkflow {
  id: string
  name: string
  description?: string
  steps: WorkflowStep[]
  triggers?: WorkflowTrigger[]
  metadata?: Record<string, any>
}

interface WorkflowStep {
  id: string
  name: string
  type: 'function' | 'condition' | 'parallel' | 'loop' | 'human' | 'agent'
  function?: string | BusinessFunction
  condition?: string
  next?: string | string[]
  config?: Record<string, any>
}

interface WorkflowTrigger {
  type: 'schedule' | 'event' | 'webhook' | 'manual'
  config: Record<string, any>
  condition?: string
}

get()

Retrieve a specific workflow by ID.

Signature

get(id: string): Promise<BusinessWorkflow>

Parameters

ParameterTypeRequiredDescription
idstringYesUnique identifier of the workflow

Returns

Promise that resolves to a BusinessWorkflow object.

Example

const workflow = await api.workflows.get('onboard-customer')

console.log('Workflow:', workflow.name)
console.log('Description:', workflow.description)
console.log('Steps:', workflow.steps.length)

create()

Create a new workflow.

Signature

create(
  workflow: Omit<BusinessWorkflow, 'id'>
): Promise<BusinessWorkflow>

Parameters

ParameterTypeRequiredDescription
workflowobjectYesWorkflow definition without ID

Returns

Promise that resolves to the created BusinessWorkflow with generated ID.

Example

const workflow = await api.workflows.create({
  name: 'Process Order',
  description: 'Complete order processing workflow from payment to fulfillment',

  steps: [
    {
      id: 'validate-order',
      name: 'Validate Order',
      type: 'function',
      function: 'validate-order-data',
      next: 'process-payment',
    },
    {
      id: 'process-payment',
      name: 'Process Payment',
      type: 'function',
      function: 'charge-payment',
      next: 'check-payment',
    },
    {
      id: 'check-payment',
      name: 'Check Payment Status',
      type: 'condition',
      condition: 'result.status === "succeeded"',
      next: ['update-inventory', 'send-confirmation'], // Parallel execution
    },
    {
      id: 'update-inventory',
      name: 'Update Inventory',
      type: 'function',
      function: 'decrement-inventory',
      next: 'schedule-fulfillment',
    },
    {
      id: 'send-confirmation',
      name: 'Send Order Confirmation',
      type: 'function',
      function: 'send-order-email',
      next: 'schedule-fulfillment',
    },
    {
      id: 'schedule-fulfillment',
      name: 'Schedule Order Fulfillment',
      type: 'function',
      function: 'create-fulfillment-task',
    },
  ],

  triggers: [
    {
      type: 'event',
      config: {
        eventType: 'order.created',
      },
    },
  ],

  metadata: {
    category: 'order-management',
    version: '1.0.0',
  },
})

console.log('Created workflow:', workflow.id)

Workflow Patterns

Sequential Steps

Execute steps one after another:

const workflow = await api.workflows.create({
  name: 'Customer Onboarding',
  steps: [
    {
      id: 'create-account',
      name: 'Create Account',
      type: 'function',
      function: 'create-customer-account',
      next: 'send-welcome',
    },
    {
      id: 'send-welcome',
      name: 'Send Welcome Email',
      type: 'function',
      function: 'send-welcome-email',
      next: 'assign-rep',
    },
    {
      id: 'assign-rep',
      name: 'Assign Sales Rep',
      type: 'function',
      function: 'assign-account-rep',
    },
  ],
})

Conditional Branching

Branch based on conditions:

const workflow = await api.workflows.create({
  name: 'Approve Purchase',
  steps: [
    {
      id: 'calculate-amount',
      name: 'Calculate Total Amount',
      type: 'function',
      function: 'calculate-purchase-total',
      next: 'check-threshold',
    },
    {
      id: 'check-threshold',
      name: 'Check Approval Threshold',
      type: 'condition',
      condition: 'result.total > 10000',
      next: 'require-approval', // if true
      config: {
        else: 'auto-approve', // if false
      },
    },
    {
      id: 'require-approval',
      name: 'Require Manager Approval',
      type: 'human',
      config: {
        approvers: ['[email protected]'],
        timeout: 86400000, // 24 hours
      },
      next: 'process-decision',
    },
    {
      id: 'auto-approve',
      name: 'Auto Approve',
      type: 'function',
      function: 'approve-purchase',
      next: 'finalize',
    },
    {
      id: 'process-decision',
      name: 'Process Approval Decision',
      type: 'condition',
      condition: 'result.approved === true',
      next: 'finalize',
      config: {
        else: 'reject',
      },
    },
    {
      id: 'finalize',
      name: 'Finalize Purchase',
      type: 'function',
      function: 'finalize-purchase',
    },
    {
      id: 'reject',
      name: 'Reject Purchase',
      type: 'function',
      function: 'reject-purchase',
    },
  ],
})

Parallel Execution

Execute multiple steps simultaneously:

const workflow = await api.workflows.create({
  name: 'Multi-Channel Notification',
  steps: [
    {
      id: 'prepare-notification',
      name: 'Prepare Notification Content',
      type: 'function',
      function: 'prepare-content',
      next: ['send-email', 'send-sms', 'send-push'], // Parallel
    },
    {
      id: 'send-email',
      name: 'Send Email',
      type: 'function',
      function: 'send-email-notification',
      next: 'track-results',
    },
    {
      id: 'send-sms',
      name: 'Send SMS',
      type: 'function',
      function: 'send-sms-notification',
      next: 'track-results',
    },
    {
      id: 'send-push',
      name: 'Send Push Notification',
      type: 'function',
      function: 'send-push-notification',
      next: 'track-results',
    },
    {
      id: 'track-results',
      name: 'Track Delivery Results',
      type: 'function',
      function: 'track-notification-delivery',
    },
  ],
})

Loop Pattern

Repeat steps based on conditions:

const workflow = await api.workflows.create({
  name: 'Retry Failed Payments',
  steps: [
    {
      id: 'get-failed-payments',
      name: 'Get Failed Payments',
      type: 'function',
      function: 'fetch-failed-payments',
      next: 'process-batch',
    },
    {
      id: 'process-batch',
      name: 'Process Payment Batch',
      type: 'loop',
      config: {
        items: 'result.payments',
        maxIterations: 100,
      },
      next: 'retry-payment',
    },
    {
      id: 'retry-payment',
      name: 'Retry Payment',
      type: 'function',
      function: 'process-payment',
      next: 'check-result',
    },
    {
      id: 'check-result',
      name: 'Check Retry Result',
      type: 'condition',
      condition: 'result.success === true',
      next: 'mark-success',
      config: {
        else: 'mark-failed',
      },
    },
    {
      id: 'mark-success',
      name: 'Mark as Successful',
      type: 'function',
      function: 'update-payment-status',
    },
    {
      id: 'mark-failed',
      name: 'Mark as Failed',
      type: 'function',
      function: 'log-payment-failure',
    },
  ],
})

Human-in-the-Loop

Require human approval or input:

const workflow = await api.workflows.create({
  name: 'Content Review Workflow',
  steps: [
    {
      id: 'submit-content',
      name: 'Submit Content',
      type: 'function',
      function: 'receive-content',
      next: 'ai-review',
    },
    {
      id: 'ai-review',
      name: 'AI Content Review',
      type: 'agent',
      config: {
        agentRole: 'content-reviewer',
        tools: ['sentiment-analysis', 'compliance-check'],
      },
      next: 'check-ai-decision',
    },
    {
      id: 'check-ai-decision',
      name: 'Check AI Decision',
      type: 'condition',
      condition: 'result.requiresHumanReview === true',
      next: 'human-review',
      config: {
        else: 'auto-publish',
      },
    },
    {
      id: 'human-review',
      name: 'Human Content Review',
      type: 'human',
      config: {
        reviewers: ['[email protected]', '[email protected]'],
        timeout: 172800000, // 48 hours
        requiredApprovals: 1,
      },
      next: 'process-review',
    },
    {
      id: 'process-review',
      name: 'Process Review Decision',
      type: 'condition',
      condition: 'result.approved === true',
      next: 'publish',
      config: {
        else: 'reject',
      },
    },
    {
      id: 'auto-publish',
      name: 'Auto Publish',
      type: 'function',
      function: 'publish-content',
    },
    {
      id: 'publish',
      name: 'Publish Content',
      type: 'function',
      function: 'publish-content',
    },
    {
      id: 'reject',
      name: 'Reject Content',
      type: 'function',
      function: 'reject-content',
    },
  ],
})

Workflow Triggers

Event Trigger

Trigger workflow when an event occurs:

const workflow = await api.workflows.create({
  name: 'Handle New Order',
  steps: [...],
  triggers: [
    {
      type: 'event',
      config: {
        eventType: 'order.created'
      }
    }
  ]
})

// Workflow automatically triggers when event is published
await api.events.publish({
  type: 'order.created',
  timestamp: new Date().toISOString(),
  source: 'order-service',
  data: { orderId: 'order-123' }
})

Schedule Trigger

Trigger workflow on a schedule:

const workflow = await api.workflows.create({
  name: 'Daily Report Generation',
  steps: [...],
  triggers: [
    {
      type: 'schedule',
      config: {
        cron: '0 9 * * *', // Every day at 9 AM
        timezone: 'America/New_York'
      }
    }
  ]
})

Webhook Trigger

Trigger workflow via HTTP webhook:

const workflow = await api.workflows.create({
  name: 'Process Stripe Webhook',
  steps: [...],
  triggers: [
    {
      type: 'webhook',
      config: {
        path: '/webhooks/stripe',
        method: 'POST',
        validateSignature: true,
        secret: process.env.STRIPE_WEBHOOK_SECRET
      }
    }
  ]
})

// Workflow URL: https://apis.do/webhooks/stripe

Manual Trigger

Trigger workflow manually via API:

const workflow = await api.workflows.create({
  name: 'Manual Data Import',
  steps: [...],
  triggers: [
    {
      type: 'manual'
    }
  ]
})

// Trigger manually
await api.workflows.trigger(workflow.id, { file: 'data.csv' })

Conditional Triggers

Add conditions to triggers:

const workflow = await api.workflows.create({
  name: 'High-Value Order Processing',
  steps: [...],
  triggers: [
    {
      type: 'event',
      config: {
        eventType: 'order.created'
      },
      condition: 'event.data.total > 1000' // Only trigger for orders > $1000
    }
  ]
})

Complete Examples

E-commerce Order Fulfillment

const orderFulfillment = await api.workflows.create({
  name: 'Order Fulfillment Workflow',
  description: 'Complete order fulfillment from payment to delivery',

  steps: [
    {
      id: 'validate',
      name: 'Validate Order',
      type: 'function',
      function: 'validate-order',
      next: 'process-payment',
    },
    {
      id: 'process-payment',
      name: 'Process Payment',
      type: 'function',
      function: 'charge-payment',
      next: 'verify-payment',
    },
    {
      id: 'verify-payment',
      name: 'Verify Payment',
      type: 'condition',
      condition: 'result.status === "succeeded"',
      next: ['update-inventory', 'notify-customer'],
      config: { else: 'payment-failed' },
    },
    {
      id: 'update-inventory',
      name: 'Update Inventory',
      type: 'function',
      function: 'decrement-stock',
      next: 'create-shipment',
    },
    {
      id: 'notify-customer',
      name: 'Send Confirmation Email',
      type: 'function',
      function: 'send-order-confirmation',
    },
    {
      id: 'create-shipment',
      name: 'Create Shipment',
      type: 'function',
      function: 'schedule-shipment',
      next: 'notify-warehouse',
    },
    {
      id: 'notify-warehouse',
      name: 'Notify Warehouse',
      type: 'function',
      function: 'send-pick-notification',
    },
    {
      id: 'payment-failed',
      name: 'Handle Payment Failure',
      type: 'function',
      function: 'notify-payment-failure',
    },
  ],

  triggers: [
    {
      type: 'event',
      config: { eventType: 'order.created' },
    },
  ],
})

Customer Support Ticket

const supportTicket = await api.workflows.create({
  name: 'Support Ticket Workflow',
  description: 'Automated support ticket triage and routing',

  steps: [
    {
      id: 'receive-ticket',
      name: 'Receive Support Ticket',
      type: 'function',
      function: 'parse-ticket',
      next: 'ai-triage',
    },
    {
      id: 'ai-triage',
      name: 'AI Ticket Triage',
      type: 'agent',
      config: {
        agentRole: 'support-triage',
        tools: ['sentiment-analysis', 'intent-classification', 'urgency-detection'],
      },
      next: 'check-urgency',
    },
    {
      id: 'check-urgency',
      name: 'Check Urgency Level',
      type: 'condition',
      condition: 'result.urgency === "critical"',
      next: 'escalate',
      config: { else: 'check-can-automate' },
    },
    {
      id: 'escalate',
      name: 'Escalate to Senior Support',
      type: 'function',
      function: 'assign-senior-agent',
      next: 'notify-escalation',
    },
    {
      id: 'check-can-automate',
      name: 'Check if Can Auto-Resolve',
      type: 'condition',
      condition: 'result.canAutoResolve === true',
      next: 'auto-resolve',
      config: { else: 'assign-agent' },
    },
    {
      id: 'auto-resolve',
      name: 'Auto-Resolve Ticket',
      type: 'agent',
      config: {
        agentRole: 'support-agent',
        tools: ['knowledge-base', 'customer-history'],
      },
      next: 'send-resolution',
    },
    {
      id: 'assign-agent',
      name: 'Assign to Support Agent',
      type: 'function',
      function: 'assign-to-available-agent',
    },
    {
      id: 'send-resolution',
      name: 'Send Resolution',
      type: 'function',
      function: 'send-ticket-response',
    },
    {
      id: 'notify-escalation',
      name: 'Notify Team of Escalation',
      type: 'function',
      function: 'send-escalation-alert',
    },
  ],

  triggers: [
    {
      type: 'event',
      config: { eventType: 'ticket.created' },
    },
    {
      type: 'webhook',
      config: {
        path: '/webhooks/support',
        method: 'POST',
      },
    },
  ],
})

Best Practices

1. Keep Steps Focused

Each step should do one thing:

// ❌ Bad - step does too much
{
  id: 'process-everything',
  type: 'function',
  function: 'validate-charge-and-fulfill'
}

// ✅ Good - clear, focused steps
{
  id: 'validate',
  type: 'function',
  function: 'validate-order',
  next: 'charge'
},
{
  id: 'charge',
  type: 'function',
  function: 'process-payment',
  next: 'fulfill'
},
{
  id: 'fulfill',
  type: 'function',
  function: 'create-fulfillment'
}

2. Handle Errors

Add error handling steps:

const workflow = await api.workflows.create({
  name: 'Payment with Error Handling',
  steps: [
    {
      id: 'charge',
      type: 'function',
      function: 'process-payment',
      next: 'verify',
      config: {
        onError: 'handle-error',
        retries: 3,
      },
    },
    {
      id: 'verify',
      type: 'condition',
      condition: 'result.success === true',
      next: 'success',
      config: { else: 'handle-error' },
    },
    {
      id: 'handle-error',
      type: 'function',
      function: 'log-payment-error',
      next: 'notify-failure',
    },
    {
      id: 'notify-failure',
      type: 'function',
      function: 'send-failure-notification',
    },
    {
      id: 'success',
      type: 'function',
      function: 'complete-order',
    },
  ],
})

3. Use Descriptive Names

Make workflow intent clear:

// ✅ Good names
const workflow = await api.workflows.create({
  name: 'Customer Onboarding with Trial Period',
  steps: [
    { id: 'create-trial-account', name: 'Create Trial Account', ... },
    { id: 'send-welcome-series', name: 'Send Welcome Email Series', ... },
    { id: 'schedule-followup', name: 'Schedule 7-Day Follow-up', ... }
  ]
})

4. Document Complex Logic

Add descriptions for complex conditions:

{
  id: 'check-eligibility',
  name: 'Check Discount Eligibility',
  type: 'condition',
  condition: 'customer.lifetimeValue > 10000 && customer.purchases > 5',
  config: {
    description: 'Customer qualifies for VIP discount if LTV > $10k and 5+ purchases'
  }
}

Next Steps