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

# Create Workflows

> Learn how to create workflows in Medusa using createWorkflow and createStep.

Workflows are composable, transactional business processes in Medusa. They allow you to build complex operations from reusable steps with automatic compensation (rollback) on failure.

## What is a Workflow?

A workflow is:

* A sequence of steps that execute in order
* Transactional - all steps succeed or all are rolled back
* Composable - workflows can call other workflows
* Asynchronous - steps can run in the background
* Type-safe - fully typed inputs and outputs

## Creating Your First Workflow

<Steps>
  <Step title="Create a Step">
    Steps are the building blocks of workflows. Each step has an invocation function and optional compensation function:

    ```typescript title="src/workflows/brand/steps/create-brand.ts" theme={null}
    import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
    import { IBrandModuleService } from "../../../modules/brand/types"
    import { BRAND_MODULE } from "../../../modules/brand"

    export interface CreateBrandStepInput {
      name: string
      description?: string
    }

    export const createBrandStep = createStep(
      "create-brand",
      async (input: CreateBrandStepInput, { container }) => {
        const brandService = container.resolve<IBrandModuleService>(
          BRAND_MODULE
        )

        const brand = await brandService.createBrands(input)

        return new StepResponse(
          brand,
          { brandId: brand.id } // Compensation input
        )
      },
      async (compensationData, { container }) => {
        if (!compensationData) {
          return
        }

        const brandService = container.resolve<IBrandModuleService>(
          BRAND_MODULE
        )

        await brandService.deleteBrands([compensationData.brandId])
      }
    )
    ```

    The `StepResponse` takes two arguments:

    1. The output of the step (returned to the workflow)
    2. Data to pass to the compensation function if rollback is needed
  </Step>

  <Step title="Create the Workflow">
    Compose steps into a workflow using `createWorkflow`:

    ```typescript title="src/workflows/brand/create-brand.ts" theme={null}
    import {
      createWorkflow,
      WorkflowData,
      WorkflowResponse,
    } from "@medusajs/framework/workflows-sdk"
    import { createBrandStep } from "./steps/create-brand"

    export interface CreateBrandWorkflowInput {
      name: string
      description?: string
    }

    export const createBrandWorkflow = createWorkflow(
      "create-brand",
      (input: WorkflowData<CreateBrandWorkflowInput>) => {
        const brand = createBrandStep(input)

        return new WorkflowResponse(brand)
      }
    )
    ```
  </Step>

  <Step title="Use the Workflow">
    Execute the workflow from an API route or another workflow:

    ```typescript title="src/api/admin/brands/route.ts" theme={null}
    import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
    import { createBrandWorkflow } from "../../../workflows/brand/create-brand"

    export async function POST(
      req: MedusaRequest,
      res: MedusaResponse
    ) {
      const { result: brand } = await createBrandWorkflow(req.scope).run({
        input: req.body,
      })

      res.json({ brand })
    }
    ```
  </Step>
</Steps>

## Advanced Workflow Patterns

### Multiple Steps with Compensation

Here's a complete example showing multiple steps with compensation:

```typescript title="src/workflows/brand/steps/delete-brand.ts" theme={null}
import type { IBrandModuleService } from "../../../modules/brand/types"
import { BRAND_MODULE } from "../../../modules/brand"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"

export const deleteBrandStep = createStep(
  "delete-brand",
  async (ids: string[], { container }) => {
    const brandService = container.resolve<IBrandModuleService>(BRAND_MODULE)

    await brandService.softDeleteBrands(ids)

    return new StepResponse(void 0, ids)
  },
  async (idsToRestore, { container }) => {
    if (!idsToRestore?.length) {
      return
    }

    const brandService = container.resolve<IBrandModuleService>(BRAND_MODULE)

    await brandService.restoreBrands(idsToRestore)
  }
)
```

```typescript title="src/workflows/brand/delete-brand.ts" theme={null}
import {
  createHook,
  createWorkflow,
  WorkflowData,
  WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { deleteBrandStep } from "./steps/delete-brand"

export interface DeleteBrandWorkflowInput {
  ids: string[]
}

export const deleteBrandWorkflow = createWorkflow(
  "delete-brand",
  (input: WorkflowData<DeleteBrandWorkflowInput>) => {
    const deletedBrands = deleteBrandStep(input.ids)
    
    const brandsDeleted = createHook("brandsDeleted", {
      ids: input.ids,
    })

    return new WorkflowResponse(deletedBrands, {
      hooks: [brandsDeleted],
    })
  }
)
```

