Skip to content

Exploring participants

Every step in a duckflux workflow is a participant — the atomic unit of work. Participants receive input, do something, and produce output. The flow is just an ordered sequence of participants.

There are two dimensions to understand: how you define a participant (anonymous, named inline, or reusable) and what type it is (exec, http, mcp, workflow, emit).


The simplest form. Write the participant directly in the flow without a name:

flow:
- type: exec
run: curl -s https://api.example.com/data
- type: exec
run: ./process.sh

Each step’s output is automatically passed to the next step — like Unix pipes. Anonymous participants are quick to write, but their output can’t be referenced by name in later steps.

Add as to give a step an addressable name, while still defining it inline:

flow:
- as: build
type: exec
run: npm run build
- as: deploy
type: exec
run: ./deploy.sh
input: build.output.artifactPath

Named steps are addressable in CEL expressions (build.output, build.status, etc.). Use this when you need to reference the result downstream, but the step is only used once.

Define steps in the top-level participants block and reference them by name in the flow. This is ideal for steps that appear more than once, or have complex configuration:

participants:
test:
type: exec
run: npm test
timeout: 5m
onError: retry
retry:
max: 2
backoff: 5s
flow:
- test
- deploy
- test # run again after deploy

Reusable participants keep the flow readable and the configuration in one place.


AnonymousNamed inlineReusable
Defined inflow (no as)flow (with as)participants block
Output addressable by nameNoYesYes
Can appear multiple timesNoNoYes
Best forSimple one-off stepsOne-off named stepsShared steps, complex config

The type field determines how a participant executes:

TypeWhat it does
execRuns a shell command. Output is the command’s stdout.
httpMakes an HTTP request. Supports dynamic URLs, headers, and body via CEL.
mcpInvokes a tool on an MCP server.
workflowRuns another .flow.yaml file as a sub-workflow.
emitPublishes an event to the workflow’s event hub.

The most common type. Runs any shell command:

participants:
build:
type: exec
run: npm run build
timeout: 10m

If the command outputs valid JSON, it’s parsed automatically — fields become accessible as build.output.<field> in downstream expressions.

Makes an HTTP request to an external URL. All field values support CEL expressions:

participants:
notify:
type: http
url: https://hooks.example.com/deploy
method: POST
headers:
Authorization: '"Bearer " + env.API_TOKEN'
body:
status: deploy.output.status
onError: skip

Delegates work to an MCP server tool:

participants:
reviewer:
type: mcp
server: claude
tool: code_review
input:
code: coder.output

Embeds another workflow file as a step. Paths are resolved relative to the parent file:

participants:
reviewCycle:
type: workflow
path: ./review-loop.flow.yaml
input:
repo: workflow.inputs.repoUrl

Publishes an event. By default fire-and-forget; use ack: true to block until delivery:

- as: notify
type: emit
event: "deploy.completed"
payload:
status: deploy.output.status
ack: true
timeout: 10s