Steps are the fundamental building blocks of Mastra workflows. Each step is a unit of work with defined inputs, outputs, and an execute function.
Creating Steps
Use createStep to define a workflow step:
import { createStep } from '@mastra/core';
import { z } from 'zod';
const processDataStep = createStep({
id: 'process-data',
description: 'Processes user data and returns results',
inputSchema: z.object({
userId: z.string(),
data: z.array(z.string()),
}),
outputSchema: z.object({
processedCount: z.number(),
results: z.array(z.string()),
}),
execute: async ({ inputData }) => {
const results = inputData.data.map(item => item.toUpperCase());
return {
processedCount: results.length,
results,
};
},
});
Step Configuration
Unique identifier for the step within the workflow
Zod schema defining the step’s input structure
Zod schema defining the step’s output structure
Async function that performs the step’s work. Receives execution parameters and returns output matching outputSchema.
Human-readable description of what the step does
Zod schema for step-specific state (must be a subset of workflow state)
Zod schema for data provided when resuming a suspended step
Zod schema for data provided when suspending the step
Zod schema for validating request context values
Number of retry attempts if the step fails
Custom metadata attached to the step
Execute Function Parameters
The execute function receives a comprehensive context object:
const step = createStep({
id: 'my-step',
inputSchema: z.object({ input: z.string() }),
outputSchema: z.object({ output: z.string() }),
execute: async ({
inputData, // Validated input matching inputSchema
state, // Shared workflow state
setState, // Function to update workflow state
runId, // Unique workflow run ID
workflowId, // Workflow identifier
mastra, // Mastra instance
requestContext, // Request-scoped context
getStepResult, // Get output from previous steps
getInitData, // Get workflow input data
suspend, // Suspend the workflow
resumeData, // Data from resume (if applicable)
abort, // Abort execution
retryCount, // Current retry attempt
abortSignal, // AbortSignal for cancellation
writer, // Stream writer for custom events
}) => {
// Step logic here
return { output: 'result' };
},
});
Key Parameters
The validated input data for this step, matching the inputSchema
The current workflow state, shared across all steps
Async function to update the workflow state: await setState(newState)
Get the output from a previous step by ID or step reference:const prevResult = getStepResult('step-id');
const typedResult = getStepResult(myStep); // Type-safe
Get the original workflow input data
Suspend the workflow for human-in-the-loop or async operations (see Suspend & Resume)
Access to the Mastra instance for integrations, memory, agents, etc.
Request-scoped context for accessing user/tenant data:const userId = requestContext.get('userId');
Creating Steps from Agents
You can create steps directly from Mastra agents:
import { Agent, createStep } from '@mastra/core';
import { openai } from '@ai-sdk/openai';
const myAgent = new Agent({
id: 'summarizer',
model: openai('gpt-4'),
instructions: 'Summarize the provided text concisely.',
});
// Create a step from the agent
const summarizeStep = createStep(myAgent);
// With structured output
const structuredStep = createStep(myAgent, {
structuredOutput: {
schema: z.object({
summary: z.string(),
keyPoints: z.array(z.string()),
}),
},
});
Agent steps automatically have:
- Input schema:
{ prompt: string }
- Output schema:
{ text: string } (or your structured output schema)
Mastra tools can be converted into workflow steps:
import { Tool, createStep } from '@mastra/core';
const fetchDataTool = new Tool({
id: 'fetch-user-data',
description: 'Fetches user data from API',
inputSchema: z.object({ userId: z.string() }),
outputSchema: z.object({
name: z.string(),
email: z.string(),
}),
execute: async ({ context, inputData }) => {
const response = await fetch(`/api/users/${inputData.userId}`);
return response.json();
},
});
const fetchStep = createStep(fetchDataTool);
Accessing Previous Step Results
Steps can access outputs from any previous step:
const step1 = createStep({
id: 'fetch-data',
inputSchema: z.object({ id: z.string() }),
outputSchema: z.object({ data: z.string() }),
execute: async ({ inputData }) => {
return { data: `fetched-${inputData.id}` };
},
});
const step2 = createStep({
id: 'process-data',
inputSchema: z.object({ data: z.string() }),
outputSchema: z.object({ result: z.string() }),
execute: async ({ inputData, getStepResult }) => {
// Get result from step1
const fetchedData = getStepResult('fetch-data');
// Or use type-safe reference
const typedData = getStepResult(step1);
return { result: `processed-${inputData.data}` };
},
});
State Management in Steps
Steps can read and update workflow state:
const counterStep = createStep({
id: 'increment-counter',
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ newCount: z.number() }),
execute: async ({ inputData, state, setState }) => {
const currentCount = (state.counter || 0) as number;
const newCount = currentCount + inputData.value;
// Update state
await setState({
...state,
counter: newCount,
lastUpdated: new Date().toISOString(),
});
return { newCount };
},
});
Error Handling and Retries
Steps support automatic retries on failure:
const apiStep = createStep({
id: 'call-external-api',
inputSchema: z.object({ endpoint: z.string() }),
outputSchema: z.object({ data: z.any() }),
retries: 3, // Retry up to 3 times on failure
execute: async ({ inputData, retryCount }) => {
console.log(`Attempt ${retryCount + 1}`);
const response = await fetch(inputData.endpoint);
if (!response.ok) {
throw new Error(`API call failed: ${response.status}`);
}
return { data: await response.json() };
},
});
Attach custom metadata to steps for tracking and observability:
const step = createStep({
id: 'my-step',
inputSchema: z.object({ input: z.string() }),
outputSchema: z.object({ output: z.string() }),
metadata: {
category: 'data-processing',
owner: 'data-team',
version: '1.0.0',
},
execute: async ({ inputData }) => {
return { output: inputData.input };
},
});
Next Steps
Control Flow
Learn about .then(), .branch(), and .parallel()
Suspend & Resume
Implement human-in-the-loop workflows