# Workflows

Legacy workflow features have been removed.

## Changed

### `getWorkflows` to `listWorkflows`

The `mastra.getWorkflows()` method has been renamed to `mastra.listWorkflows()`. This change aligns with the naming convention used across the API where plural getter methods use the `list` prefix.

To migrate, replace all calls to `mastra.getWorkflows()` with `mastra.listWorkflows()`.

```diff
- const workflows = mastra.getWorkflows();
+ const workflows = mastra.listWorkflows();
```

> **Codemod:** You can use Mastra's codemod CLI to update your imports automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/mastra-plural-apis .
> ```

### `RuntimeContext` to `RequestContext` in step context

The parameter name `runtimeContext` has been changed to `requestContext` in workflow step execution context. This change aligns with the global rename for clarity.

To migrate, update references from `runtimeContext` to `requestContext` in step execution functions.

```diff
createStep({
-   execute: async ({ runtimeContext } ) => {
-     const userTier = context.runtimeContext.get('userTier');
+   execute: async ({ requestContext } ) => {
+     const userTier = requestContext.get('userTier');
      return { result: userTier };
    },
  });
```

> **Codemod:** You can use Mastra's codemod CLI to update your imports automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/runtime-context .
> ```

### `createRunAsync` to `createRun`

The `createRunAsync()` method has been renamed to `createRun()`. This change simplifies the API by removing the redundant "Async" suffix since all run creation is asynchronous.

To migrate, rename method calls from `createRunAsync` to `createRun`.

```diff
- await workflow.createRunAsync({ input: { ... } });
+ await workflow.createRun({ input: { ... } });
```

> **Codemod:** You can use Mastra's codemod CLI to update your code automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/workflow-create-run-async .
> ```

### `runCount` to `retryCount` (deprecated)

The `runCount` parameter has been deprecated in favor of `retryCount` in workflow step execution. This change provides clearer naming that better describes retry behavior. The old `runCount` still works but shows deprecation warnings.

To migrate, rename `runCount` to `retryCount` in step execution functions.

```diff
createStep({
    execute: async (inputData, context) => {
-     console.log(`Step run ${context.runCount} times`);
+     console.log(`Step retry count: ${context.retryCount}`);
    },
  });
```

> **Codemod:** You can use Mastra's codemod CLI to update your code automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/workflow-run-count .
> ```

### `getInitData` returns unknown

The `getInitData` function in the execute function now returns unknown instead of any. You'll need to type it yourself. To migrate, change `getInitData()` into `getInitData<any>()`

```diff
createStep({
    execute: async ({ getInitData }) => {
-     const initData = getInitData();
-     if (initData.key === 'value') {}
+     const initData = getInitData<any>();
+     if (initData.key === 'value') {}
    },
  });
```

> **Codemod:** You can use Mastra's codemod CLI to update your code automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/workflow-get-init-data .
> ```

### `getWorkflowRuns` to `listWorkflowRuns`

The `getWorkflowRuns()` method has been renamed to `listWorkflowRuns()`. This change aligns with the convention that `list*` methods return collections.

To migrate, rename method calls from `getWorkflowRuns` to `listWorkflowRuns`.

```diff
- const runs = await workflow.getWorkflowRuns({ fromDate, toDate });
+ const runs = await workflow.listWorkflowRuns({ fromDate, toDate });
```

> **Codemod:** You can use Mastra's codemod CLI to update your code automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/workflow-list-runs .
> ```

### Inputs are validated by default

Previously, inputs weren't validated by default. The [`validateInputs`](https://mastra.ai/reference/workflows/workflow) flag determines whether to validate the workflow inputs or not. This boolean has been flipped to `true`. If you want the old behavior or have workflows whose schemas don't need to be validated, set `validateInputs: false`.

```diff
createWorkflow({
+  options: {
+    validateInputs: false
+  }
})
```

### Step `suspendPayload` validation

Step `suspendPayload` is now validated for steps that have a `suspendSchema` defined. This also uses the `validateInputs` flag to determine whether to validate the `suspendPayload` or not.

```diff
createStep({
    id: "suspend-resume-step",
    // ... other step properties
    suspendSchema: z.object({
      reason: z.string(),
      otherReason: z.string()
    }),
    execute: async ({ suspend, resumeData}) => {
      if (!resumeData) {
-       return suspend({ reason: "Suspension reason" }); // Missing otherReason
+       return suspend({ reason: "Suspension reason", otherReason: "Other reason" });
      }
    },
  });
```

### Branch result fields are now optional

The `.branch()` method now returns a schema where all branch output fields are optional. This reflects the runtime behavior where each branch only executes if its condition is truthy, so outputs from any branch may be undefined.

To migrate, update any code that consumes branch outputs to handle optional values.

```diff
const workflow = createWorkflow({...})
    .branch([
      [condition1, stepA],  // outputSchema: { result: z.string() }
      [condition2, stepB],  // outputSchema: { data: z.number() }
    ])
-   // Previously: stepA.result typed as string, stepB.data typed as number
+   // Now: stepA.result typed as string | undefined, stepB.data typed as number | undefined
    .then(nextStep);
```

If your code depends on non-optional types, add runtime checks or provide default values when accessing branch outputs.

### `writableStream` to `outputWriter` in `Run.start()` & `Run.timeTravel()`

The `writableStream` parameter in `Run.start()` and `Run.timeTravel()` has been replaced with `outputWriter`. Instead of passing a `WritableStream`, you now pass an async callback function that receives each workflow event chunk directly.

This change simplifies the API - rather than creating a `WritableStream` wrapper, you handle chunks directly in the callback.

**Example:** Streaming workflow events to an HTTP response (SSE):

```diff
const run = await workflow.createRun();

- const stream = new WritableStream({
-   write(chunk) {
-     response.write(`data: ${JSON.stringify(chunk)}\n\n`);
-   }
- });
- await run.start({ inputData, writableStream: stream });

+ await run.start({
+   inputData,
+   outputWriter: async (chunk) => {
+     response.write(`data: ${JSON.stringify(chunk)}\n\n`);
+   },
+ });
```

> **Note:** The `writer` parameter passed to step `execute` functions isn't affected by this change. It remains a `ToolStream` that extends `WritableStream<unknown>` and provides `.write()` and `.custom()` methods:
>
> ```ts
> createStep({
>   id: 'my-step',
>   execute: async ({ writer }) => {
>     // This API is unchanged
>     await writer.write({ data: 'some output' })
>     await writer.custom({ type: 'custom-event', payload: {} })
>   },
> })
> ```

### `setState()` is now async and the data passed is validated

The `setState()` function is now async. The data passed is now validated against the `stateSchema` defined in the step. The state data validation also uses the `validateInputs` flag to determine whether to validate the state data or not. Also, when calling `setState()`, you can now pass only the state data being updated, instead of adding the previous state spread `(...state)`.

To migrate, update the `setState()` function to be async.

```diff
- setState({ ...state, sharedCounter: state.sharedCounter + 1 });
+ await setState({ sharedCounter: state.sharedCounter + 1 });
+ // await setState({ ...state, sharedCounter: state.sharedCounter + 1 });
+ // this also works, as the previous state spread remains supported
```

## Removed

### `streamVNext`, `resumeStreamVNext`, and `observeStreamVNext` methods

The experimental `streamVNext()`, `resumeStreamVNext()`, and `observeStreamVNext()` methods have been removed. These methods are now the standard implementation with updated event structures and return types.

To migrate, use the standard `stream()`, `resumeStream()`, and `observeStream()` methods. Update event type checks to use workflow-prefixed names and access stream properties directly.

See [`Run.stream()`](https://mastra.ai/reference/streaming/workflows/stream), [`Run.resumeStream()`](https://mastra.ai/reference/streaming/workflows/resumeStream), and [`Run.observeStream()`](https://mastra.ai/reference/streaming/workflows/observeStream) for details.

> **Codemod:** You can use Mastra's codemod CLI to update your code automatically:
>
> ```bash
> npx @mastra/codemod@latest v1/workflow-stream-vnext .
> ```

### `suspend()` and `setState()` aren't available in step condition functions parameters

The `suspend()` and `setState()` functions aren't available in step condition function parameters.

To migrate, use the `suspend()` function in the step execute function instead.

```diff
.dowhile(step, async ({ suspend, state, setState }) => {
- setState({...state, updatedState: "updated state"})
- await suspend({ reason: "Suspension reason" });
+ // Use the suspend/setState in the step execute function instead
});
```

This is the same for `dountil` and `branch` condition functions parameters.

### Legacy workflows export

The `./workflows/legacy` export path has been removed from `@mastra/core`. Legacy workflows are no longer supported.

To migrate, use the new workflow API. No direct migration path exists from legacy workflows.

```diff
- import { LegacyWorkflow } from '@mastra/core/workflows/legacy';
+ // Legacy workflows are no longer supported
+ // Migrate to the new workflow API
```

### `pipeThrough` and `pipeTo` methods from `WorkflowRunOutput`

The `pipeThrough()` and `pipeTo()` methods on `WorkflowRunOutput` are deprecated. These methods still work but show console warnings.

To migrate, use the `fullStream` property instead of calling methods directly on the run output.

```diff
const run = await workflow.createRun({ input: { ... } });
- await run.pipeTo(writableStream);
- const transformed = run.pipeThrough(transformStream);
+ await run.fullStream.pipeTo(writableStream);
+ const transformed = run.fullStream.pipeThrough(transformStream);
```

### Watch events API

Legacy watch events have been removed and consolidated on the v2 events API. The `watch()` method and related watch endpoints are no longer available.

To migrate, use the workflow events API or streaming instead of watch events.

```diff
- const workflow = mastraClient.getWorkflow('my-workflow');
- const run = await workflow.createRun();
- await run.watch((event) => {
-   console.log('Step completed:', event);
- });

+ const workflow = mastraClient.getWorkflow('my-workflow');
+ const run = await workflow.createRun();
+ const stream = await run.stream({ inputData: { ... } });
+ for await (const chunk of stream) {
+   console.log('Step completed:', chunk);
+ }
```

### `waitForEvent` API

The `waitForEvent` API has been removed from workflows. Use the suspend/resume API instead.

To migrate, use suspend/resume API for waiting on workflow execution milestones.

```diff
- workflow.waitForEvent('step-complete', step1).commit();
+ workflow.then(step1).commit();
+ // Use suspend/resume API instead, in step1 execute function
createStep({
- execute: async (inputData, context) => {
-  // ... execution logic
- }
+ execute: async (inputData, context) => {
+   if (!context.resumeData) {
+     return context.suspend({})
+   }
+ }
});
+
+ // after workflow is suspended, you can resume it
+ const result = await run.start({ inputData: { ... } });
+ if (result.status === 'suspended') {
+   const resumedResult = await run.resume({
+     resumeData: {
+       event: 'step-complete',
+     },
+     step: 'step1',
+   });
+ }
```

### `sendEvent` API

The `sendEvent` API has been removed from workflows. Use the suspend/resume API instead.