### Transform Data Between Steps

Use `transform` to manipulate data between steps:

```typescript theme={null}
import { transform } from "@medusajs/framework/workflows-sdk"

export const updateBrandWorkflow = createWorkflow(
  "update-brand",
  (input: WorkflowData<UpdateBrandInput>) => {
    const brand = getBrandStep(input.id)

    const updateData = transform(
      { brand, input },
      ({ brand, input }) => ({
        id: brand.id,
        name: input.name ?? brand.name,
        description: input.description ?? brand.description,
      })
    )

    const updatedBrand = updateBrandStep(updateData)

    return new WorkflowResponse(updatedBrand)
  }
)
```

### Conditional Execution

Use `when` to conditionally execute steps:

```typescript theme={null}
import { when } from "@medusajs/framework/workflows-sdk"

export const processBrandWorkflow = createWorkflow(
  "process-brand",
  (input: WorkflowData<ProcessBrandInput>) => {
    const brand = createBrandStep(input)

    when({ brand }, ({ brand }) => {
      return brand.logo_url === undefined
    }).then(() => {
      const defaultLogo = assignDefaultLogoStep(brand.id)
    })

    return new WorkflowResponse(brand)
  }
)
```

### Parallel Execution

Use `parallelize` to run independent steps concurrently:

```typescript theme={null}
import { parallelize } from "@medusajs/framework/workflows-sdk"

export const setupBrandWorkflow = createWorkflow(
  "setup-brand",
  (input: WorkflowData<SetupBrandInput>) => {
    const brand = createBrandStep(input.brand)

    const [products, collections] = parallelize(
      createProductsStep(input.products),
      createCollectionsStep(input.collections)
    )

    return new WorkflowResponse({ brand, products, collections })
  }
)
```

### Query Data in Workflows

Use `useQueryGraphStep` to fetch data:

```typescript theme={null}
import { useQueryGraphStep } from "@medusajs/framework/workflows-sdk"

export const getBrandDetailWorkflow = createWorkflow(
  "get-brand-detail",
  (input: WorkflowData<{ id: string; fields: string[] }>) => {
    const { data: brands } = useQueryGraphStep({
      entity: "brand",
      filters: { id: input.id },
      fields: input.fields,
    })

    const brand = transform({ brands }, ({ brands }) => brands[0])

    return new WorkflowResponse(brand)
  }
)
```

## Step Context

The step context provides access to:

* `container` - Dependency injection container
* `context` - Shared context across steps
* `metadata` - Additional metadata
* `idempotencyKey` - Unique key for idempotent execution

```typescript theme={null}
export const myStep = createStep(
  "my-step",
  async (input, { container, context, metadata }) => {
    const logger = container.resolve("logger")
    logger.info("Executing step", { context, metadata })

    // Your logic here
  }
)
```

## Error Handling

Workflows automatically handle errors and trigger compensation:

```typescript theme={null}
import { MedusaError } from "@medusajs/framework/utils"

export const validateBrandStep = createStep(
  "validate-brand",
  async (input: { name: string }) => {
    if (!input.name || input.name.length < 2) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        "Brand name must be at least 2 characters"
      )
    }

    return new StepResponse({ valid: true })
  }
)
```

When a step throws an error, all previously executed steps run their compensation functions in reverse order.

## Workflow Hooks

Hooks allow you to emit events when a workflow completes:

```typescript theme={null}
import { createHook } from "@medusajs/framework/workflows-sdk"

export const createBrandWorkflow = createWorkflow(
  "create-brand",
  (input: WorkflowData<CreateBrandInput>) => {
    const brand = createBrandStep(input)

    const brandCreated = createHook("brandCreated", {
      id: brand.id,
    })

    return new WorkflowResponse(brand, {
      hooks: [brandCreated],
    })
  }
)
```

Subscribe to hooks in subscribers (see [Event Subscribers](/development/subscribers)).

## Best Practices

* Keep steps focused on a single responsibility
* Always provide compensation functions for steps that modify data
* Use `transform` for data manipulation instead of complex logic in steps
* Name steps and workflows descriptively
* Type your inputs and outputs for type safety
* Use `parallelize` for independent operations
* Handle errors with proper MedusaError types
* Test workflows in isolation

## Next Steps

<CardGroup cols={2}>
  <Card title="Create API Routes" icon="route" href="/development/api-routes">
    Build HTTP endpoints that use workflows
  </Card>

  <Card title="Event Subscribers" icon="bell" href="/development/subscribers">
    React to workflow events and hooks
  </Card>
</CardGroup>
