Sub-workflows
A sub-workflow is a workflow invoked by another workflow. A Workflow Template can declare a step that runs a second template as a child Run, producing its own Artifact and rolling its cost up into the parent. This is how Esy keeps artifacts reusable instead of re-deriving the same work inside every workflow.
Why composition
Some artifacts are inputs to others. A research report is valuable on its own, and it is also the substrate for a research infographic, a brief, or a slide. Rather than re-implement research inside each of those workflows, the report is authored once as generate-research-report and composed by the others. The child run is a first-class Run: it has its own steps, telemetry, cost, artifact, and review state.
The subWorkflow step
Composition is declared as a runtime step with kind: "subWorkflow". The step pins a child template id and version and supplies an intakeMapping — a map from parent context paths to the child template’s intake field names.
{
"id": "step-1",
"name": "Research report",
"kind": "subWorkflow",
"subWorkflow": {
"templateId": "generate-research-report",
"templateVersion": "2026.05.29",
"intakeMapping": {
"intake.topic": "topic",
"intake.audience": "audience"
}
}
}| Field | Purpose |
|---|---|
templateId | The child Workflow Template to invoke. |
templateVersion | Pinned child version. Composition is reproducible against the exact child definition used. |
intakeMapping | Maps a parent context path (for example intake.topic) to a child intake field name (for example topic). The engine resolves each path against the parent run context and passes the result as the child’s intake. |
Parent and child runs
When the engine reaches a subWorkflow step, it creates a child run linked to its parent by parentRunId and parentStepId. The child executes to completion and persists its own artifact; the parent step records the childRunId so the two are navigable in both directions in the app.
Parent run (generate-research-infographic)
├── step-1 subWorkflow ─────────────┐
│ ▼
│ Child run (generate-research-report)
│ └── artifact: research-report
├── step-2 llm (compose infographic prompt)
└── step-3 image (render infographic)
└── artifact: infographic
└── sourceReportArtifactId → research-reportThe downstream artifact links back to the artifact the sub-workflow produced. An infographic composed from a report carries sourceReportArtifactId, so a reader can open the underlying research artifact from the visual.
Cost rollup
A child run captures its own provider cost the same way any run does. The parent run’s total is the sum of its own step costs plus the totals of every child it spawned, so a composed workflow reports the full cost of producing its artifact — not just the parent’s steps.
parent run total
= parent's own step costs (llm + image)
+ Σ child run totals (each child's own step costs)Cost estimation mirrors this at planning time: estimating a template that contains a subWorkflow step resolves the pinned child template and adds its estimate to the parent’s, so a budget pre-check sees the true per-run cost before launch.
Sub-workflow composition is guarded against runaway depth and cycles. A template cannot compose itself, directly or transitively, and nesting is capped. This keeps a single operator action from fanning out into an unbounded tree of runs and cost.
Related concepts
- Workflow templates — declare the
subWorkflowstep. - Runs — child runs link to their parent via
parentRunId. - Artifacts — composed artifacts back-reference their source.
- Costs and Budgets — child cost rolls up into the parent and into pre-run estimates.