Loading

Flow control steps

Flow control steps shape a workflow's logic. They decide what runs, what gets skipped, when the workflow loops, and where it pauses.

Workflows include the following flow-control step types. Use this table to understand when to use each.

Pattern Step
Branch on a condition if
Iterate over an array foreach
Loop until a condition is false while
Multi-way dispatch on a value switch
Run independent work at the same time parallel
Pause for a fixed duration wait
Exit a loop early loop.break
Skip to the next loop iteration loop.continue
Pause for human input (human-in-the-loop) waitForInput
Pause for approve/reject (human-in-the-loop) waitForApproval

Two step types fan work out. Use parallel when the work runs inside the current workflow and you need the results, and workflow.executeAsync when each unit of work should run as its own independent execution that the parent doesn't wait for.

Conditional branching. Evaluates a Kibana Query Language (KQL) or boolean expression and runs the steps block if true, or the optional else block if false.

- name: route_severity
  type: if
  condition: "event.alerts[0].kibana.alert.risk_score >= 70"
  steps:
    - name: high_priority
      type: console
      with: { message: "High priority alert" }
  else:
    - name: low_priority
      type: console
      with: { message: "Standard alert" }
		

For expression syntax and additional examples, refer to If step.

Iterate over an array, running nested steps once per item. Inside the loop, the current item is available as foreach.item, the zero-based position as foreach.index, and the total count as foreach.total. It also supports guardrails to cap iterations, set timeouts, and handle failures.

- name: process_alerts
  type: foreach
  foreach: "${{ event.alerts }}"
  max-iterations:
    limit: 100
    on-limit: fail
  iteration-timeout: "30s"
  steps:
    - name: log_alert
      type: console
      with:
        message: "[{{ foreach.index }}/{{ foreach.total }}] {{ foreach.item._id }}"
		

For the full parameter reference, refer to Foreach step.

Loop while a KQL condition evaluates to true. The max-iterations field caps the number of iterations and defaults to 2000. The default on-limit behavior is continue, which means the step succeeds quietly when the cap is reached. To fail the workflow on the cap instead, use the object form with on-limit: fail. Like foreach, it also supports loop-level guardrails and per-iteration controls.

- name: poll_until_ready
  type: while
  condition: "steps.check.output.status : pending"
  max-iterations:
    limit: 60
    on-limit: fail
  steps:
    - name: check
      type: elasticsearch.search
      with:
        index: "jobs"
        size: 1
    - name: backoff
      type: wait
      with:
        duration: "5s"
		

For the full parameter reference and gotchas, refer to While step.

Multi-way branching. The engine evaluates an expression once and routes to the matching case. Each case has a match value and a steps array. An optional default runs when no case matches.

- name: dispatch_by_category
  type: switch
  expression: "{{ steps.classify.output.category }}"
  cases:
    - match: "malware"
      steps:
        - name: handle_malware
          type: console
          with: { message: "malware path" }
    - match: "phishing"
      steps:
        - name: handle_phishing
          type: console
          with: { message: "phishing path" }
  default:
    - name: handle_other
      type: console
      with: { message: "other" }
		

For the full parameter reference, refer to Switch step.

Run branches at the same time and continue when all of them reach a terminal state. Choose one of two modes: dynamic fan-out (foreach plus steps) runs the same body once per item in a runtime list, and static branches (branches) run a fixed set of named branches that each do different work. A step can't use both.

By default, five branches run at once and the first failure stops the step from starting more branches. The step output holds a per-branch result plus aggregate counts.

- name: enrich_file
  type: parallel
  mode: settled
  branch-timeout: "30s"
  branches:
    - name: reputation
      steps:
        - name: check_reputation
          type: http
          with:
            url: "https://api.example.com/hash/{{ inputs.file_hash }}"
    - name: internal_sightings
      steps:
        - name: search_sightings
          type: elasticsearch.search
          with:
            index: "logs-*"
            query:
              term:
                file.hash.sha256: "{{ inputs.file_hash }}"
		

A branch body must be a straight-line sequence of steps: no nested if, switch, foreach, or while, no waitForInput or waitForApproval, and no step-level on-failure or timeout. Use mode and branch-timeout on the parallel step instead, and handle failures in a step that runs after it.

For the full parameter reference, both modes, and the output shape, refer to Parallel step.

Pause execution for a specified duration, then continue to the next step.

- name: backoff
  type: wait
  with:
    duration: "30s"
		

For the full parameter reference, refer to Wait step.

Exit the innermost enclosing loop (foreach or while) immediately. Takes no parameters.

- name: stop_on_match
  type: if
  condition: "foreach.item.severity : critical"
  steps:
    - name: exit
      type: loop.break
		

For the full reference, refer to Loop break step.

Skip to the next iteration of the innermost enclosing loop. Takes no parameters.

- name: skip_empty
  type: if
  condition: "foreach.item.empty : true"
  steps:
    - name: next
      type: loop.continue
		

For the full reference, refer to Loop continue step.

Pause the workflow until a human responds. The primary human-in-the-loop primitive. Responders can reply in Kibana, through the resume API, or through an external channel.

- name: review
  type: waitForInput
  with:
    message: "Review the AI classification and confirm the action."
    schema:
      type: object
      properties:
        approved:
          type: boolean
          title: "Approve"
        notes:
          type: string
          title: "Notes"
      required: ["approved"]
		

For the complete HITL pattern, refer to Human-in-the-loop. For the step parameter reference, refer to waitForInput step.

Pause the workflow until a human approves or rejects the request. Use when the decision is yes or no. The step returns approved: true or false.

- name: request_approval
  type: waitForApproval
  timeout: 24h
  with:
    message: "Approve isolation for {{ event.alerts[0].host.name }}?"
    approveLabel: Approve
    rejectLabel: Decline
		

For the complete HITL pattern, refer to Human-in-the-loop. For the step parameter reference, refer to waitForApproval step.