Participant types
A participant is the atomic unit of work in a duckflux workflow. Each participant has a type that determines how it executes. All participants share a set of common fields, and each type adds its own specific fields on top.
Common fields
Section titled “Common fields”These fields are available on every participant type:
| Field | Type | Default | Description |
|---|---|---|---|
type | string | — | Required. The participant type (exec, http, mcp, workflow, emit). |
as | string | — | Display name. Required for reusable participants; optional for inline ones. Enables output reference by name. |
timeout | duration | from defaults | Maximum execution time. Treated as a failure when exceeded. |
onError | string | fail | Error handling strategy. See Error Handling. |
retry | object | — | Retry configuration. Only applies when onError: retry. |
input | string or object | — | Explicit input mapping via CEL expressions. See Inputs & Outputs. |
output | object | — | Output schema (JSON Schema, opt-in). |
exec — Shell command
Section titled “exec — Shell command”Runs a shell command in the host environment. Use this for builds, tests, linting, deployments, or any operation that can be expressed as a terminal command.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
run | string | Required. The shell command to execute. |
cwd | string | Working directory. Supports CEL expressions. |
Examples
Section titled “Examples”participants: build: type: exec run: npm run buildparticipants: test: type: exec run: npm test timeout: 5m onError: retry retry: max: 3 backoff: 5s factor: 2participants: build: type: exec run: npm run build cwd: workflow.inputs.packagePathThe cwd field accepts either a literal path or a CEL expression. Precedence for working directory resolution:
participant.cwd > defaults.cwd > CLI --cwd > process cwdhttp — HTTP request
Section titled “http — HTTP request”Sends an HTTP request to an external URL. Use this for API integration, webhooks, and external service calls.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
url | string | Required. Target URL. Supports CEL expressions. |
method | string | HTTP method: GET, POST, PUT, PATCH, DELETE. |
headers | object | HTTP headers. Values support CEL expressions. |
body | string or object | Request body. Supports CEL expressions. |
Examples
Section titled “Examples”participants: fetchStatus: type: http url: https://api.example.com/status method: GETparticipants: notifySlack: type: http url: https://hooks.slack.com/services/T000/B000/xxxx method: POST headers: Content-Type: application/json body: text: '"Deployment completed: " + deploy.output.version'participants: fetchUser: type: http url: '"https://api.example.com/users/" + workflow.inputs.userId' method: GET headers: Authorization: '"Bearer " + env.API_TOKEN'mcp — MCP tool call
Section titled “mcp — MCP tool call”Delegates a task to an MCP (Model Context Protocol) server. Use this to invoke tools exposed by external MCP servers or integrations.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
server | string | Required. MCP server identifier. |
tool | string | Required. MCP tool name to invoke. |
Example
Section titled “Example”participants: codeReviewer: type: mcp server: claude tool: code_review input: code: coder.output language: workflow.inputs.language
flow: - coder - codeReviewerworkflow — Sub-workflow
Section titled “workflow — Sub-workflow”References another duckflux workflow file as a step. Use this to compose large workflows from smaller, reusable pieces.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
path | string | Required. Path to the sub-workflow YAML file, resolved relative to the parent workflow’s directory. |
Example
Section titled “Example”participants: reviewCycle: type: workflow path: ./review-loop.yaml input: repo: workflow.inputs.repoUrl branch: workflow.inputs.branch
flow: - coder - reviewCycle - deployThe sub-workflow’s output is accessible as reviewCycle.output in the parent. onError and timeout from the parent apply to the sub-workflow as a whole.
See Nested Workflows for full composition semantics.
emit — Event
Section titled “emit — Event”Publishes an event to the workflow’s event hub. Use this to broadcast state changes, trigger external listeners, or communicate between parallel branches.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
event | string | Required. Event name to emit. |
payload | string or object | Event payload. A single CEL expression or a map of CEL expression values. |
ack | boolean | If true, blocks until the event hub confirms delivery. Default: false. |
Fire-and-forget vs acknowledged
Section titled “Fire-and-forget vs acknowledged”By default, emit dispatches the event and continues immediately:
- as: notify type: emit event: "build.started" payload: build.outputWith ack: true, the step blocks until delivery is confirmed. Combine with timeout to cap the wait:
- as: notifyCritical type: emit event: "deploy.started" payload: deploy.output ack: true timeout: 10s onError: skipPayload formats
Section titled “Payload formats”A single CEL expression (passes the value directly):
payload: coder.outputA structured object with CEL expression values:
payload: taskId: workflow.inputs.taskId status: coder.output.status startedAt: execution.startedAtChoosing the right type
Section titled “Choosing the right type”| You want to… | Use |
|---|---|
| Run a shell command, script, or CLI tool | exec |
| Call an external API or send a webhook | http |
| Invoke a tool on an MCP server | mcp |
| Reuse a workflow from another file | workflow |
| Broadcast an event to listeners | emit |
Complete example
Section titled “Complete example”A pipeline combining all five participant types:
id: full-pipelinename: Full Pipelineversion: "1"
defaults: timeout: 10m
inputs: repoUrl: type: string required: true branch: type: string default: "main"
participants: # exec — run tests locally runTests: type: exec run: npm test timeout: 5m onError: fail
# http — fetch external config fetchConfig: type: http url: '"https://config.example.com/apps/" + workflow.inputs.branch' method: GET headers: Authorization: '"Bearer " + env.CONFIG_TOKEN'
# mcp — delegate code review to an MCP tool codeReview: type: mcp server: claude tool: review_pull_request input: repo: workflow.inputs.repoUrl branch: workflow.inputs.branch
# workflow — run deployment as a sub-workflow deploy: type: workflow path: ./deploy.flow.yaml input: repo: workflow.inputs.repoUrl
# emit — notify on completion notifyComplete: type: emit event: "pipeline.completed" payload: branch: workflow.inputs.branch testStatus: runTests.status reviewApproved: codeReview.output.approved ack: true timeout: 5s onError: skip
flow: - fetchConfig
- parallel: - runTests - codeReview
- deploy: when: runTests.status == "success" && codeReview.output.approved == true
- notifyComplete
output: testStatus: runTests.status reviewApproved: codeReview.output.approved deployStatus: deploy.status