Skip to content

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:

  • id
  • type: context_bind
  • alias

Optional:

  • input_from
  • format (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_call for composition, but avoid deep nesting unless required.
  • Validate workflows locally with:
python engine/scripts/validate-workflow.py flows/workflows/<your-file>.yaml