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 Type | Purpose |
|---|---|
start | Entry point (exactly one required) |
agent | Execute an agent |
condition | Branch based on conditions |
parallel | Execute branches concurrently |
merge | Join parallel branches |
transform | Transform data |
delay | Pause execution |
human | Wait for human input/approval |
end | Exit 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
}
| Strategy | Behavior |
|---|---|
all | Wait for all branches to complete |
any | Continue when any branch completes |
race | Continue 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
startnode required - At least one
endnode 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.