Docs
Reference

Step Fanout (:fanout)

Quick Answer

Use :fanout when you need a bounded batch of child workflows that start now and collect later.
Async fanout is available only when every item is {:type :call-flow ...}.
Legacy non-child fanout items are still accepted for backward compatibility, but they run on the sequential compatibility path.

Use for bounded child-workflow spawn/collect, not as a general parallel side-effect primitive.

Canonical Shape

Core fields:

FieldTypeRequiredNotes
:typekeywordYesUse :fanout
:itemsvectorYesRuntime width is bounded to 25 items
:max-concurrencyintNoRequested width for async child fanout; must be 1-5
:on-errorkeyword/stringNo:fail (default), :continue, or :collect
:transformformNoSCI transform over the aggregated result map

Async child-workflow items:

Item fieldTypeRequiredNotes
:typekeywordYesMust be :call-flow for async child fanout
:flow-slugkeyword/stringYesChild flow to start
:inputanyNoChild input payload
:timeoutintNoMapped to child workflow timeout fields
:versionintNoOptional child version override
:installation-idstringNoOptional child installation/live target override
:workflow-idstringNoOptional explicit child workflow id
:parent-close-policykeyword/stringNo:abandon, :request-cancel, or :terminate
:retry-policymapNoOptional child retry override

:profile-id is still accepted as a compatibility alias for older child-flow
items, but new source should use :installation-id for target-specific child
runs.

Child-Workflow Mode

Async :fanout is enabled only when all items are :call-flow items.
In that mode, Breyta:

  • starts children up to the effective concurrency cap
  • waits until child start is confirmed before counting the child as spawned
  • collects results later while preserving the original item order
  • records fanout lineage on child executions (:root-workflow-id, :parent-workflow-id, :fanout-depth, :fanout-parent-step-id, :fanout-max-concurrency)
  • inherits draft child version 0 when the parent is running from draft and the item does not specify :version, :installation-id, or live target context
  • treats an explicit child :version as a versioned child execution; run lists
    show the child lineage fields so you can distinguish these from direct manual
    live runs

Canonical example:

(flow/step :fanout :spawn-children
           {:items [{:type :call-flow
                     :flow-slug :billing-apply
                     :input {:invoice-id "inv-1"}}
                    {:type :call-flow
                     :flow-slug :billing-apply
                     :input {:invoice-id "inv-2"}}]
            :max-concurrency 5
            :on-error :collect})

Typical parent-flow pattern:

'(let [input (flow/input)
       fanout (flow/step :fanout :spawn-children
                         {:items (:items input)
                          :max-concurrency 5
                          :on-error :collect})]
   {:item-count (count (:items input))
    :fanout fanout})

Legacy Compatibility Mode

Legacy fanout items are still accepted for these step types:

  • :http
  • :llm
  • :notify
  • :function
  • :code

That mode is kept for compatibility only.
It executes deterministically in a loop, even if :max-concurrency is provided.
For new orchestration, prefer explicit loops or child workflows instead of adding new legacy fanout usage.

Result Shape

Default return shape:

{:successes [{:item ... :result ...}]
 :failures [{:item ... :error ...}]
 :total N
 :success-count N
 :failure-count N}

When :transform is set, the transform receives:

{:successes [...]
 :failures [...]
 :total N
 :success-count N
 :failure-count N
 :items [...]}

Guardrails And Limits

  • Async fanout supports only :call-flow items.
  • Do not mix legacy step items and :call-flow items in one :fanout.
  • Nested :fanout items are rejected.
  • Nested async child fanout is rejected beyond one level.
  • :max-concurrency must be at most 5; validation rejects larger values.
  • One :fanout step can include at most 25 items.
  • One root run can start at most 25 child workflows total.
  • One run can execute at most 5 fanout steps and 100 total fanout items across those steps.

For child flows that can produce large outputs, persist large artifacts inside the child flow and return compact refs or summaries.
Collected fanout results are still subject to inline result limits.

Related

As of May 16, 2026