Define Workflows
Updated 12 hours ago • October 13, 2026
The core contract lives in @kortyx/core and is exposed through kortyx.
Good to know: If you want the mental model for what a node receives and returns, start with Nodes before using this page as the workflow shape reference.
TypeScript workflow (recommended)
Callable workflows
A workflow called by useWorkflow(...) declares inputSchema and outputSchema. Its last node returns ordinary data and reaches __end__; the runtime validates accumulated child data and returns it to the caller. Existing root workflows can omit these schemas.
Calls live in node code or custom hooks. Register both workflows with the agent; no special call edge or return node is required. See Call Child Workflows for typed definitions, string IDs, and interrupts.
Opt-in data reducers
Node data continues to deep-merge object fields by default; arrays and scalars
are overwritten. There is no automatic union branch replacement. Use workflow
reduce to override individual top-level fields, with callback types inferred
from z.output<typeof outputSchema>:
Reducers run only for explicitly returned fields. Omitted fields retain their
stored values; fields without callbacks keep the existing deep merge. previous
is undefined when no value is stored. The callback result is stored directly,
without another deep merge; returning undefined removes the field from data
and flowing node input.
Callbacks must be synchronous, pure and deterministic: do not mutate arguments,
call hooks, perform I/O or depend on time/randomness. A parallel join can recompute
an accumulated view from raw updates, and replay can execute callbacks again.
Joins fold each visible node's writes once in stable topological order, using
nodes declaration order for ties, regardless of completion timing. Shared
ancestors are not counted twice. Conflicting branch fields without reducers still
produce GRAPH_OUTPUT_CONFLICT.
The same policy applies across nodes, chat turns and resume. Restoring a saved snapshot does not reduce its values again. Child workflows use their own reducers; a parent reducer then sees the child's validated output only when a parent node returns that field. A child's omitted field does not clear the parent's value.
Reducers require outputSchema. Existing output validation remains at completion
boundaries, allowing incomplete intermediate output. Clearing a required field or
returning the wrong type fails with INVALID_OUTPUT. A thrown callback or an async
callback supplied by untyped code fails with WORKFLOW_REDUCER_FAILED (workflow
category, not retryable), identifying the workflow and field; the original error
is retained as a private cause. No field values or raw exception prose are added
to the public failure.
Use TypeScript/object workflow definitions for callbacks; JSON and YAML cannot encode functions. Bump the workflow version when changing reducers before resuming old executions. Existing workflows need no changes; reducer-enabled workflows require the new core/runtime packages on every execution worker.
Node return shape
Nodes return a subset of NodeResult. See Nodes for the full return contract and how returned data is passed to the next node.
Important fields in practice:
data: merged into runtime state and forwarded as future inputui.message: emitted as a final message eventui.structured: emitted asstructured-datacondition/intent: used for conditional edge routingtransitionTo: triggers workflow handoff
Node behavior currently implemented
From WorkflowNodeBehavior and runtime code:
behavior.retry.maxAttemptsbehavior.retry.delayMsbehavior.checkpoint
Note: onError.mode exists in schema types but is not currently applied by the execution graph compiler.
Validation
You can validate directly: