Appearance
Visual Workflows
Build complex automation flows with a drag-and-drop workflow editor.
Overview
The workflow editor provides:
- 30+ node types for various operations
- Drag-and-drop interface
- Real-time preview of workflow structure
- Execution tracking and history
- Error handling and retries
Creating Workflows
Via Dashboard
- Navigate to Workflows in the sidebar
- Click New Workflow
- Drag nodes from the palette
- Connect nodes by dragging from output to input handles
- Configure each node's properties
- Save and activate the workflow
Workflow Canvas
The canvas provides:
- Zoom and pan controls
- Mini-map for navigation
- Grid snapping
- Undo/redo
- Copy/paste nodes
Node Categories
Triggers
| Node | Description |
|---|---|
trigger | Start point for workflow |
webhook | HTTP webhook trigger |
schedule | Time-based trigger |
Logic
| Node | Description |
|---|---|
if-else | Conditional branching |
switch | Multi-way branching on a value |
decision | Model-judged branching — see Decision node |
approval | Approval Gate: ask a human over a gateway, wait durably, route approved / rejected — see Approval Gate |
await-event | Pause until an external event arrives (or time out) — see Await Event |
loop | Iterate over arrays |
wait | Delay execution |
parallel | Run branches concurrently |
HTTP & API
| Node | Description |
|---|---|
http-request | Make HTTP requests |
webhook-response | Respond to webhooks |
graphql | GraphQL queries |
Communication
| Node | Description |
|---|---|
email | Send emails |
telegram | Telegram messages |
slack | Slack messages |
discord | Discord messages |
whatsapp | WhatsApp messages |
sms | SMS messages |
Social Media
| Node | Description |
|---|---|
post-linkedin | Post to LinkedIn via UGC Posts API |
post-x | Post to X (Twitter) |
post-instagram | Post to Instagram |
post-youtube | Upload to YouTube |
post-tiktok | Upload to TikTok |
Browser Automation
| Node | Description |
|---|---|
navigate | Go to URL |
click | Click element |
type | Enter text |
extract | Extract content |
screenshot | Capture screenshot |
scroll | Scroll page |
AI & LLM
| Node | Description |
|---|---|
chat | AI conversation |
prompt | Single prompt completion |
tool-call | Invoke agent tools |
embeddings | Generate embeddings |
ai-agent | Delegate a task to an agent and get a typed result back — see AI Agent node |
Data Operations
| Node | Description |
|---|---|
database-query | SQL queries |
transform | Data transformation |
filter | Filter arrays |
aggregate | Aggregate data |
Utilities
| Node | Description |
|---|---|
code | Execute JavaScript |
variable | Set/get variables |
log | Log messages |
error | Throw errors |
Decision node
The Decision logic node branches on a judgement rather than a value. Its outputs are the node's outflow labels; each label gets a short description, and the Jev typed-decision model picks one of them from the node's state (the trigger payload or a template).
Acceptance rules (resolveDecision() in agent/jev.ts, emitted verbatim into generated Worker code so interpreter and deployed workflows agree):
- the chosen option must reach the threshold (default 0.6), and
- it must beat the runner-up by the margin (default 0.15).
Otherwise the node takes its default output — the one labelled default (or *); when none is, the last output is the silent fallback and the node panel warns which one that is. Any error or timeout also routes to the default. Inside a Workflow the Jev call runs within step.do with two retries, so a cold start is retried before falling back; the timeout is 10 s there (4 s in the interpreter).
Approval Gate
The Approval Gate logic node pauses a workflow until a human says yes or no. Configuration: the question, the gateway to send it on, the recipient, a timeout in seconds (default 3600), and what to do on timeout (reject by default).
How it runs:
- The step calls
POST /api/agents/{agentId}/workflows/approvals/requeston the agent (AGENT_BASE_URL+AGENT_API_KEY), which sends the question with approve / reject links over the chosen gateway. The links are publicworkflow.uranus.im/{slug}/events/{name}?executionId=…URLs, so a tap in WhatsApp or Telegram answers. - The generated step waits durably with
step.waitForEvent(after a D1 pre-check for an answer that already arrived); the interpreter polls for up to 45 s. - The answer routes to the
approvedorrejectedoutput; a timeout follows the configured policy.
Await Event
Await Event pauses until a named event is delivered for this execution, or times out. Deployed workflows wait on step.waitForEvent(type, timeout) with a D1 pre-check before and a final check on timeout, so an event that arrives while the Worker was between steps is not lost. Nodes wired to the received output only run when the event arrived, not on timeout.
Events reach a running instance two ways, both of which also write the workflow_events row that older deployments poll:
| Route | Who calls it |
|---|---|
POST /api/agents/{agentId}/workflows/events/{eventName} { executionId, data } (or GET …?executionId=…&k=v for clickable links) | external systems, the agent's add_workflow_event tool |
POST https://<workflow-worker>/events/{instanceId} { type, payload } | the agent, forwarding to the instance's sendEvent |
AI Agent node
AI Agent delegates a task to an agent — the workflow's own agent or another one — and returns its reply. It takes a task template, a context object, and an optional output schema (JSON Schema). With a schema the agent returns data.structured, the step checks the required keys are present, and a mismatch fails the step so Workflows retries it; the result then carries both response and structured. In the interpreter the node really runs (the agent's own LLM for self-delegation, the Durable Object binding for other agents) rather than returning a placeholder.
Deployed workflows call back into the agent with AGENT_BASE_URL and AGENT_API_KEY, which the deployer injects (see Supporting Services).
Node Configuration
Each node has configurable properties accessed in the side panel.
Example: HTTP Request
json
{
"type": "http-request",
"data": {
"method": "POST",
"url": "https://api.example.com/data",
"headers": {
"Authorization": "Bearer {{secrets.api_key}}"
},
"body": {
"message": "{{previous.output}}"
}
}
}Variables & Expressions
Syntax
Use double curly braces for dynamic values:
{{variable.path}}
{{previous.output}}
{{context.user.email}}
{{secrets.api_key}}Available Contexts
| Context | Description |
|---|---|
previous | Output from previous node |
trigger | Trigger node data |
context | Workflow context |
secrets | Stored secrets |
env | Environment variables |
Execution
Manual Trigger
Click the Run button to execute immediately.
Scheduled Trigger
Link to a schedule for automatic execution.
Webhook Trigger
Create a webhook URL for external triggers.
Execution Status
| Status | Description |
|---|---|
queued | Waiting to start |
running | Currently executing |
complete | Finished successfully |
errored | Failed with error |
terminated | Manually stopped |
Error Handling
Retry Configuration
json
{
"retry": {
"attempts": 3,
"delay": 60,
"backoff": "exponential"
}
}Error Handlers
Add error handler nodes to catch and handle failures.
Best Practices
1. Start Simple
Begin with 2-3 nodes and add complexity gradually.
2. Use Descriptive Names
Name nodes clearly: fetch-user-data not node-1.
3. Handle Errors
Always include error handling nodes.
4. Test Thoroughly
Test each execution path before going live.
5. Monitor Executions
Review execution history regularly.
Agent integration
Workflows aren't just user-built artifacts that the agent invokes. They participate in the agent's reasoning loop: linked to goals, recalled from memory, proposed by reflection, discovered semantically.
Goal linkage
A workflow can be attributed to one or more agent goals via the goal_workflows table. When execute_workflow(workflow_id, ..., goal_id=...) runs, two things happen:
- The link is auto-created on first run (
INSERT OR IGNORE INTO goal_workflows). - On completion the goal's
progress_notesget a new line ([YYYY-MM-DD] workflow_name: completed|failed).
list_goals returns linked workflow names per goal; list_workflows returns linked goal descriptions per workflow. The system prompt's "Active Goals" section shows linked workflows inline so the agent doesn't re-derive the mapping each turn.
Role values in goal_workflows:
| Role | Meaning |
|---|---|
achieves | Completing this workflow completes the goal |
advances | Each run pushes the goal forward (default) |
evaluates | Workflow checks goal status or produces evidence |
Outcomes → memory
Every workflow execution (success or failure) gets summarized via summarizeWorkflowOutcome (background, glm-4.7-flash) and written to a memory key:
memory:workflow:{YYYY-MM-DD}:{slug}:{success|failure}The summary is embedded into Vectorize so the next time the agent considers running a similar workflow, prior outcomes surface via semantic recall. Failures are stored at higher confidence (0.7) than successes (0.5) — failure modes outrank successes when the model is deciding what to try.
Throttled to one summary per workflow per 60s to prevent spam from rapidly-cycling workflows.
Reflection proposals
Reflection can propose new workflows when it notices recurring patterns. Output schema:
json
{
"proposed_workflows": [
{
"description": "Auto-tag incoming receipts",
"trigger_kind": "gateway_event",
"outline": "Step 1: detect attachment...\nStep 2: ..."
}
]
}These insert as rows with status='draft' and source='reflection'. They appear in the existing Workflows UI tab (filterable by status). The agent never auto-activates — activate_draft_workflow requires user approval.
Semantic discovery
find_workflow_for_goal(goal) queries Vectorize over workflow embeddings instead of doing an LLM match. Each workflow is embedded with its description + node-type summary into the shared vector index (tagged metadata.type = "workflow"). On a goal query:
- Embed the goal text.
- Vectorize query, top-5, score ≥ 0.55.
- Return the best match with confidence (
high≥ 0.75,medium≥ 0.65, otherwiselow) plus up to two alternates. - Fall back to the prior LLM matcher if no semantic hits.
When workflows get embedded:
createWorkflowLLM tool — embeds on create (ctx.waitUntil)activateDraftWorkflow— embeds when draft → active- Editor save (
PUT /workflows/{id}) — re-embeds when name/description/nodes change - Daily
reindex-workflowscron at 05:00 UTC — catches any drift; hash-gated so unchanged workflows are skipped
Goal context in workflow execution
When a run is attributed to a goal, the workflow trigger payload includes a goal object:
js
{
trigger: "chat",
input: {
...conversationContext,
goal: {
id: "abc-123",
description: "Send weekly summary to Acme",
progress_notes: "..."
},
...userInputs
}
}Workflow nodes can reference this via the existing interpolation syntax:
{{trigger.goal.description}} → "Send weekly summary to Acme"
{{trigger.goal.progress_notes}} → "...accumulating log..."No codegen change was needed — the interpolation engine resolves dotted paths against ctx.input.
Run stats
list_workflows returns 30-day rollup stats per workflow so the LLM picking between candidates sees reliability data:
json
{
"id": "...",
"name": "Daily standup",
"stats_30d": {
"runs_total": 30,
"runs_failed": 2,
"failure_rate": 0.07,
"avg_duration_ms": 8421,
"last_run_at": "2026-05-14T08:00:00Z"
}
}Reflection's gatherSignals also surfaces a per-workflow rollup so the LLM can say "workflow X failed 12/15 times this week" instead of just "12 failures."
Callbacks from workflows to the agent
Deployed workflows can call back into the agent for memory, messaging, contact lookup, and event emission. See Workflow → Agent RPC for the HMAC envelope, whitelisted methods, and rate limits.
Schema
| Table | Purpose |
|---|---|
workflows | Workflow definitions (nodes/edges JSON), now with source and callback_secret columns |
workflow_executions | Per-run state + outputs + duration |
workflow_events | Async event stream for await-event nodes |
goal_workflows | Many-to-many linkage to agent_goals |
workflow_embeddings | Vectorize ID registry with content hash |
API Reference
See Workflows API for complete endpoint documentation.
