Skip to main content

Workflows

Workflows orchestrate complex, multi-step processes by connecting agents, conditions, and transformations into executable graphs.

Why Workflows?

Some processes are too complex for a single agent:

  • Multi-stage approval: Review → Manager approval → Finance sign-off → Execute
  • Data pipelines: Extract → Transform → Validate → Load
  • Customer journeys: Qualify → Enrich → Route → Assign → Notify
  • Conditional logic: If high-value → premium path, else → standard path

Workflows give you:

  • Visual process design
  • Conditional branching
  • Parallel execution
  • Human-in-the-loop approvals
  • Error handling and retries

Workflow Concepts

Nodes

Nodes are the building blocks:

Node TypePurpose
startEntry point (exactly one required)
agentExecute an agent
conditionBranch based on conditions
parallelExecute branches concurrently
mergeJoin parallel branches
transformTransform data
delayPause execution
humanWait for human input/approval
endExit point (at least one required)

Edges

Edges connect nodes and define execution flow:

edges: [
{ id: 'e1', source: 'start', target: 'agent1' },
{ id: 'e2', source: 'agent1', target: 'end' }
]

Creating Workflows

Basic Workflow

const workflow = await client.workflows.create({
name: 'Lead Processing',
description: 'Qualify and route incoming leads',
nodes: [
{
id: 'start',
type: 'start'
},
{
id: 'qualify',
type: 'agent',
agentId: 'qualification-agent',
inputMapping: {
leadData: '$.input.lead'
}
},
{
id: 'end',
type: 'end'
}
],
edges: [
{ id: 'e1', source: 'start', target: 'qualify' },
{ id: 'e2', source: 'qualify', target: 'end' }
]
});

With Conditional Branching

const workflow = await client.workflows.create({
name: 'Lead Routing',
nodes: [
{ id: 'start', type: 'start' },
{
id: 'qualify',
type: 'agent',
agentId: 'qualification-agent'
},
{
id: 'check-value',
type: 'condition',
conditions: [
{
id: 'high-value',
expression: '$.qualify.output.score >= 80',
target: 'premium-agent'
},
{
id: 'default',
expression: 'true', // Fallback
target: 'standard-agent'
}
]
},
{
id: 'premium-agent',
type: 'agent',
agentId: 'premium-sales-agent'
},
{
id: 'standard-agent',
type: 'agent',
agentId: 'standard-sales-agent'
},
{ id: 'end', type: 'end' }
],
edges: [
{ id: 'e1', source: 'start', target: 'qualify' },
{ id: 'e2', source: 'qualify', target: 'check-value' },
{ id: 'e3', source: 'premium-agent', target: 'end' },
{ id: 'e4', source: 'standard-agent', target: 'end' }
]
});

With Parallel Execution

const workflow = await client.workflows.create({
name: 'Customer Enrichment',
nodes: [
{ id: 'start', type: 'start' },
{
id: 'parallel-enrich',
type: 'parallel',
branches: ['social-lookup', 'company-lookup', 'history-lookup']
},
{
id: 'social-lookup',
type: 'agent',
agentId: 'social-enrichment-agent'
},
{
id: 'company-lookup',
type: 'agent',
agentId: 'company-enrichment-agent'
},
{
id: 'history-lookup',
type: 'agent',
agentId: 'history-agent'
},
{
id: 'merge-results',
type: 'merge',
strategy: 'all' // Wait for all branches
},
{
id: 'compile',
type: 'agent',
agentId: 'compilation-agent',
inputMapping: {
social: '$.social-lookup.output',
company: '$.company-lookup.output',
history: '$.history-lookup.output'
}
},
{ id: 'end', type: 'end' }
],
edges: [
{ id: 'e1', source: 'start', target: 'parallel-enrich' },
{ id: 'e2', source: 'social-lookup', target: 'merge-results' },
{ id: 'e3', source: 'company-lookup', target: 'merge-results' },
{ id: 'e4', source: 'history-lookup', target: 'merge-results' },
{ id: 'e5', source: 'merge-results', target: 'compile' },
{ id: 'e6', source: 'compile', target: 'end' }
]
});

Node Types

Start Node

Every workflow begins with exactly one start node:

{
id: 'start',
type: 'start'
}

Agent Node

Execute an agent with input/output mapping:

