Skip to content

Workflow Reference

This document describes the top-level fields and step configuration in a Mantle workflow YAML file. For connector-specific parameters, see Connectors. For AI tool use, see Tool Use. For a hands-on introduction, start with the Getting Started guide.

Complete Example

name: fetch-and-summarize
description: Fetch data from an API and summarize it with an LLM

inputs:
  url:
    type: string
    description: URL to fetch
  max_retries:
    type: number
    description: Maximum number of retries for the HTTP request

triggers:
  - type: cron
    schedule: "0 * * * *"
  - type: webhook
    path: "/hooks/fetch-and-summarize"

steps:
  - name: fetch-data
    action: http/request
    timeout: 30s
    retry:
      max_attempts: 3
      backoff: exponential
    params:
      method: GET
      url: "{{ inputs.url }}"

  - name: summarize
    action: ai/completion
    timeout: 60s
    params:
      provider: openai
      model: gpt-4o
      prompt: "Summarize this data: {{ steps.fetch-data.output.body }}"

  - name: post-result
    action: http/request
    if: "steps.summarize.output.key_points.size() > 0"
    params:
      method: POST
      url: https://hooks.example.com/results
      body:
        summary: "{{ steps.summarize.output.summary }}"

Top-Level Fields

FieldTypeRequiredDescription
namestringYesUnique identifier for the workflow. Must be kebab-case: lowercase letters, digits, and hyphens. Pattern: ^[a-z][a-z0-9-]*$.
descriptionstringNoHuman-readable description of what the workflow does.
inputsmapNoInput parameters the workflow accepts at runtime.
triggerslistNoAutomatic triggers that start the workflow. See Triggers.
stepslistYesOrdered list of steps to execute. At least one step is required.

Name Rules

The workflow name is the primary identifier used across validate, apply, plan, and run. It must:

  • Start with a lowercase letter
  • Contain only lowercase letters (a-z), digits (0-9), and hyphens (-)
  • Not start or end with a hyphen

Valid examples: fetch-data, my-workflow-v2, a1

Invalid examples: Fetch-Data, fetch_data, -fetch, 123abc

Inputs

Inputs define the parameters a workflow accepts when triggered. Each input is a key-value pair in the inputs map.

inputs:
  url:
    type: string
    description: URL to fetch
  verbose:
    type: boolean
    description: Enable verbose output
  max_items:
    type: number
    description: Maximum number of items to process

Input Fields

FieldTypeRequiredDescription
(key)stringYesInput parameter name. Must be snake_case: lowercase letters, digits, and underscores. Pattern: ^[a-z][a-z0-9_]*$.
typestringNoData type. One of: string, number, boolean.
descriptionstringNoHuman-readable description.

Input Name Rules

Input names use snake_case (underscores), not kebab-case (hyphens). This is intentional — input names appear in CEL expressions where hyphens would be interpreted as subtraction.

Valid: url, max_retries, api_key

Invalid: URL, max-retries, apiKey, 123abc

Steps

Steps are the building blocks of a workflow. Each step invokes a connector action and can optionally include conditional logic, retry policies, timeouts, and explicit dependencies. Steps without dependencies run concurrently; use depends_on to declare explicit ordering. See Parallel Execution.

steps:
  - name: fetch-data
    action: http/request
    timeout: 30s
    retry:
      max_attempts: 3
      backoff: exponential
    if: "inputs.url != ''"
    params:
      method: GET
      url: "{{ inputs.url }}"

Step Fields

FieldTypeRequiredDescription
namestringYesUnique name within the workflow. Must be kebab-case: ^[a-z][a-z0-9-]*$.
actionstringYesConnector action to invoke, in connector/action format.
paramsmapNoParameters passed to the connector action. Structure depends on the action.
ifstringNoCEL expression. The step runs only if this evaluates to true.
retryobjectNoRetry policy for this step. See Retry Policy.
timeoutstringNoMaximum duration for the step. Uses Go duration format (e.g., 30s, 5m, 1h).
credentialstringNoName of a stored credential to inject into this step. See Secrets Guide.
depends_onlist of stringsNoDeclares explicit dependencies on other steps for parallel execution. See Parallel Execution.

Step Name Rules

Step names follow the same rules as the workflow name: kebab-case, starting with a lowercase letter. Step names must be unique within a workflow — duplicate names cause a validation error.

Step names matter because you reference step outputs in CEL expressions using steps.STEP_NAME.output.

Note on hyphenated step names in CEL: When a step name contains hyphens (e.g., fetch-data), you can use dot notation in template strings ({{ steps.fetch-data.output.body }}), but in if expressions you must use bracket notation: steps['fetch-data'].output.body. This is because CEL interprets hyphens as subtraction in expression context.

Parallel Execution

By default, Mantle builds a directed acyclic graph (DAG) from your steps and runs steps concurrently when their dependencies allow it. You control ordering with depends_on and through implicit dependencies detected from CEL expressions.

How dependencies are resolved:

  • Explicit dependencies — list step names in depends_on to declare that a step must wait for those steps to complete before it can start.
  • Implicit dependencies — Mantle analyzes CEL expressions in params and if fields. If a step references steps.fetch-data.output, the engine automatically adds fetch-data as a dependency. You do not need to list implicit dependencies in depends_on.
  • Skipped steps count as resolved — if a step is skipped (its if condition evaluated to false), downstream steps that depend on it are unblocked and can proceed.

