Skip to main content

Suspend & Resume

Workflows can be paused at any step to collect additional data, wait for API callbacks, throttle expensive operations, or wait for Human in the Loop input. When a workflow is suspended, its current state can be persisted as a snapshot and later resumed from the same point. Snapshots are stored in the configured storage system and persist across deployments and application restarts.

New to suspend and resume? Watch these official video tutorials:

Workflow status types

When running a workflow, its status can be one of the following:

  • running - The workflow is currently running
  • suspended - The workflow is suspended
  • success - The workflow has completed
  • failed - The workflow has failed

Suspending a workflow with suspend()

To pause execution at a specific step until user input is received, use the ⁠suspend function to temporarily halt the workflow, allowing it to resume only when the necessary data is provided.

Suspending a workflow with suspend()

src/mastra/workflows/test-workflow.ts
const step1 = createStep({
id: "step-1",
inputSchema: z.object({
input: z.string(),
}),
outputSchema: z.object({
output: z.string(),
}),
resumeSchema: z.object({
city: z.string(),
}),
execute: async ({ resumeData, suspend }) => {
const { city } = resumeData ?? {};

if (!city) {
return await suspend({});
}

return { output: "" };
},
});

export const testWorkflow = createWorkflow({
// ...
})
.then(step1)
.commit();

For more details, check out the Suspend workflow example.

Identifying suspended steps

To resume a suspended workflow, inspect the suspended array in the result to determine which step needs input:

src/mastra/workflows/test-workflow.ts
import { mastra } from "./mastra";

const run = await mastra.getWorkflow("testWorkflow").createRunAsync();

const result = await run.start({
inputData: {
city: "London",
},
});

console.log(JSON.stringify(result, null, 2));

if (result.status === "suspended") {
const resumedResult = await run.resume({
step: result.suspended[0],
resumeData: {
city: "Berlin",
},
});
}

In this case, the logic resumes the first step listed in the suspended array. A step can also be defined using it's id, for example: 'step-1'.

{
"status": "suspended",
"steps": {
// ...
"step-1": {
// ...
"status": "suspended"
}
},
"suspended": [["step-1"]]
}

See Workflow Output for more details.

Providing user feedback with suspend

When a workflow is suspended, feedback can be surfaced to the user through the suspendSchema. Include a reason in the suspend payload to explain why the workflow paused.

src/mastra/workflows/test-workflow.ts
import { createWorkflow, createStep } from "@mastra/core/workflows";
import { z } from "zod";

const step1 = createStep({
id: "step-1",
inputSchema: z.object({
value: z.string(),
}),
resumeSchema: z.object({
confirm: z.boolean(),
}),
suspendSchema: z.object({
reason: z.string(),
}),
outputSchema: z.object({
value: z.string(),
}),
execute: async ({ resumeData, suspend }) => {
const { confirm } = resumeData ?? {};

if (!confirm) {
return await suspend({
reason: "Confirm to continue",
});
}

return { value: "" };
},
});

export const testWorkflow = createWorkflow({
// ...
})
.then(step1)
.commit();

In this case, the reason provided explains that the user must confirm to continue.

{
"step-1": {
// ...
"status": "suspended",
"suspendPayload": {
"reason": "Confirm to continue"
}
}
}

See Workflow Output for more details.

Resuming a workflow with resume()

A workflow can be resumed by calling resume and providing the required resumeData. You can either explicitly specify which step to resume from, or when exactly one step is suspended, omit the step parameter and the workflow will automatically resume that step.

src/mastra/workflows/test-workflow.ts
import { mastra } from "./mastra";

const run = await mastra.getWorkflow("testWorkflow").createRunAsync();

const result = await run.start({
inputData: {
city: "London",
},
});

console.log(JSON.stringify(result, null, 2));

if (result.status === "suspended") {
const resumedResult = await run.resume({
step: "step-1",
resumeData: {
city: "Berlin",
},
});

console.log(JSON.stringify(resumedResult, null, 2));
}

You can also omit the step parameter when exactly one step is suspended:

src/mastra/workflows/test-workflow.ts
const resumedResult = await run.resume({
resumeData: {
city: "Berlin",
},
// step parameter omitted - automatically resumes the single suspended step
});

You can pass runtimeContext as an argument to both the start and resume commands.

src/mastra/workflows/test-workflow.ts
import { RuntimeContext } from "@mastra/core/runtime-context";

const runtimeContext = new RuntimeContext();

const result = await run.start({
step: "step-1",
inputData: {
city: "London",
},
runtimeContext,
});

const resumedResult = await run.resume({
step: "step-1",
resumeData: {
city: "New York",
},
runtimeContext,
});

See Runtime Context for more information.

Resuming nested workflows

To resume a suspended nested workflow pass the workflow instance to the step parameter of the resume function.

src/mastra/workflows/test-workflow.ts
const dowhileWorkflow = createWorkflow({
id: "dowhile-workflow",
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
})
.dountil(
createWorkflow({
id: "simple-resume-workflow",
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
steps: [incrementStep, resumeStep],
})
.then(incrementStep)
.then(resumeStep)
.commit(),
async ({ inputData }) => inputData.value >= 10,
)
.then(
createStep({
id: "final",
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
execute: async ({ inputData }) => ({ value: inputData.value }),
}),
)
.commit();

const run = await dowhileWorkflow.createRunAsync();
const result = await run.start({ inputData: { value: 0 } });

if (result.status === "suspended") {
const resumedResult = await run.resume({
resumeData: { value: 2 },
step: ["simple-resume-workflow", "resume"],
});

console.log(JSON.stringify(resumedResult, null, 2));
}

Sleep & Events

Workflows can also pause execution for timed delays or external events. These methods set the workflow status to waiting rather than suspended, and are useful for polling, delayed retries, or event-driven processes.

Available methods: