Overview
The Automations module provides a visual workflow automation engine with a node-based editor. Users create automations composed of trigger nodes, condition nodes, and action nodes connected by edges. Automations can be triggered by platform events (e.g., a new follower triggers a chat message) or manually. The module includes an execution engine that walks the node graph, evaluates conditions, executes actions via a dispatcher, and supports template variables. Execution history tracks completed steps for debugging.
Architecture
Backend
- GraphQL (
apps/api/src/graphql/automations.rs) -- Full CRUD for automations, nodes, and edges. Manual execution mutation that builds and runs the execution graph.
- Database (
apps/api/src/db/automations.rs) -- PostgreSQL operations for automations, automation_nodes, and automation_edges tables. Supports bulk replace for nodes and edges.
- Execution Engine (
crates/lo-automation/src/) -- ActionExecutor walks the execution graph from a trigger node, evaluates conditions, and dispatches actions. TemplateContext provides variable interpolation. NodeType enum defines available node types (triggers, conditions, actions).
- Dispatcher (
apps/api/src/dispatch/) -- RedisActionDispatcher sends action payloads via Redis pub/sub for real-time execution.
Frontend
- Node-based visual editor (React Flow or similar) for creating and arranging nodes.
- Edge connections define the execution flow between nodes.
- Manual execution button triggers the automation from the UI.
API
GraphQL Queries
| Query | Permission | Description |
|---|
automations | automations:read | List all automations for the account |
automation(id: UUID) | automations:read | Get a single automation with all its nodes and edges |
GraphQL Mutations
| Mutation | Permission | Description |
|---|
createAutomation(input: CreateAutomationInput) | automations:write | Create a new automation with name and description |
updateAutomation(input: UpdateAutomationInput) | automations:write | Update automation name, description, or enabled state |
deleteAutomation(id: UUID) | automations:write | Delete an automation and all its nodes/edges |
saveAutomationNodes(automationId: UUID, nodes: [AutomationNodeInput]) | automations:write | Bulk replace all nodes for an automation |
saveAutomationEdges(automationId: UUID, edges: [AutomationEdgeInput]) | automations:write | Bulk replace all edges for an automation |
executeAutomation(id: UUID) | automations:execute | Manually execute an automation. Finds the manual_trigger node (or first trigger), builds the execution graph, and runs it. |
AutomationNodeInput:
| Field | Type | Description |
|---|
id | UUID | Node ID (client-generated) |
nodeType | String | Node type (trigger, condition, action) |
positionX | f64 | X position in the editor canvas |
positionY | f64 | Y position in the editor canvas |
config | JSON | Node-specific configuration (default: {}) |
AutomationEdgeInput:
| Field | Type | Description |
|---|
id | UUID | Edge ID (client-generated) |
sourceNodeId | UUID | Source node ID |
targetNodeId | UUID | Target node ID |
sourceHandle | String | Source handle name (default: "output") |
targetHandle | String | Target handle name (default: "input") |
REST Endpoints
Mirror the GraphQL surface. All paths live under /v1/automations.
| Method | Path | Permission | Description |
|---|
GET | /v1/automations | automations:read | List automations |
POST | /v1/automations | automations:write | Create an automation |
GET | /v1/automations/{id} | automations:read | Get an automation (with nodes and edges) |
PATCH | /v1/automations/{id} | automations:write | Update name/description/enabled |
DELETE | /v1/automations/{id} | automations:write | Delete automation + nodes + edges |
PUT | /v1/automations/{id}/nodes | automations:write | Bulk replace node list |
PUT | /v1/automations/{id}/edges | automations:write | Bulk replace edge list |
POST | /v1/automations/{id}/execute | automations:execute | Run the automation manually |
Request bodies are snake_case copies of the GraphQL CreateAutomationInput / UpdateAutomationInput / AutomationNodeInput / AutomationEdgeInput.
Permissions
| Permission | Description |
|---|
automations:read | View automations and their configuration |
automations:write | Create, edit, and delete automations |
automations:execute | Manually trigger and start/stop automations |
automations:history | View automation execution history and debug logs |
Database
| Table | Database | Description |
|---|
automations | PostgreSQL | id, account_id, name, description, enabled, created_at, updated_at |
automation_nodes | PostgreSQL | id, automation_id (FK), node_type, position_x, position_y, config (JSONB), created_at |
automation_edges | PostgreSQL | id, automation_id (FK), source_node_id, target_node_id, source_handle, target_handle, created_at |
Data Flow
- User creates an automation and adds nodes (triggers, conditions, actions) via the visual editor.
- Nodes are connected with edges that define execution flow.
- Nodes and edges are saved via bulk-replace mutations (
saveAutomationNodes, saveAutomationEdges).
- When a platform event matches a trigger node, or the user clicks "Execute":
- The execution graph is built from nodes and edges.
- The executor starts at the trigger node and walks the graph.
- Conditions are evaluated; actions are dispatched via
RedisActionDispatcher.
- Each step is recorded for the execution result.
ExecutionResultGql returns whether the execution completed and how many steps ran.
Key Files
| Path | Description |
|---|
apps/api/src/graphql/automations.rs | GraphQL queries and mutations |
apps/api/src/db/automations.rs | Database CRUD for automations, nodes, edges |
crates/lo-automation/src/ | Execution engine, template context, node types |
apps/api/src/dispatch/ | RedisActionDispatcher for action execution |
apps/api/src/workers/automation.rs | build_execution_graph helper |