Workflow YAML Syntax
Understand Hexabot workflow YAML structure, execution rules, and authoring patterns for tasks, flow, and outputs.
Workflow logic is stored as YAML on workflow versions and derives a compiled runtime graph when a workflow run starts or resumes.
Use this page when you are writing editor code, debugging validation, generating YAML, or adding new workflow DSL features. For a concise user-facing reference, see Workflow YAML Reference.
Complete Shape
A workflow definition is a YAML object with optional input/context/default sections and required defs, flow, and outputs sections:
defaults:
settings:
timeout_ms: 0
retries:
enabled: false
max_attempts: 3
backoff_ms: 25
max_delay_ms: 10000
jitter: 0
multiplier: 1
defs:
send_reply:
kind: task
description: Send a response.
action: send_text_message
inputs:
text: "='You said: ' & $input.text"
flow:
- do: send_reply
outputs:
sent: "=$output.send_reply.sent"defaults
No
Workflow-level execution settings merged into every task's settings.
defs
Yes
Registry of task definitions and binding definitions. It may be {} in a blank draft.
flow
Yes
Ordered array of executable steps and operators. It may be [] in a blank draft.
outputs
Yes
Final output mapping evaluated after the flow finishes. It may be {} when the workflow does not expose a result.
Values and Expressions
Any string that starts with = is compiled as a JSONata expression. Other strings are literals.
Expression scopes are exposed as JSONata variables:
$input
Task inputs, operator expressions, final outputs
Validated run input payload.
$context
Task inputs, operator expressions, final outputs
Runtime context state, including workflow/run IDs, initiator data, trigger context, and mounted memory values.
$output
Task inputs after earlier steps, operator expressions, final outputs
Raw action results keyed by task ID, plus named loop accumulator outputs.
$iteration
Loop body expressions and loop accumulator expressions
Current loop item and zero-based index.
$accumulator
Loop body expressions and loop accumulator expressions
Current accumulator value when accumulate is configured.
Hexabot registers the API helper function $t(string) for localized JSONata strings during workflow execution.
Expression-aware fields are not recursively compiled. For task inputs, only each top-level input value is compiled. If an action input needs a dynamic object, make the whole top-level field an expression that returns the object.
Inputs
The YAML inputs.schema section belongs to the agentic runner. It supports a compact schema shape:
Supported field types are string, number, integer, boolean, array, and object. Array fields must declare items; only array fields may declare items. Object fields may declare properties; only object fields may declare properties. Input fields are optional at runtime, including nested object properties.
In the Hexabot API, manual workflow trigger forms use the workflow entity's inputSchema, which is stored outside the YAML definition. Conversational and scheduled workflows receive fixed input schemas from the API.
Defaults and Settings
defaults.settings is deep-merged into every task's settings. Task-level settings override defaults, and undefined values do not erase default values.
Shared execution settings are:
timeout_ms
0
Maximum action runtime in milliseconds. 0 disables the timeout wrapper.
retries.enabled
false
Enables retry attempts for failing actions.
retries.max_attempts
3
Total attempts before the action fails.
retries.backoff_ms
25
Initial retry delay.
retries.max_delay_ms
10000
Maximum retry delay.
retries.jitter
0
Randomization factor applied to retry delays.
retries.multiplier
1
Backoff multiplier after each retry.
Action-specific settings share the same settings object. The editor splits shared execution settings from action settings when it validates against schemas.
Definitions
All reusable workflow definitions live under defs.
Task Definitions
A task definition is executable and must use kind: task:
kind
Yes
Must be task.
description
No
Human-readable note for the editor and reviews.
action
Yes
Registered action name.
inputs
No
Top-level action input values. Each value may be a literal or expression.
settings
No
Shared execution settings plus action-specific settings.
bindings
No
References to non-task definitions grouped by binding kind.
Task IDs should use snake_case with at least one underscore because the agentic compiler enforces that naming rule when actions are bound. Task output is always stored raw under $output.<task_id>; task-level output mapping is not supported.
Binding Definitions
Binding definitions are non-task defs entries. Their kind must match a registered runtime binding kind, and they must declare settings.
Built-in AI binding kinds include:
model
No
Action forbidden
Mount one language model provider and model ID.
memory
Yes
Action forbidden
Mount one or more memory definitions by definition_id.
mcp
Yes
Action forbidden
Mount MCP server tools by server_id and optional tool_names.
tools
Yes
Action required
Expose action-backed tools to AI actions. Tool bindings can themselves mount tools, model, or memory.
Binding references must match the registered cardinality:
Binding validation checks unknown kinds, unknown references, kind mismatches, duplicate references, action policy, unsupported nested bindings, binding settings schemas, and circular binding references.
Flow Steps
flow is an ordered array. Each item must be exactly one step shape: do, conditional, parallel, or loop.
Task Step
do references a task definition name, not an action name. The referenced defs.<name> must exist and must have kind: task.
Conditional Step
Branches are evaluated in order. The first truthy condition wins. An else branch is represented as a branch without condition, commonly written with else: true. If no branch matches and no else exists, the conditional does nothing and nested branch steps are marked skipped for tracing.
Parallel Step
strategy may be wait_all or wait_any; omitted strategy defaults to wait_all. The current in-process runner executes child steps sequentially for deterministic behavior. With wait_any, it stops after the first child completes and marks the remaining child steps skipped, so downstream expressions must check which outputs exist.
For Each Loop
for_each.in must be an expression that evaluates to an array. Non-array results are treated as an empty array. max_concurrency is a positive integer metadata hint; the runner does not enforce real concurrency today. until is checked after each iteration and stops the loop early when truthy.
While Loop
while is evaluated before each iteration. The body does not run when the expression is false at the start of an iteration.
Accumulators
Both loop types support accumulate:
The merge expression receives $input, $context, $output, $iteration, and $accumulator. If the loop has both name and accumulate, the final value is exposed as $output.<loop_name>.<accumulate.as>.
Outputs
outputs is a required map. Each value must be a JSONata expression string.
Outputs are evaluated only after the whole flow finishes. A suspended or failed workflow persists the intermediate execution state, but final outputs are not evaluated until completion.
Authoring Checklist
Include
defs,flow, andoutputs, even when they are empty in a draft.Use snake_case task IDs such as
send_initial_reply.Keep
flowentries as one-key step objects.Reference task IDs with
do; reference action names only inside task or action-backed binding definitions.Quote expressions, especially those containing
:,{},[], comparison operators, or string literals.Keep dynamic task inputs at the top input-field level, or return a whole object from one expression.
Check optional outputs with
$exists(...)when reading values from conditionals,wait_any, or skipped paths.Give accumulator loops a
namewhen later expressions need the accumulated value.
Last updated
Was this helpful?