Restrict Return Creation

In this guide, you'll learn how Medusa handles access to the Create Return API route, and how to restrict access to it in your Medusa application.

How Medusa Handles Return Creation#

The POST /store/returns API route doesn't require customer authentication. Any request that includes a valid publishable API key and a correct order ID creates a return for that order.

Medusa applies this behavior intentionally. Guest customers place orders without an account, so they have no session or token to authenticate with. They still need a way to request a return for the items they received. Requiring authentication would leave guest customers without a self-service return flow.

The order's ID acts as the credential in this flow. Medusa generates order IDs randomly, so guessing an ID requires brute forcing a value from an address space large enough to make the attempt impractical.

Note: Medusa doesn't expose a store API route to retrieve or list returns. Customers only create returns, while merchants manage them from the Medusa Admin dashboard.

The receive_now Request Body Parameter#

The route accepts a receive_now parameter in the request body. When it's enabled, Medusa marks the return's items as received as soon as it creates the return, without waiting for the merchant to receive them.

This is useful in stores that trust the customer's return request, such as digital goods or low-value items. If your store reviews returns before receiving them, reject the parameter as explained in the Reject the receive_now Parameter section.


Restrict Access to the Route#

If your store doesn't allow guest checkout, you may want stricter access rules. You can add access rules with middlewares. Middlewares that you apply to an existing API route run in addition to the route's original middlewares, so you don't have to replicate the route.

For example, add the authenticate middleware to the route:

src/api/middlewares.ts
1import {2  defineMiddlewares,3  authenticate,4} from "@medusajs/framework/http"5
6export default defineMiddlewares({7  routes: [8    {9      matcher: "/store/returns",10      method: ["POST"],11      middlewares: [12        authenticate("customer", ["session", "bearer"]),13      ],14    },15  ],16})

A request without an authenticated customer now receives a 401 error, while a logged-in customer still creates the return.

Note: This middleware only checks that a customer is authenticated. It doesn't check that the customer owns the order, so any logged-in customer can create a return for any order. Refer to the next section to also check ownership.

Restrict the Route to the Order's Customer#

To allow only the customer that placed the order to create a return for it, add a custom middleware that compares the authenticated customer's ID to the order's customer_id.

Create the file src/api/middlewares/ensure-return-order-owner.ts with the following content:

src/api/middlewares/ensure-return-order-owner.ts
1import {2  AuthenticatedMedusaRequest,3  MedusaNextFunction,4  MedusaResponse,5} from "@medusajs/framework/http"6import {7  ContainerRegistrationKeys,8  MedusaError,9} from "@medusajs/framework/utils"10
11export async function ensureReturnOrderOwner(12  req: AuthenticatedMedusaRequest,13  res: MedusaResponse,14  next: MedusaNextFunction15) {16  const query = req.scope.resolve(17    ContainerRegistrationKeys.QUERY18  )19
20  const { order_id } = req.body as { order_id?: string }21
22  const { data: [order] } = await query.graph({23    entity: "order",24    fields: ["id", "customer_id"],25    filters: {26      id: order_id,27    },28  })29
30  if (order?.customer_id !== req.auth_context.actor_id) {31    return next(32      new MedusaError(33        MedusaError.Types.UNAUTHORIZED,34        "You're not allowed to create a return for this order."35      )36    )37  }38
39  next()40}

The middleware retrieves the order's ID from the request body, since the Create Return API route accepts it as a body parameter. It then retrieves the order's customer_id with Query. The auth_context.actor_id property holds the ID of the customer that the authenticate middleware authenticated. If the two IDs don't match, the middleware rejects the request with a 401 error.

Then, apply the middleware after the authenticate middleware:

src/api/middlewares.ts
1import {2  defineMiddlewares,3  authenticate,4} from "@medusajs/framework/http"5import {6  ensureReturnOrderOwner,7} from "./middlewares/ensure-return-order-owner"8
9export default defineMiddlewares({10  routes: [11    {12      matcher: "/store/returns",13      method: ["POST"],14      middlewares: [15        authenticate("customer", ["session", "bearer"]),16        ensureReturnOrderOwner,17      ],18    },19  ],20})

The order of the middlewares matters. The authenticate middleware must run first, since ensureReturnOrderOwner reads the customer that it authenticated.

Now, only the customer that placed the order can create a return for it. Other logged-in customers receive a 401 error.


Reject the receive_now Parameter#

If your store reviews returns before marking their items as received, reject the receive_now parameter on the storefront. Merchants can still mark the return as received from the Medusa Admin dashboard or the Admin API.

Create the file src/api/middlewares/reject-receive-now.ts with the following content:

src/api/middlewares/reject-receive-now.ts
1import {2  MedusaNextFunction,3  MedusaRequest,4  MedusaResponse,5} from "@medusajs/framework/http"6import { MedusaError } from "@medusajs/framework/utils"7
8export async function rejectReceiveNow(9  req: MedusaRequest,10  res: MedusaResponse,11  next: MedusaNextFunction12) {13  const { receive_now } = req.body as {14    receive_now?: boolean15  }16
17  if (receive_now) {18    return next(19      new MedusaError(20        MedusaError.Types.NOT_ALLOWED,21        "You can't receive a return's items."22      )23    )24  }25
26  next()27}

Then, apply the middleware to the route:

src/api/middlewares.ts
1import { defineMiddlewares } from "@medusajs/framework/http"2import {3  rejectReceiveNow,4} from "./middlewares/reject-receive-now"5
6export default defineMiddlewares({7  routes: [8    {9      matcher: "/store/returns",10      method: ["POST"],11      middlewares: [12        // other middlewares...13        rejectReceiveNow14      ],15    },16  ],17})

A request that enables receive_now now receives a 400 error. The merchant marks the return as received later, as explained in the Order Return documentation.

Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break