Workflows
Build visual AI workflows with drag-and-drop nodes, agent orchestration, conditional logic, and automated triggers.
Overview
Workflows enable you to create complex AI automation pipelines using a visual node-based editor. Features include:
- Visual workflow builder with React Flow
- Multiple trigger types (manual, webhook, cron, event)
- Agent nodes for AI processing
- Conditional branching and loops
- Data transformation nodes
- Integration with all Tenzro services
Quick Start
import { Tenzro } from 'tenzro';
const tenzro = new Tenzro({ apiKey: 'your-api-key' });
const workflow = await tenzro.workflows.create({
projectId: 'project-id',
workflowName: 'content-pipeline',
triggerType: 'webhook',
nodes: [
{
id: 'input',
type: 'trigger',
position: { x: 100, y: 100 },
data: { label: 'Webhook Trigger' },
},
{
id: 'agent',
type: 'agent',
position: { x: 300, y: 100 },
data: {
label: 'Content Agent',
agentId: 'your-agent-id',
},
},
{
id: 'output',
type: 'output',
position: { x: 500, y: 100 },
data: { label: 'Output' },
},
],
edges: [
{ id: 'e1', source: 'input', target: 'agent' },
{ id: 'e2', source: 'agent', target: 'output' },
],
});
await tenzro.workflows.publish(workflow.workflow_id);
const execution = await tenzro.workflows.execute(workflow.workflow_id, {
inputData: { topic: 'AI trends' },
});
console.log(execution.status);
console.log(execution.output_data);
Node Types
Trigger Nodes
| Type | Description |
|---|
manual | Manually triggered via API or console |
webhook | Triggered by HTTP POST to webhook URL |
cron | Scheduled execution on cron schedule |
event | Triggered by system events |
Processing Nodes
| Node | Description |
|---|
agent | Execute an AI agent with input data |
inference | Direct LLM inference without agent |
embed | Generate embeddings from text |
transform | Transform data with JavaScript |
conditional | Branch based on conditions |
loop | Iterate over arrays |
Data Nodes
| Node | Description |
|---|
vec-search | Search vector database |
vec-insert | Insert into vector database |
data-query | Execute SQL query |
kev-get | Get from key-value store |
kev-set | Set in key-value store |
http | Make HTTP request |
SDK Reference
Create Workflow
const workflow = await tenzro.workflows.create({
projectId: string,
workflowName: string,
workflowDescription?: string,
nodes?: WorkflowNode[],
edges?: WorkflowEdge[],
viewport?: { x: number, y: number, zoom: number },
variables?: Record<string, any>,
triggerType?: 'manual' | 'webhook' | 'cron' | 'event',
triggerConfig?: Record<string, any>,
});
Lifecycle Management
await tenzro.workflows.publish(workflowId);
await tenzro.workflows.pause(workflowId);
await tenzro.workflows.update(workflowId, {
nodes: updatedNodes,
edges: updatedEdges,
});
await tenzro.workflows.delete(workflowId);
Execution
const execution = await tenzro.workflows.execute(workflowId, {
inputData?: Record<string, any>,
triggerType?: TriggerType,
triggerData?: Record<string, any>,
});
const result = await tenzro.workflows.trigger(workflowId, data);
const execution = await tenzro.workflows.getExecution(workflowId, executionId);
const { nodeExecutions } = await tenzro.workflows.getNodeExecutions(
workflowId,
executionId
);
await tenzro.workflows.cancelExecution(workflowId, executionId);
Templates
const { data: templates } = await tenzro.workflows.getTemplates('automation');
const workflow = await tenzro.workflows.createFromTemplate({
templateId: 'content-pipeline',
projectId: 'project-id',
workflowName: 'My Content Pipeline',
});
Statistics
const { stats } = await tenzro.workflows.getStats(workflowId);
interface WorkflowStats {
totalExecutions: number;
successfulExecutions: number;
failedExecutions: number;
successRate: string;
avgExecutionTimeMs: number;
totalCostMicrodollars: number;
totalCostDollars: string;
lastExecutedAt?: string;
nodeCount: number;
edgeCount: number;
}
Building Workflows
RAG Pipeline
const workflow = await tenzro.workflows.create({
projectId: 'project-id',
workflowName: 'rag-pipeline',
triggerType: 'webhook',
nodes: [
{
id: 'trigger',
type: 'trigger',
position: { x: 100, y: 200 },
data: { label: 'User Query' },
},
{
id: 'embed',
type: 'embed',
position: { x: 300, y: 200 },
data: {
taskType: 'RETRIEVAL_QUERY',
dimensionality: 768,
},
},
{
id: 'search',
type: 'vec-search',
position: { x: 500, y: 200 },
data: {
vecDbId: 'your-vec-db-id',
topK: 5,
},
},
{
id: 'agent',
type: 'agent',
position: { x: 700, y: 200 },
data: {
agentId: 'your-agent-id',
inputMapping: {
query: '{{trigger.query}}',
context: '{{search.results}}',
},
},
},
{
id: 'output',
type: 'output',
position: { x: 900, y: 200 },
data: {},
},
],
edges: [
{ id: 'e1', source: 'trigger', target: 'embed' },
{ id: 'e2', source: 'embed', target: 'search' },
{ id: 'e3', source: 'search', target: 'agent' },
{ id: 'e4', source: 'agent', target: 'output' },
],
});
Conditional Branching
const nodes = [
{
id: 'classify',
type: 'agent',
position: { x: 300, y: 200 },
data: {
agentId: 'classifier-agent',
prompt: 'Classify this message as: support, sales, or other',
},
},
{
id: 'condition',
type: 'conditional',
position: { x: 500, y: 200 },
data: {
conditions: [
{ path: 'support', condition: "{{classify.result}} === 'support'" },
{ path: 'sales', condition: "{{classify.result}} === 'sales'" },
{ path: 'default', condition: 'true' },
],
},
},
{
id: 'support-agent',
type: 'agent',
position: { x: 700, y: 100 },
data: { agentId: 'support-agent' },
},
{
id: 'sales-agent',
type: 'agent',
position: { x: 700, y: 200 },
data: { agentId: 'sales-agent' },
},
{
id: 'general-agent',
type: 'agent',
position: { x: 700, y: 300 },
data: { agentId: 'general-agent' },
},
];
const edges = [
{ id: 'e1', source: 'trigger', target: 'classify' },
{ id: 'e2', source: 'classify', target: 'condition' },
{ id: 'e3', source: 'condition', target: 'support-agent', sourceHandle: 'support' },
{ id: 'e4', source: 'condition', target: 'sales-agent', sourceHandle: 'sales' },
{ id: 'e5', source: 'condition', target: 'general-agent', sourceHandle: 'default' },
];
Scheduled Workflow
const workflow = await tenzro.workflows.create({
projectId: 'project-id',
workflowName: 'daily-report',
triggerType: 'cron',
triggerConfig: {
schedule: '0 9 * * *',
timezone: 'America/New_York',
},
nodes: [
{
id: 'query',
type: 'data-query',
position: { x: 200, y: 200 },
data: {
dataDbId: 'analytics-db',
sql: `
SELECT date, SUM(revenue) as total
FROM orders
WHERE date = CURRENT_DATE - 1
GROUP BY date
`,
},
},
{
id: 'summarize',
type: 'agent',
position: { x: 400, y: 200 },
data: {
agentId: 'report-agent',
prompt: 'Generate a daily sales summary from this data: {{query.results}}',
},
},
{
id: 'email',
type: 'http',
position: { x: 600, y: 200 },
data: {
method: 'POST',
url: 'https://api.sendgrid.com/v3/mail/send',
headers: { Authorization: 'Bearer {{env.SENDGRID_KEY}}' },
body: {
to: 'team@company.com',
subject: 'Daily Sales Report',
content: '{{summarize.result}}',
},
},
},
],
});
Variables and Data Flow
Reference data between nodes using template syntax:
'{{trigger.field}}'
'{{nodeId.result}}'
'{{nodeId.data.field}}'
'{{variables.apiKey}}'
'{{env.SECRET_KEY}}'
'{{nodeId.results[0].text}}'
'{{trigger.type === "urgent" ? "high" : "normal"}}'
Best Practices
- Test in draft: Use manual execution to test before publishing
- Use variables: Store configuration in workflow variables, not hardcoded
- Handle errors: Add error handling nodes for critical paths
- Monitor executions: Review failed executions and optimize
- Version control: Workflows are versioned; track changes
Limits
| Resource | Limit |
|---|
| Max nodes per workflow | 100 |
| Max edges per workflow | 200 |
| Execution timeout | 30 minutes |
| Max concurrent executions | 10 |
| Cron minimum interval | 5 minutes |