{
id: 'process-data',
type: 'agent',
agentId: 'data-processor-agent',
inputMapping: {
// Map workflow data to agent input
data: '$.input.rawData',
config: '$.previous-node.output.config'
},
outputMapping: {
// Map agent output to workflow context
processedData: '$.result',
stats: '$.metadata.stats'
},
settings: {
timeoutMs: 60000,
retries: 2
}
}

Condition Node

Branch based on expressions:

{
id: 'route-by-priority',
type: 'condition',
conditions: [
{
id: 'urgent',
expression: '$.input.priority === "urgent"',
target: 'urgent-handler'
},
{
id: 'high',
expression: '$.input.priority === "high"',
target: 'high-handler'
},
{
id: 'default',
expression: 'true',
target: 'standard-handler'
}
]
}

Parallel Node

Execute multiple branches concurrently:

{
id: 'parallel-tasks',
type: 'parallel',
branches: ['branch-a', 'branch-b', 'branch-c'],
maxConcurrency: 3 // Optional limit
}

Merge Node

Join parallel branches:

{
id: 'merge-branches',
type: 'merge',
strategy: 'all', // 'all' | 'any' | 'race'
timeout: 300000 // Optional timeout
}
StrategyBehavior
allWait for all branches to complete
anyContinue when any branch completes
raceContinue when first branch completes, cancel others

Transform Node

Transform data without an agent:

{
id: 'transform-data',
type: 'transform',
transformType: 'jsonpath', // or 'javascript'
transform: {
// JSONPath mapping
fullName: '$.input.firstName + " " + $.input.lastName',
email: '$.input.email.toLowerCase()'
}
}

// Or with JavaScript
{
id: 'custom-transform',
type: 'transform',
transformType: 'javascript',
transform: `
const { input } = context;
return {
fullName: input.firstName + ' ' + input.lastName,
email: input.email.toLowerCase(),
createdAt: new Date().toISOString()
};
`
}

Delay Node

Pause execution:

{
id: 'wait-period',
type: 'delay',
delayMs: 60000, // 1 minute
// Or use expression
delayExpression: '$.input.waitTimeMs'
}

Human Node

Wait for human input (see Human-in-the-Loop):

{
id: 'manager-approval',
type: 'human',
prompt: 'Please review and approve this request',
approvers: ['manager@company.com'],
inputFields: [
{
name: 'approved',
type: 'boolean',
required: true
},
{
name: 'comments',
type: 'text',
required: false
}
],
timeoutMs: 86400000, // 24 hours
autoApproveOnTimeout: false
}

End Node

Every workflow needs at least one end node:

{
id: 'end',
type: 'end',
outputMapping: {
// Define workflow output
result: '$.final-agent.output',
summary: '$.compile.output.summary'
}
}

Input/Output Mapping

JSONPath Syntax

Reference data throughout the workflow:

// Workflow input
'$.input.fieldName'

// Previous node output
'$.nodeId.output.fieldName'

// Nested access
'$.nodeId.output.nested.deeply.value'

// Array access
'$.nodeId.output.items[0].name'

Example Mappings

// Agent node with full mapping
{
id: 'enrich-customer',
type: 'agent',
agentId: 'enrichment-agent',
inputMapping: {
// From workflow input
customerId: '$.input.customerId',
// From previous node
qualificationScore: '$.qualify.output.score',
// Static value
enrichmentType: 'full'
},
outputMapping: {
enrichedData: '$.result.data',
confidence: '$.result.confidence'
}
}

Workflow Settings

const workflow = await client.workflows.create({
name: 'Complex Process',
settings: {
maxExecutionTimeMs: 3600000, // 1 hour max
maxNodeRetries: 5, // Retry failed nodes
enableLogging: true, // Detailed execution logs
shareMemory: true // Agents share memory context
},
nodes: [...],
edges: [...]
});

Running Workflows

Execute Workflow

const run = await client.workflows.execute('workflow-123', {
input: {
customerId: 'cust-456',
priority: 'high'
}
});

console.log(run.id); // run-789
console.log(run.status); // 'running'

Monitor Execution

// Get run status
const run = await client.workflows.getRun('run-789');

console.log({
status: run.status,
currentNode: run.currentNode,
completedNodes: run.completedNodes,
output: run.output
});

Cancel Workflow

