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
| Parameter | Type | Required | Description |
|---|---|---|---|
| workflowId | string | Yes | Unique identifier of the workflow to trigger |
| input | any | No | Input 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
| Parameter | Type | Required | Description |
|---|---|---|---|
| executionId | string | Yes | Unique 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
| Status | Description |
|---|---|
| pending | Workflow is queued but not yet started |
| running | Workflow is currently executing |
| completed | Workflow completed successfully |
| failed | Workflow 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
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique 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
| Parameter | Type | Required | Description |
|---|---|---|---|
| workflow | object | Yes | Workflow 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/stripeManual 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
- Agents API - Use autonomous agents in workflows
- Functions API - Create functions for workflow steps
- Events API - Trigger workflows with events