Step Contract (Fields, Options, Rules)¶
This page documents the step-level contract for workflow YAML.
Scope: - Field-level options per step type. - Required vs optional fields. - Common validation and behavior rules.
Source of truth:
- engine/services/orchestrator/app/workflow_runner.py
- WorkflowStep discriminated union and corresponding step models.
Shared Contract (Most Step Types)¶
Common fields used by many steps through shared mixins:
| Field | Required | Type | Description |
|---|---|---|---|
id |
yes | string |
Unique step identifier within the workflow. |
type |
yes | string |
Step discriminator (for example http, xslt, for_each). |
when |
no | object | Conditional execution rule based on context values. |
run_after |
no | list | Dependency gating based on previous step status. |
compensate |
no | object | Compensation steps for rollback behavior. |
retry_policy |
no | object | Per-step retry override (where supported). |
Notes:
- when supports equals, not_equals, or one_of (exactly one).
- run_after supports concise YAML form like {step_id: succeeded}.
- Control-flow steps may handle retries differently than integration steps.
Step Type Registry¶
Supported values for type:
- xslt
- http
- ftp
- imap_peek
- smtp_send
- ssh
- sftp
- rabbitmq_publish
- xml2json
- json2xml
- liquid
- javascript
- storage_read
- storage_write
- context_set
- context_extract_json
- context_extract_xml
- json_set
- xml_set_text
- terminate
- wait
- log
- workflow_call
- collect
- for_each
- repeat_until
- if
- parallel
Contract by Step Type¶
xslt¶
Required:
- id
- type: xslt
- at least one of xslt or xslt_file
Optional:
- input_from
- retry_policy
- shared fields (when, run_after, compensate)
Validation rules:
- Fails validation if both xslt and xslt_file are empty.
http¶
Required:
- id
- type: http
- http.body_from
- either:
- connection with http.path or http.url, or
- no connection and http.url set
Optional:
- connection
- http.method (default GET)
- http.headers (default {})
- http.timeout_seconds (default 60, range 1..300)
- retry_policy
- circuit_breaker
- async_wait
- shared fields
Validation rules:
- Without connection, http.url is required.
- With connection, do not set both http.url and http.path together.
ftp¶
Required:
- id
- type: ftp
- ftp.action
- if no connection, ftp.host
- for ftp.action: store, ftp.body_from
Optional:
- connection
- ftp.protocol (default ftp)
- ftp.port (default 21)
- ftp.username, ftp.password
- ftp.remote_path (default /)
- ftp.body_encoding (default utf8, options utf8 or base64)
- ftp.timeout_seconds (default 60, range 5..600)
- retry_policy
- shared fields
ssh¶
Required:
- id
- type: ssh
- ssh.command
- if no connection: ssh.host and ssh.username
Optional:
- connection
- ssh.port (default 22)
- ssh.password
- ssh.private_key_from
- ssh.timeout_seconds (default 60, range 5..600)
- retry_policy
- shared fields
Validation rules:
- With connection, ssh.host and ssh.username must not be set inline.
sftp¶
Required:
- id
- type: sftp
- sftp.action
- if no connection: sftp.host and sftp.username
- for sftp.action: store, sftp.body_from
Optional:
- connection
- sftp.port (default 22)
- sftp.password
- sftp.private_key_from
- sftp.remote_path (default .)
- sftp.body_encoding (default utf8, options utf8 or base64)
- sftp.timeout_seconds (default 60, range 5..600)
- retry_policy
- shared fields
Validation rules:
- With connection, sftp.host and sftp.username must not be set inline.
rabbitmq_publish¶
Required:
- id
- type: rabbitmq_publish
- rabbitmq.message_from
- if no connection, rabbitmq.url
Optional:
- connection
- rabbitmq.publish_target (default exchange)
- exchange: publish to rabbitmq.exchange with rabbitmq.exchange_type and routing key (topic/direct/fanout patterns — see RabbitMQ tutorials Routing / Topics).
- queue: publish directly to a queue name via the broker default exchange (same idea as Hello World / Work Queues: producers send to a queue by name). Set rabbitmq.queue or rabbitmq.routing_key to the queue name; with connection, you may fall back to connection.routing_key if both are omitted on the step.
- rabbitmq.exchange (default meshflows.events; ignored when publish_target is queue)
- rabbitmq.exchange_type (default topic)
- rabbitmq.routing_key
- rabbitmq.queue (optional; when publish_target: queue, preferred over routing_key for the target queue name)
- rabbitmq.properties / rabbitmq.property_refs
- rabbitmq.headers
- rabbitmq.persistent (default true)
- rabbitmq.content_type (default text/plain)
- retry_policy
- shared fields
imap_peek¶
Required:
- id
- type: imap_peek
- connection
Optional:
- action (default peek; allowed: peek, list, read)
- mailbox (default INBOX)
- filter (default UNSEEN)
- limit (default 10, only relevant for action: list)
- message_id (optional, static message id for action: read)
- message_id_from (optional ref for dynamic message id, e.g. context:selected_message_id)
- include_body (default false, only for action: read)
- include_attachments (default false, only for action: read)
- attachment_max_bytes (default 1048576, max bytes per attachment for action: read)
- mark_seen (default false, only for action: read)
- retry_policy
- shared fields
smtp_send¶
Required:
- id
- type: smtp_send
- connection
- to
- subject
Optional:
- body_from (default previous)
- is_html (default false)
- from_override
- cc (comma-separated or list)
- bcc (comma-separated or list)
- reply_to
- text_body_from (optional source for plain-text body)
- html_body_from (optional source for HTML body)
- attachments (optional static list of {filename, content_b64, content_type?})
- attachments_from (optional source resolving to JSON list with attachment rows)
- retry_policy
- shared fields
Notes:
- to can be a string or a list; list input is normalized to comma-separated string.
xml2json¶
Required:
- id
- type: xml2json
Optional:
- input_from
- retry_policy
- shared fields
json2xml¶
Required:
- id
- type: json2xml
Optional:
- input_from
- retry_policy
- shared fields
liquid¶
Required:
- id
- type: liquid
- at least one of template or template_file
Optional:
- input_from
- retry_policy
- shared fields
Validation rules:
- Fails validation if both template and template_file are empty.
javascript¶
Required:
- id
- type: javascript
- code
Optional:
- input_from
- retry_policy
- shared fields
storage_read¶
Required:
- id
- type: storage_read
- storage_read.bucket
- storage_read.key
Optional:
- storage_read.output_field (default value)
- storage_read.write_context_key
- storage_read.required_scope
- retry_policy
- shared fields
storage_write¶
Required:
- id
- type: storage_write
- storage_write.bucket
- storage_write.key
- storage_write.value_from
Optional:
- storage_write.content_type (default text/plain)
- storage_write.write_context_key
- storage_write.required_scope
- retry_policy
- shared fields
context_set¶
Required:
- id
- type: context_set
- context_key
- exactly one of value or value_from
Optional:
- retry_policy
- shared fields
Validation rules:
- Fails validation if both value and value_from are set, or both missing.
context_bind¶
Parse JSON, XML, or YAML once and register it under an alias for path references (request:json:/field). See Bound documents.
Required:
idtype: context_bindalias
Optional:
input_fromformat(auto,json,xml,yaml)- shared fields
context_extract_json¶
Required:
- id
- type: context_extract_json
- context_key
- json_path
Optional:
- input_from
- retry_policy
- shared fields
context_extract_xml¶
Required:
- id
- type: context_extract_xml
- context_key
- xpath
Optional:
- input_from
- shared fields
json_set¶
Required:
- id
- type: json_set
- json_path
- value_from
Optional:
- input_from
- mirror_to_context
- shared fields
xml_set_text¶
Required:
- id
- type: xml_set_text
- xpath
- value_from
Optional:
- input_from
- attribute
- mirror_to_context
- shared fields
terminate¶
Required:
- id
- type: terminate
- status in: success, failed, ignored, canceled, skipped
Optional:
- reason
- http_status (range 200..599)
- shared fields
Behavior: - Stops workflow execution immediately and returns a controlled termination state.
wait¶
Required:
- id
- type: wait
- seconds (range 0..3600)
Optional: - shared fields
Behavior: - Pauses execution for the configured duration.
log¶
Required:
- id
- type: log
- message
Optional:
- values (default {})
- level (debug, info, warning, error; default info)
- tags (default [])
- into
- shared fields
Behavior:
- Writes structured log event and can store rendered message in context via into.
workflow_call¶
Required:
- id
- type: workflow_call
- target
Optional:
- payload
- output_key
- timeout_seconds (range 1..3600)
- shared fields
Behavior:
- Invokes another workflow as a child run.
- Applies cycle detection and maximum call depth.
- Inherits parent timeout budget, optionally capped by timeout_seconds.
- Emits trace linkage metadata (parent_run_id, child_run_id, depth/call-chain context).
collect¶
Required:
- id
- type: collect
- from_steps (at least one entry)
Optional:
- merge_mode (object or array, default object)
- try_parse_json (default true)
- shared fields
Notes:
- from_steps entries can be:
- step_id
- {step_id: alias}
Validation rules:
- Mapping entries in from_steps must contain exactly one key.
for_each¶
Required:
- id
- type: for_each
- steps (at least one nested step)
Optional:
- input_from
- items_path (default /)
- as_key (default item)
- index_as
- max_iterations (default 100, range 1..10000)
- shared fields
Behavior: - Iterates over array items and executes nested steps per item.
repeat_until¶
Required:
- id
- type: repeat_until
- until
- steps (at least one nested step)
Optional:
- max_iterations (default 20, range 1..10000)
- shared fields
Behavior: - Repeats nested steps until condition matches or limit is reached.
if¶
Required:
- id
- type: if
- condition
- then (or then_steps)
Optional:
- else (or else_steps, default empty)
- shared fields
condition is a WhenCondition object:
| Field | Required | Description |
|---|---|---|
context_key (alias variable) |
yes | Run context key to read |
equals |
exactly one of | Expected string value |
not_equals |
these three | Must not equal |
one_of |
Value must be in list |
Behavior:
- Executes then branch when condition matches, otherwise else.
- Compares context strings only — no JSON Pointer or XPath in context_key.
- To branch on a field inside trigger JSON, XML, or service_call output, use context_extract_json / context_extract_xml first (see Conditions & Path Expressions).
try¶
Required:
- id
- type: try
- steps (at least one nested step)
Optional:
- catch (or catch_steps, default empty)
- rethrow (default false)
- error_context_key (default {step_id}.error)
- shared fields
Behavior:
- Runs steps sequentially; on failure stores the error message in context and runs catch substeps when present.
- When catch is empty, failures propagate like a normal step failure.
- When rethrow is false (default), the try step is marked successful after catch and the workflow continues.
- When rethrow is true, catch substeps still run but the original error is raised afterward (workflow fails).
- WorkflowTerminatedByStep is never caught and always stops the run immediately.
parallel¶
Required:
- id
- type: parallel
- steps (at least one nested step)
Optional:
- max_concurrency (default 10, range 1..100)
- outputs_map (auto-generated if omitted)
- fail_fast (default true)
- shared fields
Validation rules:
- outputs_map values must reference existing substep IDs.
Contract Tips¶
- Prefer connection-based integration steps over inline credentials.
- Keep step IDs stable; IDs are used by references (
body_from,run_after,collect). - Use
workflow_callfor composition, but avoid deep nesting unless required. - Validate workflows locally with:
python engine/scripts/validate-workflow.py flows/workflows/<your-file>.yaml
Related Pages¶
- Workflow YAML syntax: Workflow YAML (Forgejo mirror: Workflow YAML)
- Step type overview: Step Types (Forgejo mirror: Step Types)
- Error handling and compensation: Error Handling (Forgejo mirror: Error Handling)