> ## Documentation Index
> Fetch the complete documentation index at: https://openworkflow.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Child Workflows

> Run workflows from other workflows and wait for their results

A parent workflow can start a child workflow and durably wait for its result
using `step.runWorkflow()`. This lets you compose complex processes from
smaller, reusable workflows — like splitting an order pipeline into separate
payment and shipping workflows.

The parent sleeps while the child runs, freeing its worker slot. When the child
finishes, the parent automatically resumes and receives the child's output.

## Basic Usage

```ts theme={null}
import { defineWorkflow } from "openworkflow";

const generateReport = defineWorkflow(
  { name: "generate-report" },
  async ({ input, step }) => {
    const data = await step.run({ name: "fetch-data" }, async () => {
      return await db.reports.getData(input.reportId);
    });

    await step.run({ name: "upload-report" }, async () => {
      await storage.upload(`reports/${input.reportId}.pdf`, data);
    });

    return { reportUrl: `https://example.com/reports/${input.reportId}.pdf` };
  },
);

const processOrder = defineWorkflow(
  { name: "process-order" },
  async ({ input, step }) => {
    await step.run({ name: "charge-card" }, async () => {
      await payments.charge(input.orderId);
    });

    // Start the report workflow and wait for it to finish
    const report = await step.runWorkflow(generateReport.spec, {
      reportId: input.orderId,
    });

    await step.run({ name: "send-confirmation" }, async () => {
      await email.send({
        to: input.email,
        subject: "Order complete",
        body: `Your report is ready: ${report.reportUrl}`,
      });
    });
  },
);
```

## Timeout

By default, the parent waits up to **1 year** for the child to finish. You can
customize this with `options.timeout`:

```ts theme={null}
const result = await step.runWorkflow(
  quickTaskWorkflow.spec,
  { taskId: "abc" },
  { timeout: "5m" }, // wait at most 5 minutes
);
```

`timeout` accepts a [duration string](/docs/sleeping#duration-formats), a number
of milliseconds, or a `Date`:

```ts theme={null}
// Duration string
await step.runWorkflow(w.spec, undefined, { timeout: "1h" });

// Milliseconds
await step.runWorkflow(w.spec, undefined, { timeout: 60_000 });

// Absolute deadline
await step.runWorkflow(w.spec, undefined, {
  timeout: new Date("2026-03-01"),
});
```

<Warning>
  When the timeout is reached, the **parent step fails** but the **child
  workflow keeps running** independently. The child is not automatically
  canceled.
</Warning>

## Workflow Spec

The first argument accepts a workflow spec:

```ts theme={null}
// From a defined workflow
await step.runWorkflow(myWorkflow.spec, { key: "value" });

// Or any WorkflowSpec-compatible object
await step.runWorkflow({ name: "my-workflow" }, { key: "value" });
```

## Step Name

Set `options.name` to control the durable step name. If omitted, OpenWorkflow
uses the target workflow name.

## Error Handling

If the child workflow **fails**, the parent workflow step also fails:

```ts theme={null}
const orderPipeline = defineWorkflow(
  { name: "order-pipeline" },
  async ({ input, step }) => {
    try {
      const result = await step.runWorkflow(chargeWorkflow.spec, {
        orderId: input.orderId,
      });
      return result;
    } catch (error) {
      // Handle child failure in the parent
      await step.run({ name: "notify-failure" }, async () => {
        await alerts.send(`Payment failed for order ${input.orderId}`);
      });
    }
  },
);
```

If the child workflow is **canceled**, the parent workflow step fails with an
error indicating the child was canceled.

<Note>
  Workflow steps do not retry automatically. The child workflow is responsible
  for its own retries. If the child fails permanently, the error propagates to
  the parent.
</Note>

## Parallel Child Workflows

Start multiple child workflows concurrently with `Promise.all`:

```ts theme={null}
const [payment, shipping] = await Promise.all([
  step.runWorkflow(paymentWorkflow.spec, {
    orderId: input.orderId,
  }),
  step.runWorkflow(shippingWorkflow.spec, {
    orderId: input.orderId,
  }),
]);
```
