Skip to Content
DocumentationRegistryExamplesSub-Workflow Example

@loopstack/run-sub-workflow-example

A module for the Loopstack AI  automation framework.

This module provides an example demonstrating how to execute child workflows from within a parent workflow for hierarchical workflow composition.

Overview

The Run Sub Workflow Example shows how to build workflows that spawn and manage child workflows asynchronously. It demonstrates a parent workflow that starts sub-workflows, tracks their completion through a callback mechanism, and receives result data from the child workflows.

By using this example as a reference, you’ll learn how to:

  • Use constructor injection and .run() to start child workflows asynchronously
  • Set up callback transitions to handle sub-workflow completion
  • Define callback schemas with CallbackSchema.extend() to type callback payloads
  • Return structured data from sub-workflows via the return value of the final @Transition method
  • Control how a child appears in the parent’s run view via the show option ('inline', 'link', 'hidden')
  • Compose complex workflows from smaller, reusable workflow components
  • Chain multiple sequential sub-workflow executions

This example is essential for developers building workflows that need to orchestrate multiple workflow executions or break down complex processes into manageable sub-workflows.

Installation

npm install @loopstack/run-sub-workflow-example

Then register the module in your app:

import { StudioApp } from '@loopstack/common'; import { RunSubWorkflowExampleModule, RunSubWorkflowExampleParentWorkflow } from '@loopstack/run-sub-workflow-example'; @StudioApp({ title: 'Sub-Workflow Example', workflows: [RunSubWorkflowExampleParentWorkflow], }) @Module({ imports: [RunSubWorkflowExampleModule], }) export class MyAppModule {}

How It Works

Workflows

Parent Workflow

The parent workflow injects the sub-workflow class and calls .run() to start it:

@Workflow({ uiConfig: __dirname + '/run-sub-workflow-example-parent.ui.yaml' }) export class RunSubWorkflowExampleParentWorkflow extends BaseWorkflow { constructor(private readonly subWorkflow: RunSubWorkflowExampleSubWorkflow) { super(); } }

Sub-Workflow

The sub-workflow returns structured data from its final @Transition method. This data is delivered to the parent workflow via the callback payload:

@Workflow({ uiConfig: __dirname + '/run-sub-workflow-example-sub.ui.yaml' }) export class RunSubWorkflowExampleSubWorkflow extends BaseWorkflow { @Transition({ to: 'end' }) async message(): Promise<{ message: string }> { await this.documentStore.save(MessageDocument, { role: 'assistant', text: 'Sub workflow completed.', }); return { message: 'Hi mom!' }; } }

Key Concepts

1. Running a Sub-Workflow with Callbacks

Use .run() on the injected workflow to start it asynchronously. Pass a callback option specifying which transition to trigger when the sub-workflow completes, and a show option to control how the child appears in the parent’s run view:

@Transition({ to: 'sub_workflow_started' }) async runWorkflow() { await this.subWorkflow.run( {}, { callback: { transition: 'subWorkflowCallback' }, show: 'link', label: 'Sub-Workflow' }, ); }

2. The show Option

show controls how the child sub-workflow is rendered inside the parent’s run view:

  • 'inline' (default) — embed the child as an inline iframe in the parent’s view. Best for HITL/OAuth/interactive children.
  • 'link' — show a status link card; clicking opens the child in a separate window. Best for autonomous children the parent just tracks.
  • 'hidden' — no UI at all. Best for background fan-out.

This example uses show: 'link' because the sub-workflows run autonomously without needing user interaction.

3. Defining Callback Schemas

Extend CallbackSchema to type the callback payload. The base schema includes workflowId, and you add your custom data shape:

const SubWorkflowCallbackSchema = CallbackSchema.extend({ data: z.object({ message: z.string() }), }); type SubWorkflowCallback = z.infer<typeof SubWorkflowCallbackSchema>;

4. Handling the Callback

Define a transition with wait: true and the callback schema. The payload contains both the workflowId and the data returned by the sub-workflow:

@Transition({ from: 'sub_workflow_started', to: 'sub_workflow_ended', wait: true, schema: SubWorkflowCallbackSchema }) async subWorkflowCallback(payload: SubWorkflowCallback) { await this.documentStore.save(MessageDocument, { role: 'assistant', text: `A message from sub workflow 1: ${payload.data.message}`, }); }

5. Chaining Multiple Sub-Workflows

The parent workflow demonstrates running two sub-workflows sequentially. After the first callback completes, a second sub-workflow is started with its own callback. The second callback uses a terminal @Transition to end the parent workflow:

@Transition({ from: 'sub_workflow_ended', to: 'sub_workflow2_started' }) async runWorkflow2() { await this.subWorkflow.run( {}, { callback: { transition: 'subWorkflow2Callback' }, show: 'link', label: 'Sub-Workflow 2' }, ); } @Transition({ from: 'sub_workflow2_started', to: 'end', wait: true, schema: SubWorkflowCallbackSchema }) async subWorkflow2Callback(payload: SubWorkflowCallback) { await this.documentStore.save(MessageDocument, { role: 'assistant', text: `A message from sub workflow 2: ${payload.data.message}`, }); }

Dependencies

This workflow uses the following Loopstack modules:

  • @loopstack/common - Core workflow/runtime APIs (BaseWorkflow, @Workflow, @Transition, CallbackSchema, MessageDocument)

About

Author: Jakob Klippel 

License: MIT

Additional Resources

Last updated on