Workflows
The workflow engine gives your bot deterministic automation. Instead of letting the LLM decide everything, you design a directed graph of nodes and edges that define exactly how each message is processed. The engine executes the graph step by step, following your branching logic, calling the tools you specify, and sending the messages you define.
When to Use Workflows vs Agent Loop
| Scenario | Agent Loop | Workflow |
|---|---|---|
| Open-ended conversation and Q&A | Best choice | Overkill |
| Predictable multi-step processes (refunds, onboarding) | Unreliable | Best choice |
| Conditional routing and triage | Possible but fragile | Best choice |
| Guaranteed tool execution order | Not possible | Best choice |
| Identity verification before data access | Not available | auth_gate node |
| Human approval for high-stakes actions | Not available | human_handoff action |
| Delegating to specialist bots | Not available | delegate_to_bot node |
| Maximum flexibility for unexpected questions | Best choice | Limited |
Rule of thumb: If you can draw the flow on a whiteboard with boxes and arrows, use a workflow. If the user might ask anything and you want the LLM to figure it out, use agent loop.
How Workflows Work
Workflows are JSON graphs stored in the bot’s workflow_json field. Each workflow has:
- Nodes — individual processing steps (14 types available)
- Edges — connections between nodes that define execution order
When workflow_enabled is set to 1, incoming messages are processed through the workflow graph instead of the agent loop.
Execution Model
- Entry point: The engine finds all
triggernodes and starts execution from them - BFS traversal: Nodes are executed in breadth-first order following edges
- Variable passing: Each node can read from and write to a shared
variablescontext - Branching: Logic nodes control which edges are followed via
sourceHandlevalues - Safety limits: Maximum 50 nodes per execution, with cycle detection
Workflow JSON Structure
{
"nodes": [
{
"id": "node_1",
"type": "trigger",
"data": { "triggerType": "message_received", "label": "On Message" },
"position": { "x": 0, "y": 0 }
},
{
"id": "node_2",
"type": "llm_call",
"data": {
"prompt": "Classify the user's intent: {{message}}",
"outputVariable": "intent",
"label": "Classify Intent"
},
"position": { "x": 200, "y": 0 }
},
{
"id": "node_3",
"type": "send_message",
"data": {
"message": "Your intent was classified as: {{intent.text}}",
"label": "Respond"
},
"position": { "x": 400, "y": 0 }
}
],
"edges": [
{ "id": "e1", "source": "node_1", "target": "node_2" },
{ "id": "e2", "source": "node_2", "target": "node_3" }
]
}All 14 Node Types
| Node Type | Purpose | Key Capability |
|---|---|---|
trigger | Entry point | Sets message, user_id, platform variables |
llm_call | Call the LLM | Variable interpolation in prompts |
logic | if/else, switch, while | Deterministic branching |
mcp_tool | Call workspace tools | Any MCP, Skill, or Function |
send_message | Reply to user | Template interpolation |
action | Side effects | set_variable, end_conversation, human_handoff, create_ticket |
loop | Iteration | for_each, count, while (max 50 iterations) |
code_block | Run JavaScript | Sandboxed execution with variable access |
auth_gate | Identity verification | OTP, magic link, 4 providers + custom HTTP |
schedule_message | Delayed messages | Send at a future time |
send_proactive | Cross-channel messages | Send to a different channel or user |
delegate_to_bot | Bot-to-bot routing | Call another bot (max 3 hops) |
error_handler | Graceful fallback | Catch and handle errors |
parallel | Fan-out execution | Multiple paths in parallel |
See Node Types for complete configuration details and examples for each type.
Enabling Workflows
Via Dashboard
- Navigate to your bot’s detail page
- Open the Workflow tab
- Use the visual builder to add and connect nodes
- Toggle Enable Workflow to activate
Via API
curl -X PATCH https://api.aerostack.dev/api/bots/YOUR_BOT_ID \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workflow_json": "{\"nodes\":[...],\"edges\":[...]}",
"workflow_enabled": 1
}'When a workflow is enabled, the bot uses the workflow engine exclusively. The agent loop is bypassed entirely. Disable the workflow to return to agent loop mode.
Validation
When you update workflow_json, Aerostack validates the structure:
- The JSON must parse successfully
- It must contain
nodes(array) andedges(array) - Invalid JSON returns a
400error
Runtime validations:
- A workflow with no
triggernode returns “No trigger node in workflow” - A workflow with no nodes returns “Workflow is empty”
- Cycles are detected and prevented (max 50 node executions)
Workflow Runs
Every workflow execution is recorded:
| Field | Description |
|---|---|
status | running, completed, or error |
nodes_executed | How many nodes were executed |
nodes_total | Total nodes in the workflow |
execution_log | Details of each node’s execution and timing |
error_message | Error details if the workflow failed |
duration_ms | Total execution time |
Use the Testing & Debugging page to learn how to inspect workflow runs.
Combining AI and Deterministic Logic
Workflows and agent loop are mutually exclusive per message. But llm_call nodes within a workflow still use the bot’s configured LLM — you get AI reasoning at specific points while maintaining deterministic flow control.
Example pattern:
triggeron messagellm_callto classify intent (AI decides the category)logicto branch (deterministic routing)mcp_toolto fetch data (guaranteed execution)llm_callto generate a response using the data (AI writes the reply)send_messageto the user
This gives you the best of both: structured flow with AI-powered decision-making where it matters.
Next Steps
- Node Types — All 14 node types with configuration and examples
- Variables & Conditions — Variable interpolation and condition syntax
- Workflow Recipes — 6 complete workflow examples
- Human Handoffs — Dedicated guide to the human_handoff action
- Bot Teams — Dedicated guide to delegate_to_bot