Workflow authoring
The versioned workflow schema defines the allowed fields. The prime_stats and product_onboarding definitions are executable examples.
Steps, sequence, and result
steps names reusable operations. Each step has one target: kind: service
requires service and action, while kind: workflow requires one child workflow. flow names
each invocation and its transition. result publishes one resolved reference as workflow output.
workflow: lookup_order
result: lookup.order
steps:
get_order:
target:
kind: service
service: orders
action: get
flow:
- name: lookup
op: get_order
input:
order_ref: order_ref
output: order
then: done
- name: done
terminal: true
References resolve from trigger globals, workflow parameters, and earlier named outputs. Validation rejects unknown references, cycles, unreachable states, and illegal transition combinations.
Branching and failure handling
on_result evaluates in order and ends with one default target. condition can skip an operation
to its then target. on_failure handles an operation failure; workflow-level on_error routes
otherwise unhandled failures.
- name: decide
op: classify
output: classification
on_result:
- when: output.accepted == true
then: accepted
- default: rejected
Failures are recorded with stable error codes. The transport or provider determines whether an error can be retried; your workflow defines where to go if the operation fails.
Fan-out and fork/join
for_each invokes one operation per item. parallel: true requires max_concurrency; the runtime
also enforces global item, allocation-chunk, and invocation limits. on_iteration_fail is stop,
skip, or collect.
- name: inspect_items
op: inspect
input: load.items
for_each: input
as: item
parallel: true
max_concurrency: 8
on_iteration_fail: collect
output: inspections
then: summarize
This is dynamic bounded fan-out with an implicit join. Static independent fork/join branches are not supported.
Polling loops and durable waits
An until operation retries with an optional durable interval and a bounded maximum. Exhaustion can
route to a named state.
- name: poll
op: read_status
until: output.ready == true
max_iterations: 20
interval_sec: 30
on_exhausted: timed_out
output: status
then: ready
wait_for durably waits for a declared signal/event and must have a relative or absolute timeout.
The product-onboarding example demonstrates the timeout branch.
- name: approval
wait_for:
signal: approved
timeout_sec: 3600
on_timeout: approval_expired
output: decision
then: continue
Absolute wait deadlines
timeout_until points to a workflow parameter or earlier step output that resolves when the wait
begins. The resolved value must be either numeric epoch seconds or an ISO-8601 timestamp with Z or
an explicit UTC offset. The runtime normalizes offset timestamps to UTC before calculating the
timer. A timestamp without an offset, such as 2026-09-01T12:00:00, is invalid because it does not
identify one instant.
- name: wait_for_confirmation
wait_for:
signal: confirmed
timeout_until: schedule.confirmation_deadline
on_timeout: request_manual_confirmation
then: confirmed
Store and emit deadline timestamps in UTC with Z. If the source value represents local wall-clock
time, the producing action must resolve its IANA timezone and daylight-saving policy before
returning it. When both timeout_sec and timeout_until are present, the wait uses whichever
expires first. A deadline that has already passed takes the timeout path immediately.
Use sleep_sec for a durable delay that produces no output.
Child workflows
A step with a workflow target starts a Temporal child using the exact definition selected for that
child. Its trigger globals contain the declared step parameters, the resolved flow input under
input when present, and the as alias during iteration. Parent workflow parameters are available
for interpolation but are not implicitly copied. The child's workflow input schema validates this
whole globals object, including these envelope fields. A child used both through an API and with
as: customer can require a customer field and accept the additional input field supplied by
the parent. Declare customer: "${customer}" in the child's workflow params so its flow can refer
to it. Iteration aliases cannot use reserved engine roots such as input. The child owns its
input/output contracts and internal cache.
steps:
provision:
target:
kind: workflow
workflow: provision_workspace
params:
workspace_ref: "${workspace_ref}"
Contracts and cache integrity
Workflow and action input_schema/output_schema accept an inline Draft 2020-12 JSON Schema or a
dotted Pydantic model path. $ref and $dynamicRef resolve only within the declared resource;
local pointers, anchors, dynamic anchors and nested $id resources are supported. An HTTPS $id
is an identifier, not a download. External references are rejected and the runtime resolver cannot
retrieve schemas from the network. Values use strict JSON: duplicate keys,
non-finite numbers, malformed cache entries, and schema-invalid values fail without coercion.
steps:
lookup:
target:
kind: service
service: inventory
action: lookup
input_schema: application.contracts.LookupRequest
output_schema: application.contracts.LookupResult
cache:
resource: workflow_cache
key: inventory/${item_ref}
ttl_sec: 300
Caching is for idempotent operations and is unavailable on child, for_each, or until steps.
The cache provider must protect sensitive values and enforce TTL.
Audit capture
on_complete selects an archive resource and finite retention policy. Capture is metadata-only by
default. redacted requires explicit JSON Pointer paths and a byte bound; approved-full additionally
requires encrypted archival. Ordinary failure and completion attempt archival, while Temporal
service termination, timeout, and cancellation do not currently invoke it.
on_complete:
resource: audit_store
path: audit/orders/${request_id}.json
retention_policy: audit-30d
capture:
mode: redacted
paths:
- /params/contact_ref
max_payload_bytes: 65536
See security for Temporal payload codecs, archive encryption, and retention responsibilities.