Fan-out/fan-in example:

name: fan-out-fan-in
description: Run two API calls in parallel, then merge results

steps:
  - name: fetch-users
    action: http/request
    params:
      method: GET
      url: https://api.example.com/users

  - name: fetch-orders
    action: http/request
    params:
      method: GET
      url: https://api.example.com/orders

  - name: merge-results
    action: ai/completion
    credential: openai
    depends_on:
      - fetch-users
      - fetch-orders
    params:
      model: gpt-4o
      prompt: >
        Correlate these users and orders:
        Users: {{ steps['fetch-users'].output.body }}
        Orders: {{ steps['fetch-orders'].output.body }}

In this workflow, fetch-users and fetch-orders have no dependencies on each other, so they run concurrently. The merge-results step declares both as explicit dependencies via depends_on and waits for both to complete before it starts.

Retry Policy

The retry policy controls what happens when a step fails.

retry:
  max_attempts: 3
  backoff: exponential
FieldTypeRequiredDescription
max_attemptsintegerYesMaximum number of attempts. Must be greater than 0.
backoffstringNoBackoff strategy between retries. One of: fixed, exponential.

If backoff is omitted and retry is present, the default behavior depends on the engine implementation.

Timeout

The timeout field accepts Go duration strings. These consist of a number followed by a unit suffix:

UnitSuffixExample
Millisecondsms500ms
Secondss30s
Minutesm5m
Hoursh1h

You can combine units: 1m30s means one minute and thirty seconds.

The timeout must be a positive duration. 0s and negative values are invalid.

CEL Expressions

Mantle uses CEL (Common Expression Language) for conditional logic and data access between steps. CEL expressions appear in two places:

  1. if fields — determine whether a step runs
  2. Template strings in params — reference data from inputs and previous steps using {{ expression }} syntax

Available Variables

VariableDescription
inputs.NAMEValue of the input parameter NAME.
steps.STEP_NAME.outputOutput of the step named STEP_NAME. The structure depends on the connector.
env.NAMEValue of the environment variable NAME.
trigger.payloadRequest body from a webhook trigger, parsed as JSON. Only available for webhook-triggered executions.

Expression Examples

Reference an input:

url: "{{ inputs.url }}"

Reference a previous step’s output:

prompt: "Summarize: {{ steps.fetch-data.output.body }}"

Conditional execution based on step output:

if: "steps.summarize.output.key_points.size() > 0"

Boolean logic:

if: "inputs.verbose == true && steps.fetch-data.output.status == 200"

String operations:

if: "steps.fetch-data.output.body.contains('error') == false"

CEL Type Safety

CEL is a strongly typed language. If you compare values of different types, the expression will fail at evaluation time. For example, inputs.count > "5" fails because you are comparing a number to a string.

Triggers

Triggers define how a workflow is started automatically when Mantle runs in server mode (mantle serve). A workflow can have zero, one, or multiple triggers.

triggers:
  - type: cron
    schedule: "*/5 * * * *"
  - type: webhook
    path: "/hooks/my-workflow"

Triggers are optional. Without them, the workflow can still be executed manually with mantle run or via the REST API (POST /api/v1/run/{workflow}).

Trigger Fields

FieldTypeRequiredDescription
typestringYesTrigger type. One of: cron, webhook.
schedulestringCron onlyCron expression defining the schedule. Required when type is cron.
pathstringWebhook onlyURL path for the webhook endpoint. Required when type is webhook.

Trigger Lifecycle

Triggers are managed through the standard IaC lifecycle. When you run mantle apply:

  • New triggers in the YAML are registered with the server
  • Changed triggers (e.g., updated cron schedule) are updated
  • Removed triggers (deleted from the YAML) are deregistered

You do not manage triggers separately. The workflow definition is the single source of truth.

Validation Rules Summary

Mantle validates the following rules when you run mantle validate or mantle apply:

RuleError Message
Workflow name is requiredname is required
Workflow name must be kebab-casename must match ^[a-z][a-z0-9-]*$
At least one step is requiredat least one step is required
Input names must be snake_caseinput name must match ^[a-z][a-z0-9_]*$
Input types must be validtype must be one of: string, number, boolean
Step names are requiredstep name is required
Step names must be kebab-casestep name must match ^[a-z][a-z0-9-]*$
Step names must be uniqueduplicate step name "NAME"
Step actions are requiredstep action is required
Retry max_attempts must be > 0max_attempts must be greater than 0
Retry backoff must be validbackoff must be one of: fixed, exponential
Timeout must be a valid durationinvalid duration: ...
Timeout must be positivetimeout must be a positive duration
Dependency cycle detectedcycle detected in step dependencies
depends_on references undefined stepreferences undefined step "NAME"

Validation errors include line and column numbers when available, formatted as:

workflow.yaml:3:1: error: step name must match ^[a-z][a-z0-9-]*$ (steps[0].name)

Minimal Valid Workflow

The smallest valid workflow contains a name and one step with an action:

name: hello
steps:
  - name: greet
    action: http/request
    params:
      method: GET
      url: https://httpbin.org/get