Orchestration Constructs
Canonical reference for non-step flow forms and metadata labels used for readable orchestration timelines.
Quick Answer
Use standard flow forms (if, for, loop, cond, case, etc.) inside :flow, and annotate control nodes with metadata labels (^{:label ...}) so runs render human-readable branch and loop names.
When you need bounded child-workflow batch spawn/collect, use the :fanout step instead of a non-step form.
Supported Non-Step Forms
- sequencing:
let,do - branching:
if,if-not,when,when-not,cond,case - iteration:
for,doseq,loop,recur - child orchestration:
flow/call-flow
:fanout is intentionally not listed here because it is a step, not a non-step form.
See Step Fanout for bounded child-workflow fanout behavior.
Child Flow Calls
Use the positional flow/call-flow form:
(flow/call-flow :child-flow
{:plain "child input"})
For installed public child flows, pass the installation in the third options map:
(flow/call-flow :reviewed-ai-response-generator
{:prompt prompt}
{:installation-id reviewed-ai-response-installation-id})
For an author-owned child flow in the same workspace, a draft run may need a
child profile when the child uses profile-bound connections, secrets, or setup
values. An unqualified call from a draft parent automatically selects the
child flow's enabled draft profile:
(let [input (flow/input)]
(flow/call-flow :review-social-draft input))
The inherited draft marker is resolved separately for each child flow, so this
does not reuse or expose the parent's profile id. If the child has no enabled
draft profile, pass an explicit child :profile-id after configuring it. For an
installed public child flow, use :installation-id; a workspace draft profile
is not the consumer's installation. When a parent is already running inside a
linked public app, omit the target and let the linked install context flow to
same-app children.
Do not put :installation-id in the child input map, and do not use a one-map
shape such as (flow/call-flow {:flow-slug ... :installation-id ... :input ...}).
Label Metadata
Attach metadata directly to a control form:
- shorthand:
^"My Label" (if ...) - explicit:
^{:label "My Label"} (if ...)
Branch-specific labels on if / if-not:
^{:label "Risk gate"
:yes "Requires approval"
:no "Auto-approve"}
(if (:needs-approval summary)
(flow/step :wait :approval {...})
{:action :approve})
Loop metadata:
^{:label "Retry status poll" :max-iterations 6}
(loop [attempt 0]
(let [status (flow/step :http :get-status
{:connection :orders-api
:method :get
:path "/orders/status"})]
(if (or (= :done (:state status))
(>= attempt 5))
status
(recur (inc attempt)))))
for / doseq label example:
^{:label "Enrich each order"}
(for [order orders]
(flow/step :http :enrich-order
{:connection :orders-api
:method :get
:path (str "/orders/" (:id order))}))
Branching Guidance
- use
if/if-notfor binary business gates and set:yes/:no - use
when/when-notfor one-sided conditionals - use
cond/casefor multi-branch routing; add^{:label ...}on the whole form - keep transformation logic in top-level
:functions, not inside branch bodies
Design Rules
- label major decisions and loops; skip labels only for trivial one-liners
- keep labels business-facing ("Approve order?", "Retry payment status")
- keep
:flowfocused on orchestration, and call steps/functions/templates for heavy logic/data - combine labels with
:persistand templates to keep large flows readable and size-safe - use
flow/pollfor bounded external status polling instead of custom unbounded loops