terminalWorkflow 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"
Section
Required
Purpose

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 JSONataarrow-up-right expression. Other strings are literals.

Expression scopes are exposed as JSONata variables:

Scope
Available in
Meaning

$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:

Setting
Default
Meaning

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:

Field
Required
Meaning

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:

Kind
Multiple
Action policy
Typical use

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

  1. Include defs, flow, and outputs, even when they are empty in a draft.

  2. Use snake_case task IDs such as send_initial_reply.

  3. Keep flow entries as one-key step objects.

  4. Reference task IDs with do; reference action names only inside task or action-backed binding definitions.

  5. Quote expressions, especially those containing :, {}, [], comparison operators, or string literals.

  6. Keep dynamic task inputs at the top input-field level, or return a whole object from one expression.

  7. Check optional outputs with $exists(...) when reading values from conditionals, wait_any, or skipped paths.

  8. Give accumulator loops a name when later expressions need the accumulated value.

Last updated

Was this helpful?