Skip to main content
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

id
string
required
Unique identifier for the step within the workflow
inputSchema
ZodSchema
required
Zod schema defining the step’s input structure
outputSchema
ZodSchema
required
Zod schema defining the step’s output structure
execute
function
required
Async function that performs the step’s work. Receives execution parameters and returns output matching outputSchema.
description
string
Human-readable description of what the step does
stateSchema
ZodSchema
Zod schema for step-specific state (must be a subset of workflow state)
resumeSchema
ZodSchema
Zod schema for data provided when resuming a suspended step
suspendSchema
ZodSchema
Zod schema for data provided when suspending the step
requestContextSchema
ZodSchema
Zod schema for validating request context values
retries
number
Number of retry attempts if the step fails
metadata
Record<string, any>
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

inputData
T
The validated input data for this step, matching the inputSchema
state
Record<string, any>
The current workflow state, shared across all steps
setState
function
Async function to update the workflow state: await setState(newState)
getStepResult
function
Get the output from a previous step by ID or step reference:
const prevResult = getStepResult('step-id');
const typedResult = getStepResult(myStep); // Type-safe
getInitData
function
Get the original workflow input data
suspend
function
Suspend the workflow for human-in-the-loop or async operations (see Suspend & Resume)
mastra
Mastra
Access to the Mastra instance for integrations, memory, agents, etc.
requestContext
RequestContext
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)

Creating Steps from Tools

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() };
  },
});

Step Metadata

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

Build docs developers (and LLMs) love