await client.workflows.cancelRun('run-789');

Workflow Templates

Use pre-built templates for common patterns:

// List available templates
const templates = await client.workflows.listTemplates();

// Get template details
const template = await client.workflows.getTemplate('template-123');

// Create workflow from template
const workflow = await client.workflows.createFromTemplate({
templateId: 'template-123',
name: 'My Lead Processor',
customizations: {
// Override template defaults
'qualify.agentId': 'my-qualification-agent'
}
});

Validation

Validate workflow structure before saving:

const validation = await client.workflows.validate({
nodes: [...],
edges: [...]
});

if (!validation.valid) {
console.log(validation.errors);
// [
// "Missing start node",
// "Node 'agent-1' has no outgoing edges",
// "Orphaned node: 'transform-2'"
// ]
}

Validation Rules

  • Exactly one start node required
  • At least one end node required
  • No orphaned nodes (all nodes must be connected)
  • All edges must reference valid source/target nodes
  • No circular dependencies (except for explicit loops)

Workflow API Reference

List Workflows

const workflows = await client.workflows.list({
appId: 'app-123',
teamId: 'team-456',
limit: 50
});

Update Workflow

await client.workflows.update('workflow-123', {
name: 'Updated Name',
nodes: [...],
edges: [...]
});

Delete Workflow

await client.workflows.delete('workflow-123');

List Runs

const runs = await client.workflows.listRuns('workflow-123', {
status: 'completed',
limit: 100
});

Example: Order Processing Workflow

const orderWorkflow = await client.workflows.create({
name: 'Order Processing',
description: 'Complete order fulfillment workflow',
nodes: [
{ id: 'start', type: 'start' },

// Validate order
{
id: 'validate',
type: 'agent',
agentId: 'validation-agent',
inputMapping: { order: '$.input.order' }
},

// Check inventory (parallel with fraud check)
{ id: 'parallel-checks', type: 'parallel', branches: ['inventory', 'fraud'] },

{
id: 'inventory',
type: 'agent',
agentId: 'inventory-agent',
inputMapping: { items: '$.input.order.items' }
},

{
id: 'fraud',
type: 'agent',
agentId: 'fraud-detection-agent',
inputMapping: {
customer: '$.input.order.customer',
amount: '$.input.order.total'
}
},

{ id: 'merge-checks', type: 'merge', strategy: 'all' },

// Decision based on checks
{
id: 'approve-decision',
type: 'condition',
conditions: [
{
id: 'needs-review',
expression: '$.fraud.output.riskScore > 70',
target: 'manual-review'
},
{
id: 'out-of-stock',
expression: '!$.inventory.output.available',
target: 'backorder'
},
{
id: 'approved',
expression: 'true',
target: 'process-payment'
}
]
},

// Manual review path
{
id: 'manual-review',
type: 'human',
prompt: 'High-risk order requires manual review',
approvers: ['fraud-team@company.com'],
inputFields: [{ name: 'approved', type: 'boolean', required: true }]
},

// Payment processing
{
id: 'process-payment',
type: 'agent',
agentId: 'payment-agent'
},

// Fulfillment
{
id: 'fulfill',
type: 'agent',
agentId: 'fulfillment-agent'
},

// Notification
{
id: 'notify',
type: 'agent',
agentId: 'notification-agent'
},

// End states
{ id: 'backorder', type: 'end' },
{ id: 'end-success', type: 'end' },
{ id: 'end-rejected', type: 'end' }
],
edges: [
{ id: 'e1', source: 'start', target: 'validate' },
{ id: 'e2', source: 'validate', target: 'parallel-checks' },
{ id: 'e3', source: 'inventory', target: 'merge-checks' },
{ id: 'e4', source: 'fraud', target: 'merge-checks' },
{ id: 'e5', source: 'merge-checks', target: 'approve-decision' },
{ id: 'e6', source: 'manual-review', target: 'process-payment' },
{ id: 'e7', source: 'process-payment', target: 'fulfill' },
{ id: 'e8', source: 'fulfill', target: 'notify' },
{ id: 'e9', source: 'notify', target: 'end-success' }
],
settings: {
maxExecutionTimeMs: 1800000, // 30 minutes
maxNodeRetries: 3
}
});

Next: Learn about Human-in-the-Loop for human approval workflows.