Can Medusa Workflows Trigger API Routes? Architecture and Implementation Guide

No, Medusa workflows are not designed to trigger API routes; instead, API routes invoke workflows, and workflows interact directly with services, repositories, or other workflows without HTTP overhead.

Medusa, the open-source ecommerce platform, uses a workflow orchestrator defined in the core/workflows-sdk package to handle complex business logic with built-in transaction management. Understanding the directional relationship between API routes and workflows is crucial for building scalable customizations, as the architecture follows a strict pattern where HTTP handlers instantiate and execute workflows, while workflows remain agnostic of the HTTP layer.

How API Routes Invoke Medusa Workflows

In Medusa's architecture, workflows serve as the execution layer for business operations. API routes act as the entry point that prepares input data, resolves the appropriate workflow from the Medusa container, and triggers execution via the run() method.

The standard pattern appears throughout the admin API. In packages/medusa/src/api/admin/orders/route.ts, the GET handler demonstrates this relationship:

// packages/medusa/src/api/admin/orders/route.ts
const workflow = getOrdersListWorkflow(req.scope)
const { result } = await workflow.run({ input: { fields, variables } })

This approach ensures that HTTP concerns (request parsing, authentication, response formatting) remain separated from business logic, which the workflow orchestrates using steps defined in the core-flows package.

Why Workflows Should Not Trigger API Routes

Workflows execute within the server process and have direct access to the dependency injection container, allowing them to resolve any service, repository, or module without HTTP indirection. Attempting to trigger API routes from within a workflow creates several architectural problems:

  • Performance overhead: HTTP requests introduce unnecessary network latency and serialization costs when the code already runs within the same process
  • Transaction boundary violations: Workflows manage distributed transactions and compensation logic; HTTP calls bypass these safeguards and can cause race conditions
  • Circular dependency risks: Routes call workflows; workflows calling routes creates circular references that complicate debugging and testing

According to the Medusa source code in packages/core/workflows-sdk/src/medusa-workflow.ts, the Workflow class is designed to orchestrate steps that interact with the domain layer directly, not to function as an HTTP client.

Exposing Workflows via HTTP: The Generic Execution Endpoint

While workflows should not initiate HTTP requests, Medusa provides a built-in mechanism to trigger workflows externally. The platform ships with a generic workflow execution endpoint at POST /admin/workflows/:workflowId/run, implemented in packages/medusa/src/api/admin/workflow-executions/route.ts.

This endpoint resolves any registered workflow from the container and invokes it:

curl -X POST https://api.medusa.com/admin/workflows/get-orders-list/run \
     -H "Authorization: Bearer <ADMIN_TOKEN>" \
     -d '{"input":{"fields":["id","status"],"variables":{"filters":{}}}}'

The server locates the workflow using the orchestrator manager (packages/core/orchestration/src/workflow/workflow-manager.ts) and executes it within the proper transactional context, returning the result directly to the HTTP client.

Best Practices for Workflow Integration

Follow these architectural guidelines when integrating workflows into your Medusa application:

  • Route-to-Workflow: Always instantiate workflows inside API route handlers using the request scope, then call await workflow.run({ input }) to execute business logic
  • Workflow-to-Service: Access services, repositories, and modules directly from workflow steps using the workflow's container context
  • Workflow-to-Workflow: Compose complex operations by calling other workflows directly, as shown in the order management flows, avoiding HTTP intermediation
  • External Exposure: When external systems need to trigger logic, either use the generic /admin/workflows/:workflowId/run endpoint or create a dedicated API route that invokes your specific workflow

Implementation Examples

Creating an Admin Route that Invokes a Workflow

When building custom admin functionality, import the workflow from core-flows and execute it within your route handler:

// packages/medusa/src/api/admin/price-lists/route.ts
import { createPriceListWorkflow } from "@medusajs/core-flows"

export const POST = async (req, res) => {
  const workflow = createPriceListWorkflow(req.scope)
  const { result } = await workflow.run({ input: req.body })
  res.status(201).json({ price_list: result })
}

This pattern ensures your route remains thin, delegating all business logic to the workflow engine where transaction management and compensation policies apply consistently.

Triggering Workflows via the REST API

For external integrations or third-party systems, use the workflow execution API to run any registered workflow by its identifier:

curl -X POST https://api.medusa.com/admin/workflows/create-order/run \
     -H "Authorization: Bearer <ADMIN_TOKEN>" \
     -H "Content-Type: application/json" \
     -d '{"input":{"customer_id":"cust_123","items":[{"variant_id":"var_456","quantity":1}]}}'

The endpoint automatically resolves the workflow from the container and returns the execution result, making it ideal for webhook handlers and external automation.

Composing Workflows Without HTTP

Workflows can invoke other workflows directly to reuse logic and maintain transaction atomicity across business operations:

// packages/core/core-flows/src/order/workflows/create-order.ts
import { createWorkflow } from "@medusajs/workflows-sdk"
import { getOrdersListWorkflow } from "./get-orders-list"

export const createOrderWorkflow = createWorkflow(
  "create-order",
  (input) => {
    // ...order creation steps...
    // Re-use the list workflow to fetch fresh data without HTTP overhead
    const list = getOrdersListWorkflow(container)
    return list.run({ input: { fields: [], variables: {} } })
  }
)

This composition pattern leverages the workflow orchestrator's ability to manage nested transactions and compensation steps across the entire operation chain.

Key Source Files

Understanding the relationship between workflows and API routes requires familiarity with these specific source locations:

Summary

  • API routes invoke workflows, not the reverse, using workflow.run() after resolving the workflow from the request scope
  • Workflows execute within the server process and should interact directly with services, repositories, and other workflows rather than making HTTP requests
  • Medusa provides a generic execution endpoint (POST /admin/workflows/:workflowId/run) for external systems to trigger workflows via HTTP
  • The workflow orchestrator in packages/core/workflows-sdk manages transaction boundaries and compensation logic, which would be bypassed by HTTP calls from within workflows
  • When exposing new business logic to external callers, create dedicated API routes that invoke workflows rather than embedding logic directly in handlers

Frequently Asked Questions

Can a Medusa workflow make an HTTP request to an internal API route?

No, workflows should never make HTTP requests to internal API routes. Doing so introduces unnecessary network overhead, bypasses the workflow orchestrator's transaction management, and breaks the architectural separation between the HTTP layer and business logic. Workflows have direct access to all services and repositories through the dependency injection container, making HTTP requests redundant.

How do I expose a custom workflow to external systems without creating a new API route?

Use the built-in workflow execution endpoint at POST /admin/workflows/:workflowId/run. This endpoint, implemented in packages/medusa/src/api/admin/workflow-executions/route.ts, allows authorized clients to trigger any registered workflow by passing its ID and input payload. Alternatively, the Medusa Admin Dashboard provides a UI for workflow executions that leverages this same endpoint.

What is the difference between calling a workflow and calling a service directly from an API route?

Calling a workflow provides orchestration benefits including distributed transaction management, automatic compensation on failure, and step-by-step execution tracking that calling a service directly does not offer. While direct service calls are appropriate for simple reads, workflows in packages/core/core-flows wrap complex operations (like order creation or inventory management) that span multiple domains and require atomic consistency.

Where is the workflow orchestrator defined in the Medusa source code?

The workflow orchestrator is defined in packages/core/workflows-sdk/src/medusa-workflow.ts, which exports the createWorkflow function and Workflow class used throughout the system. Workflow registration and resolution are handled by the WorkflowManager in packages/core/orchestration/src/workflow/workflow-manager.ts, which maintains the registry of all available workflows for both API route handlers and internal service calls.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →