From 70a99a2434a18a5bd7f30de75ca3349ddf24f329 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Thu, 1 May 2025 19:39:53 +0300 Subject: [PATCH] docs: generate events reference (#12341) * docs: generate events reference * change link in navbar * fix redirect --- www/apps/book/.content.eslint.mjs | 6 +- .../data-payload/page.mdx | 2 +- .../events-and-subscribers/page.mdx | 2 +- www/apps/book/app/learn/page.mdx | 2 +- www/apps/book/generated/edit-dates.mjs | 6 +- www/apps/book/public/llms-full.txt | 31762 ++++++++-------- www/apps/book/public/llms.txt | 2 +- www/apps/book/utils/redirects.mjs | 2 +- www/apps/resources/.content.eslintrc.mjs | 7 +- .../commerce-modules/auth/events/_content.mdx | 36 - .../app/commerce-modules/auth/events/page.mdx | 11 - .../commerce-modules/cart/events/_content.mdx | 76 - .../app/commerce-modules/cart/events/page.mdx | 11 - .../customer/events/_content.mdx | 76 - .../commerce-modules/customer/events/page.mdx | 11 - .../fulfillment/events/_content.mdx | 55 - .../fulfillment/events/page.mdx | 11 - .../order/events/_content.mdx | 272 - .../commerce-modules/order/events/page.mdx | 11 - .../payment/events/_content.mdx | 55 - .../commerce-modules/payment/events/page.mdx | 11 - .../events/_content/product-category.mdx | 76 - .../events/_content/product-collection.mdx | 76 - .../events/_content/product-option.mdx | 76 - .../product/events/_content/product-tag.mdx | 76 - .../product/events/_content/product-type.mdx | 76 - .../events/_content/product-variant.mdx | 76 - .../product/events/_content/product.mdx | 76 - .../commerce-modules/product/events/page.mdx | 55 - .../region/events/_content.mdx | 76 - .../commerce-modules/region/events/page.mdx | 11 - .../sales-channel/events/_content.mdx | 76 - .../sales-channel/events/page.mdx | 11 - .../user/events/_content/invite.mdx | 97 - .../user/events/_content/user.mdx | 76 - .../app/commerce-modules/user/events/page.mdx | 20 - .../resources/app/events-reference/page.mdx | 126 - .../tutorials/loyalty-points/page.mdx | 2 +- .../integrations/guides/contentful/page.mdx | 2 +- .../app/integrations/guides/resend/page.mdx | 2 +- .../app/integrations/guides/sanity/page.mdx | 2 +- .../guides/revalidate-cache/page.mdx | 2 +- .../app/recipes/commerce-automation/page.mdx | 6 +- .../components/MDXComponents/index.tsx | 2 + www/apps/resources/generated/edit-dates.mjs | 791 +- www/apps/resources/generated/files-map.mjs | 88 +- .../generated-commerce-modules-sidebar.mjs | 20 +- .../generated-references-sidebar.mjs | 2 +- www/apps/resources/generated/slug-changes.mjs | 50 + www/apps/resources/next.config.mjs | 55 + .../module_events/module_events.Auth/page.mdx | 55 + .../module_events/module_events.Cart/page.mdx | 134 + .../module_events.Customer/page.mdx | 105 + .../module_events.Fulfillment/page.mdx | 78 + .../module_events.Order/page.mdx | 459 + .../module_events.Product/page.mdx | 699 + .../module_events.Region/page.mdx | 103 + .../module_events.Sales_Channel/page.mdx | 103 + .../module_events/module_events.User/page.mdx | 237 + .../references/modules/events/page.mdx | 1921 + .../references/modules/module_events/page.mdx | 239 + www/apps/resources/sidebars/auth.mjs | 2 +- www/apps/resources/sidebars/cart.mjs | 2 +- www/apps/resources/sidebars/customer.mjs | 2 +- www/apps/resources/sidebars/fulfillment.mjs | 2 +- www/apps/resources/sidebars/order-module.mjs | 2 +- www/apps/resources/sidebars/payment.mjs | 2 +- www/apps/resources/sidebars/product.mjs | 2 +- www/apps/resources/sidebars/references.mjs | 2 +- www/apps/resources/sidebars/region.mjs | 2 +- www/apps/resources/sidebars/sales-channel.mjs | 2 +- www/apps/resources/sidebars/user.mjs | 2 +- .../resources/utils/get-sidebar-for-path.ts | 2 +- www/apps/ui/.content.eslintrc.mjs | 6 +- www/apps/user-guide/.content.eslintrc.mjs | 6 +- www/packages/docs-ui/src/constants.tsx | 2 +- .../generated/typedoc-json-output/events.json | 4914 +++ .../typedoc-json-output/module-events.json | 5268 +++ .../src/classes/kinds/events.ts | 13 +- www/utils/packages/typedoc-config/tsdoc.json | 12 + .../src/constants/custom-options.ts | 13 + .../constants/merger-custom-options/events.ts | 29 + .../constants/merger-custom-options/index.ts | 2 + .../src/constants/merger-options.ts | 3 + .../src/events-resolver.ts | 106 + .../typedoc-plugin-custom/src/index.ts | 2 + .../package.json | 1 + .../src/render-utils.ts | 4 + .../src/resources/helpers/events-listing.ts | 142 + .../resources/helpers/if-events-reference.ts | 15 + .../src/resources/templates/reflection.hbs | 12 + .../src/utils/reflection-template-strings.ts | 9 + www/utils/packages/types/lib/index.d.ts | 18 + www/utils/yarn.lock | 8 + 94 files changed, 31164 insertions(+), 18109 deletions(-) delete mode 100644 www/apps/resources/app/commerce-modules/auth/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/auth/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/cart/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/cart/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/customer/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/customer/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/fulfillment/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/order/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/order/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/payment/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/payment/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product-category.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product-collection.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product-option.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product-tag.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product-type.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product-variant.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/_content/product.mdx delete mode 100644 www/apps/resources/app/commerce-modules/product/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/region/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/region/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/sales-channel/events/_content.mdx delete mode 100644 www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx delete mode 100644 www/apps/resources/app/commerce-modules/user/events/_content/invite.mdx delete mode 100644 www/apps/resources/app/commerce-modules/user/events/_content/user.mdx delete mode 100644 www/apps/resources/app/commerce-modules/user/events/page.mdx delete mode 100644 www/apps/resources/app/events-reference/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Auth/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Cart/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Customer/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Fulfillment/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Order/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Product/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Region/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.Sales_Channel/page.mdx create mode 100644 www/apps/resources/references/module_events/module_events.User/page.mdx create mode 100644 www/apps/resources/references/modules/events/page.mdx create mode 100644 www/apps/resources/references/modules/module_events/page.mdx create mode 100644 www/utils/generated/typedoc-json-output/events.json create mode 100644 www/utils/generated/typedoc-json-output/module-events.json create mode 100644 www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/events.ts create mode 100644 www/utils/packages/typedoc-plugin-custom/src/events-resolver.ts create mode 100644 www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/events-listing.ts create mode 100644 www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/if-events-reference.ts diff --git a/www/apps/book/.content.eslint.mjs b/www/apps/book/.content.eslint.mjs index 65f47a8b41..40e7abc55e 100644 --- a/www/apps/book/.content.eslint.mjs +++ b/www/apps/book/.content.eslint.mjs @@ -19,11 +19,7 @@ const compat = new FlatCompat({ export default [ { - ignores: [ - "**/references/**/*", - "**/events-reference/**/*", - "**/_events-table/**/*", - ], + ignores: ["**/references/**/*"], }, { plugins: { diff --git a/www/apps/book/app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx b/www/apps/book/app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx index 835ca4a273..fe0c0f28a0 100644 --- a/www/apps/book/app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx +++ b/www/apps/book/app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx @@ -66,4 +66,4 @@ This logs the product ID received in the `product.created` event’s data payloa ## List of Events with Data Payload -Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */} \ No newline at end of file +Refer to [this reference](!resources!/references/events) for a full list of events emitted by Medusa and their data payloads. */} \ No newline at end of file diff --git a/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx b/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx index c9fc191a39..cdf34c3400 100644 --- a/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx +++ b/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx @@ -26,7 +26,7 @@ If the action you're performing is integral to the main flow of the core commerc ### List of Emitted Events -Find a list of all emitted events in [this reference](!resources!/events-reference). +Find a list of all emitted events in [this reference](!resources!/references/events). --- diff --git a/www/apps/book/app/learn/page.mdx b/www/apps/book/app/learn/page.mdx index 77c6a557e9..91017d33fa 100644 --- a/www/apps/book/app/learn/page.mdx +++ b/www/apps/book/app/learn/page.mdx @@ -132,7 +132,7 @@ This documentation is split into the following sections: [References](!resources!/references-overview) - Useful during your development with Medusa to learn about different APIs and how to use them. Its references include the [Events Reference](!resources!/events-reference), [Core Workflows Reference](!resources!/medusa-workflows-reference), and more. + Useful during your development with Medusa to learn about different APIs and how to use them. Its references include the [Events Reference](!resources!/references/events), [Core Workflows Reference](!resources!/medusa-workflows-reference), and more. diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs index 6ec82b3484..7db56f38f9 100644 --- a/www/apps/book/generated/edit-dates.mjs +++ b/www/apps/book/generated/edit-dates.mjs @@ -2,7 +2,7 @@ export const generatedEditDates = { "app/learn/fundamentals/scheduled-jobs/page.mdx": "2024-12-09T10:51:40.570Z", "app/learn/fundamentals/workflows/page.mdx": "2024-12-09T14:45:17.837Z", "app/learn/deployment/page.mdx": "2025-03-11T14:53:25.540Z", - "app/learn/page.mdx": "2025-04-17T08:50:17.036Z", + "app/learn/page.mdx": "2025-05-01T15:30:08.238Z", "app/learn/fundamentals/modules/commerce-modules/page.mdx": "2025-04-17T08:51:32.723Z", "app/learn/fundamentals/workflows/retry-failed-steps/page.mdx": "2025-03-28T07:15:19.388Z", "app/learn/fundamentals/workflows/workflow-hooks/page.mdx": "2024-12-09T10:44:33.781Z", @@ -15,7 +15,7 @@ export const generatedEditDates = { "app/learn/fundamentals/medusa-container/page.mdx": "2024-12-09T11:02:38.225Z", "app/learn/fundamentals/api-routes/page.mdx": "2024-12-04T11:02:57.134Z", "app/learn/fundamentals/modules/modules-directory-structure/page.mdx": "2024-12-09T10:32:46.839Z", - "app/learn/fundamentals/events-and-subscribers/page.mdx": "2025-04-18T10:42:32.803Z", + "app/learn/fundamentals/events-and-subscribers/page.mdx": "2025-05-01T15:30:08.333Z", "app/learn/fundamentals/modules/container/page.mdx": "2025-03-18T15:10:03.574Z", "app/learn/fundamentals/workflows/execute-another-workflow/page.mdx": "2024-12-09T15:56:22.895Z", "app/learn/fundamentals/modules/loaders/page.mdx": "2025-04-22T15:32:00.430Z", @@ -24,7 +24,7 @@ export const generatedEditDates = { "app/learn/fundamentals/modules/remote-link/page.mdx": "2024-09-30T08:43:53.127Z", "app/learn/fundamentals/api-routes/protected-routes/page.mdx": "2025-03-17T11:47:10.101Z", "app/learn/fundamentals/workflows/add-workflow-hook/page.mdx": "2024-12-09T14:42:39.693Z", - "app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx": "2024-10-21T13:30:21.369Z", + "app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx": "2025-05-01T15:30:08.421Z", "app/learn/fundamentals/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z", "app/learn/fundamentals/events-and-subscribers/emit-event/page.mdx": "2025-03-18T15:09:40.243Z", "app/learn/fundamentals/workflows/conditions/page.mdx": "2025-01-27T08:45:19.027Z", diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt index 67e1e69d94..7c32b36a73 100644 --- a/www/apps/book/public/llms-full.txt +++ b/www/apps/book/public/llms-full.txt @@ -345,28 +345,6 @@ Refer to [this documentation](https://docs.medusajs.com/learn/update/index.html. In the next chapters, you'll learn about the architecture of your Medusa application, then learn how to customize your application to build custom features. -# Storefront Development - -The Medusa application is made up of a Node.js server and an admin dashboard. Storefronts are installed, built, and hosted separately from the Medusa application, giving you the flexibility to choose the frontend tech stack that you and your team are proficient in, and implement unique design systems and user experience. - -You can build your storefront from scratch with your preferred tech stack, or start with our Next.js Starter storefront. The Next.js Starter storefront provides rich commerce features and a sleek design. Developers and businesses can use it as-is or build on top of it to tailor it for the business's unique use case, design, and customer experience. - -- [Install Next.js Starter Storefront](https://docs.medusajs.com/resources/nextjs-starter/index.html.md) -- [Build Custom Storefront](https://docs.medusajs.com/resources/storefront-development/index.html.md) - -*** - -## Passing a Publishable API Key in Storefront Requests - -When sending a request to an API route starting with `/store`, you must include a publishable API key in the header of your request. - -A publishable API key sets the scope of your request to one or more sales channels. - -Then, when you retrieve products, only products of those sales channels are retrieved. This also ensures you retrieve correct inventory data, and associate created orders with the scoped sales channel. - -Learn more about passing the publishable API key in [this storefront development guide](https://docs.medusajs.com/resources/storefront-development/publishable-api-keys/index.html.md). - - # Updating Medusa In this chapter, you'll learn about updating your Medusa application and packages. @@ -473,48 +451,26 @@ npm install ``` -# Using TypeScript Aliases +# Storefront Development -By default, Medusa doesn't support TypeScript aliases in production. +The Medusa application is made up of a Node.js server and an admin dashboard. Storefronts are installed, built, and hosted separately from the Medusa application, giving you the flexibility to choose the frontend tech stack that you and your team are proficient in, and implement unique design systems and user experience. -If you prefer using TypeScript aliases, install following development dependencies: +You can build your storefront from scratch with your preferred tech stack, or start with our Next.js Starter storefront. The Next.js Starter storefront provides rich commerce features and a sleek design. Developers and businesses can use it as-is or build on top of it to tailor it for the business's unique use case, design, and customer experience. -```bash npm2yarn -npm install --save-dev tsc-alias rimraf -``` +- [Install Next.js Starter Storefront](https://docs.medusajs.com/resources/nextjs-starter/index.html.md) +- [Build Custom Storefront](https://docs.medusajs.com/resources/storefront-development/index.html.md) -Where `tsc-alias` is a package that resolves TypeScript aliases, and `rimraf` is a package that removes files and directories. +*** -Then, add a new `resolve:aliases` script to your `package.json` and update the `build` script: +## Passing a Publishable API Key in Storefront Requests -```json title="package.json" -{ - "scripts": { - // other scripts... - "resolve:aliases": "tsc --showConfig -p tsconfig.json > tsconfig.resolved.json && tsc-alias -p tsconfig.resolved.json && rimraf tsconfig.resolved.json", - "build": "medusa build && npm run resolve:aliases" - } -} -``` +When sending a request to an API route starting with `/store`, you must include a publishable API key in the header of your request. -You can now use TypeScript aliases in your Medusa application. For example, add the following in `tsconfig.json`: +A publishable API key sets the scope of your request to one or more sales channels. -```json title="tsconfig.json" -{ - "compilerOptions": { - // ... - "paths": { - "@/*": ["./src/*"] - } - } -} -``` +Then, when you retrieve products, only products of those sales channels are retrieved. This also ensures you retrieve correct inventory data, and associate created orders with the scoped sales channel. -Now, you can import modules, for example, using TypeScript aliases: - -```ts -import { BrandModuleService } from "@/modules/brand/service" -``` +Learn more about passing the publishable API key in [this storefront development guide](https://docs.medusajs.com/resources/storefront-development/publishable-api-keys/index.html.md). # Medusa Application Configuration @@ -1420,6 +1376,50 @@ npx medusa db:migrate ``` +# Using TypeScript Aliases + +By default, Medusa doesn't support TypeScript aliases in production. + +If you prefer using TypeScript aliases, install following development dependencies: + +```bash npm2yarn +npm install --save-dev tsc-alias rimraf +``` + +Where `tsc-alias` is a package that resolves TypeScript aliases, and `rimraf` is a package that removes files and directories. + +Then, add a new `resolve:aliases` script to your `package.json` and update the `build` script: + +```json title="package.json" +{ + "scripts": { + // other scripts... + "resolve:aliases": "tsc --showConfig -p tsconfig.json > tsconfig.resolved.json && tsc-alias -p tsconfig.resolved.json && rimraf tsconfig.resolved.json", + "build": "medusa build && npm run resolve:aliases" + } +} +``` + +You can now use TypeScript aliases in your Medusa application. For example, add the following in `tsconfig.json`: + +```json title="tsconfig.json" +{ + "compilerOptions": { + // ... + "paths": { + "@/*": ["./src/*"] + } + } +} +``` + +Now, you can import modules, for example, using TypeScript aliases: + +```ts +import { BrandModuleService } from "@/modules/brand/service" +``` + + # Build Custom Features In the upcoming chapters, you'll follow step-by-step guides to build custom features in Medusa. These guides gradually introduce Medusa's concepts to help you understand what they are and how to use them. @@ -1445,6 +1445,50 @@ The next chapters will guide you to: 3. Expose an API route that allows admin users to create a brand using the workflow. +# Customize Medusa Admin Dashboard + +In the previous chapters, you've customized your Medusa application to [add brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md), [expose an API route to create brands](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), and [linked brands to products](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md). + +After customizing and extending your application with new features, you may need to provide an interface for admin users to utilize these features. The Medusa Admin dashboard is extendable, allowing you to: + +- Insert components, called [widgets](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md), on existing pages. +- Add new pages, called [UI Routes](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). + +From these customizations, you can send requests to custom API routes, allowing admin users to manage custom resources on the dashboard + +*** + +## Next Chapters: View Brands in Dashboard + +In the next chapters, you'll continue with the brands example to: + +- Add a new section to the product details page that shows the product's brand. +- Add a new page in the dashboard that shows all brands in the store. + + +# Integrate Third-Party Systems + +Commerce applications often connect to third-party systems that provide additional or specialized features. For example, you may integrate a Content-Management System (CMS) for rich content features, a payment provider to process credit-card payments, and a notification service to send emails. + +The Medusa Framework facilitates integrating these systems and orchestrating operations across them, saving you the effort of managing them yourself. You won't find those capabilities in other commerce platforms that in these scenarios become a bottleneck to building customizations and iterating quickly. + +In Medusa, you integrate a third-party system by: + +1. Creating a module whose service provides the methods to connect to and perform operations in the third-party system. +2. Building workflows that complete tasks spanning across systems. You use the module that integrates a third-party system in the workflow's steps. +3. Executing the workflows you built in an [API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), at a scheduled time, or when an event is emitted. + +*** + +## Next Chapters: Sync Brands Example + +In the previous chapters, you've [added brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) to your Medusa application. In the next chapters, you will: + +1. Integrate a dummy third-party CMS in the Brand Module. +2. Sync brands to the CMS when a brand is created. +3. Sync brands from the CMS at a daily schedule. + + # Extend Core Commerce Features In the upcoming chapters, you'll learn about the concepts and tools to extend Medusa's core commerce features. @@ -1468,6 +1512,21 @@ The next chapters explain how to use the tools mentioned above with step-by-step - Retrieve a product's associated brand's details. +# Re-Use Customizations with Plugins + +In the previous chapters, you've learned important concepts related to creating modules, implementing commerce features in workflows, exposing those features in API routes, customizing the Medusa Admin dashboard with Admin Extensions, and integrating third-party systems. + +You've implemented the brands example within a single Medusa application. However, this approach is not scalable when you want to reuse your customizations across multiple projects. + +To reuse your customizations across multiple Medusa applications, such as implementing brands in different projects, you can create a plugin. A plugin is an NPM package that encapsulates your customizations and can be installed in any Medusa application. Plugins can include modules, workflows, API routes, Admin Extensions, and more. + +![Diagram showcasing how the Brand Plugin would add its resources to any application it's installed in](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540091/Medusa%20Book/brand-plugin_bk9zi9.jpg) + +Medusa provides the tooling to create a plugin package, test it in a local Medusa application, and publish it to NPM. + +To learn more about plugins and how to create them, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). + + # Customizations Next Steps: Learn the Fundamentals The previous guides introduced Medusa's different concepts and how you can use them to customize Medusa for a realistic use case, You added brands to your application, linked them to products, customized the admin dashboard, and integrated a third-party CMS. @@ -1490,21 +1549,6 @@ The following guides and references are useful for your development journey: In the [Recipes](https://docs.medusajs.com/resources/recipes/index.html.md) documentation, you'll also find step-by-step guides for different use cases, such as building a marketplace, digital products, and more. -# Re-Use Customizations with Plugins - -In the previous chapters, you've learned important concepts related to creating modules, implementing commerce features in workflows, exposing those features in API routes, customizing the Medusa Admin dashboard with Admin Extensions, and integrating third-party systems. - -You've implemented the brands example within a single Medusa application. However, this approach is not scalable when you want to reuse your customizations across multiple projects. - -To reuse your customizations across multiple Medusa applications, such as implementing brands in different projects, you can create a plugin. A plugin is an NPM package that encapsulates your customizations and can be installed in any Medusa application. Plugins can include modules, workflows, API routes, Admin Extensions, and more. - -![Diagram showcasing how the Brand Plugin would add its resources to any application it's installed in](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540091/Medusa%20Book/brand-plugin_bk9zi9.jpg) - -Medusa provides the tooling to create a plugin package, test it in a local Medusa application, and publish it to NPM. - -To learn more about plugins and how to create them, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). - - # General Medusa Application Deployment Guide In this document, you'll learn the general steps to deploy your Medusa application. How you apply these steps depend on your chosen hosting provider or platform. @@ -2159,50 +2203,6 @@ Medusa's Testing Framework works for integration tests only. You can write unit The next chapters explain how to use the testing tools provided by `@medusajs/test-utils` to write tests. -# Integrate Third-Party Systems - -Commerce applications often connect to third-party systems that provide additional or specialized features. For example, you may integrate a Content-Management System (CMS) for rich content features, a payment provider to process credit-card payments, and a notification service to send emails. - -The Medusa Framework facilitates integrating these systems and orchestrating operations across them, saving you the effort of managing them yourself. You won't find those capabilities in other commerce platforms that in these scenarios become a bottleneck to building customizations and iterating quickly. - -In Medusa, you integrate a third-party system by: - -1. Creating a module whose service provides the methods to connect to and perform operations in the third-party system. -2. Building workflows that complete tasks spanning across systems. You use the module that integrates a third-party system in the workflow's steps. -3. Executing the workflows you built in an [API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), at a scheduled time, or when an event is emitted. - -*** - -## Next Chapters: Sync Brands Example - -In the previous chapters, you've [added brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) to your Medusa application. In the next chapters, you will: - -1. Integrate a dummy third-party CMS in the Brand Module. -2. Sync brands to the CMS when a brand is created. -3. Sync brands from the CMS at a daily schedule. - - -# Customize Medusa Admin Dashboard - -In the previous chapters, you've customized your Medusa application to [add brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md), [expose an API route to create brands](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), and [linked brands to products](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md). - -After customizing and extending your application with new features, you may need to provide an interface for admin users to utilize these features. The Medusa Admin dashboard is extendable, allowing you to: - -- Insert components, called [widgets](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md), on existing pages. -- Add new pages, called [UI Routes](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). - -From these customizations, you can send requests to custom API routes, allowing admin users to manage custom resources on the dashboard - -*** - -## Next Chapters: View Brands in Dashboard - -In the next chapters, you'll continue with the brands example to: - -- Add a new section to the product details page that shows the product's brand. -- Add a new page in the dashboard that shows all brands in the store. - - # Medusa's Architecture In this chapter, you'll learn about the architectural layers in Medusa. @@ -2322,6 +2322,135 @@ Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui/index.html.m To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components. +# API Routes + +In this chapter, you’ll learn what API Routes are and how to create them. + +## What is an API Route? + +An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems. + +The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities. + +*** + +## How to Create an API Route? + +An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The file’s name must be `route.ts` or `route.js`. + +![Example of API route in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732808645/Medusa%20Book/route-dir-overview_dqgzmk.jpg) + +Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…). + +For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content: + +```ts title="src/api/hello-world/route.ts" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +export const GET = ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: "[GET] Hello world!", + }) +} +``` + +### Test API Route + +To test the API route above, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, send a `GET` request to the `/hello-world` API Route: + +```bash +curl http://localhost:9000/hello-world +``` + +*** + +## When to Use API Routes + +You're exposing custom functionality to be used by a storefront, admin dashboard, or any external application. + + +# Custom CLI Scripts + +In this chapter, you'll learn how to create and execute custom scripts from Medusa's CLI tool. + +## What is a Custom CLI Script? + +A custom CLI script is a function to execute through Medusa's CLI tool. This is useful when creating custom Medusa tooling to run through the CLI. + +*** + +## How to Create a Custom CLI Script? + +To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must default export a function. + +For example, create the file `src/scripts/my-script.ts` with the following content: + +```ts title="src/scripts/my-script.ts" +import { + ExecArgs, + IProductModuleService, +} from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" + +export default async function myScript({ container }: ExecArgs) { + const productModuleService: IProductModuleService = container.resolve( + Modules.PRODUCT + ) + + const [, count] = await productModuleService + .listAndCountProducts() + + console.log(`You have ${count} product(s)`) +} +``` + +The function receives as a parameter an object having a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application. + +*** + +## How to Run Custom CLI Script? + +To run the custom CLI script, run the Medusa CLI's `exec` command: + +```bash +npx medusa exec ./src/scripts/my-script.ts +``` + +*** + +## Custom CLI Script Arguments + +Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property. + +For example: + +```ts +import { ExecArgs } from "@medusajs/framework/types" + +export default async function myScript({ args }: ExecArgs) { + console.log(`The arguments you passed: ${args}`) +} +``` + +Then, pass the arguments in the `exec` command after the file path: + +```bash +npx medusa exec ./src/scripts/my-script.ts arg1 arg2 +``` + + # Data Models In this chapter, you'll learn what a data model is and how to create a data model. @@ -2525,7 +2654,7 @@ If the action you're performing is integral to the main flow of the core commerc ### List of Emitted Events -Find a list of all emitted events in [this reference](https://docs.medusajs.com/resources/events-reference/index.html.md). +Find a list of all emitted events in [this reference](https://docs.medusajs.com/resources/references/events/index.html.md). *** @@ -3742,114 +3871,6 @@ npx medusa db:migrate ``` -# Custom CLI Scripts - -In this chapter, you'll learn how to create and execute custom scripts from Medusa's CLI tool. - -## What is a Custom CLI Script? - -A custom CLI script is a function to execute through Medusa's CLI tool. This is useful when creating custom Medusa tooling to run through the CLI. - -*** - -## How to Create a Custom CLI Script? - -To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must default export a function. - -For example, create the file `src/scripts/my-script.ts` with the following content: - -```ts title="src/scripts/my-script.ts" -import { - ExecArgs, - IProductModuleService, -} from "@medusajs/framework/types" -import { Modules } from "@medusajs/framework/utils" - -export default async function myScript({ container }: ExecArgs) { - const productModuleService: IProductModuleService = container.resolve( - Modules.PRODUCT - ) - - const [, count] = await productModuleService - .listAndCountProducts() - - console.log(`You have ${count} product(s)`) -} -``` - -The function receives as a parameter an object having a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application. - -*** - -## How to Run Custom CLI Script? - -To run the custom CLI script, run the Medusa CLI's `exec` command: - -```bash -npx medusa exec ./src/scripts/my-script.ts -``` - -*** - -## Custom CLI Script Arguments - -Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property. - -For example: - -```ts -import { ExecArgs } from "@medusajs/framework/types" - -export default async function myScript({ args }: ExecArgs) { - console.log(`The arguments you passed: ${args}`) -} -``` - -Then, pass the arguments in the `exec` command after the file path: - -```bash -npx medusa exec ./src/scripts/my-script.ts arg1 arg2 -``` - - -# Plugins - -In this chapter, you'll learn what a plugin is in Medusa. - -Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). - -## What is a Plugin? - -A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. The supported customizations are [Modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), [API Routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), [Workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), [Workflow Hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md), [Links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), [Subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md), [Scheduled Jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md), and [Admin Extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md). - -Plugins allow you to reuse your Medusa customizations across multiple projects or share them with the community. They can be published to npm and installed in any Medusa project. - -![Diagram showcasing a wishlist plugin installed in a Medusa application](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540762/Medusa%20Book/plugin-diagram_oepiis.jpg) - -Learn how to create a wishlist plugin in [this guide](https://docs.medusajs.com/resources/plugins/guides/wishlist/index.html.md). - -*** - -## Plugin vs Module - -A [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) is an isolated package related to a single domain or functionality, such as product reviews or integrating a Content Management System. A module can't access any resources in the Medusa application that are outside its codebase. - -A plugin, on the other hand, can contain multiple Medusa customizations, including modules. Your plugin can define a module, then build flows around it. - -For example, in a plugin, you can define a module that integrates a third-party service, then add a workflow that uses the module when a certain event occurs to sync data to that service. - -- You want to reuse your Medusa customizations across multiple projects. -- You want to share your Medusa customizations with the community. - -- You want to build a custom feature related to a single domain or integrate a third-party service. Instead, use a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). You can wrap that module in a plugin if it's used in other customizations, such as if it has a module link or it's used in a workflow. - -*** - -## How to Create a Plugin? - -The next chapter explains how you can create and publish a plugin. - - # Modules In this chapter, you’ll learn about modules and how to create them. @@ -4150,63 +4171,136 @@ This will create a post and return it in the response: You can also execute the workflow from a [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) when an event occurs, or from a [scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md) to run it at a specified interval. -# API Routes +# Scheduled Jobs -In this chapter, you’ll learn what API Routes are and how to create them. +In this chapter, you’ll learn about scheduled jobs and how to use them. -## What is an API Route? +## What is a Scheduled Job? -An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems. +When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day. -The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities. +In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling. + +Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP. + +- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, use the operating system's equivalent of a cron job. +- You want to execute the action once when the application loads. Use [loaders](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) instead. +- You want to execute the action if an event occurs. Use [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) instead. *** -## How to Create an API Route? +## How to Create a Scheduled Job? -An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The file’s name must be `route.ts` or `route.js`. +You create a scheduled job in a TypeScript or JavaScript file under the `src/jobs` directory. The file exports the asynchronous function to run, and the configurations indicating the schedule to run the function. -![Example of API route in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732808645/Medusa%20Book/route-dir-overview_dqgzmk.jpg) +For example, create the file `src/jobs/hello-world.ts` with the following content: -Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…). +![Example of scheduled job file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732866423/Medusa%20Book/scheduled-job-dir-overview_ediqgm.jpg) -For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content: +```ts title="src/jobs/hello-world.ts" highlights={highlights} +import { MedusaContainer } from "@medusajs/framework/types" -```ts title="src/api/hello-world/route.ts" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" +export default async function greetingJob(container: MedusaContainer) { + const logger = container.resolve("logger") -export const GET = ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: "[GET] Hello world!", - }) + logger.info("Greeting!") +} + +export const config = { + name: "greeting-every-minute", + schedule: "* * * * *", } ``` -### Test API Route +You export an asynchronous function that receives the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) as a parameter. In the function, you resolve the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) from the Medusa container and log a message. -To test the API route above, start the Medusa application: +You also export a `config` object that has the following properties: + +- `name`: A unique name for the job. +- `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job. + +This scheduled job executes every minute and logs into the terminal `Greeting!`. + +### Test the Scheduled Job + +To test out your scheduled job, start the Medusa application: ```bash npm2yarn npm run dev ``` -Then, send a `GET` request to the `/hello-world` API Route: +After a minute, the following message will be logged to the terminal: ```bash -curl http://localhost:9000/hello-world +info: Greeting! ``` *** -## When to Use API Routes +## Example: Sync Products Once a Day -You're exposing custom functionality to be used by a storefront, admin dashboard, or any external application. +In this section, you'll find a brief example of how you use a scheduled job to sync products to a third-party service. + +When implementing flows spanning across systems or [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), you use [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more. + +You can learn how to create a workflow in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content: + +```ts title="src/jobs/sync-products.ts" +import { MedusaContainer } from "@medusajs/framework/types" +import { syncProductToErpWorkflow } from "../workflows/sync-products-to-erp" + +export default async function syncProductsJob(container: MedusaContainer) { + await syncProductToErpWorkflow(container) + .run() +} + +export const config = { + name: "sync-products-job", + schedule: "0 0 * * *", +} +``` + +In the scheduled job function, you execute the `syncProductToErpWorkflow` by invoking it and passing it the container, then invoking the `run` method. You also specify in the exported configurations the schedule `0 0 * * *` which indicates midnight time of every day. + +The next time you start the Medusa application, it will run this job every day at midnight. + + +# Plugins + +In this chapter, you'll learn what a plugin is in Medusa. + +Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). + +## What is a Plugin? + +A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. The supported customizations are [Modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), [API Routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), [Workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), [Workflow Hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md), [Links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), [Subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md), [Scheduled Jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md), and [Admin Extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md). + +Plugins allow you to reuse your Medusa customizations across multiple projects or share them with the community. They can be published to npm and installed in any Medusa project. + +![Diagram showcasing a wishlist plugin installed in a Medusa application](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540762/Medusa%20Book/plugin-diagram_oepiis.jpg) + +Learn how to create a wishlist plugin in [this guide](https://docs.medusajs.com/resources/plugins/guides/wishlist/index.html.md). + +*** + +## Plugin vs Module + +A [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) is an isolated package related to a single domain or functionality, such as product reviews or integrating a Content Management System. A module can't access any resources in the Medusa application that are outside its codebase. + +A plugin, on the other hand, can contain multiple Medusa customizations, including modules. Your plugin can define a module, then build flows around it. + +For example, in a plugin, you can define a module that integrates a third-party service, then add a workflow that uses the module when a certain event occurs to sync data to that service. + +- You want to reuse your Medusa customizations across multiple projects. +- You want to share your Medusa customizations with the community. + +- You want to build a custom feature related to a single domain or integrate a third-party service. Instead, use a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). You can wrap that module in a plugin if it's used in other customizations, such as if it has a module link or it's used in a workflow. + +*** + +## How to Create a Plugin? + +The next chapter explains how you can create and publish a plugin. # Worker Mode of Medusa Instance @@ -4555,6 +4649,96 @@ You can now execute this workflow in a custom API route, scheduled job, or subsc Find a full list of the registered resources in the Medusa container and their registration key in [this reference](https://docs.medusajs.com/resources/medusa-container-resources/index.html.md). You can use these resources in your custom workflows. +# Usage Information + +At Medusa, we strive to provide the best experience for developers using our platform. For that reason, Medusa collects anonymous and non-sensitive data that provides a global understanding of how users are using Medusa. + +*** + +## Purpose + +As an open source solution, we work closely and constantly interact with our community to ensure that we provide the best experience for everyone using Medusa. + +We are capable of getting a general understanding of how developers use Medusa and what general issues they run into through different means such as our Discord server, GitHub issues and discussions, and occasional one-on-one sessions. + +However, although these methods can be insightful, they’re not enough to get a full and global understanding of how developers are using Medusa, especially in production. + +Collecting this data allows us to understand certain details such as: + +- What operating system do most Medusa developers use? +- What version of Medusa is widely used? +- What parts of the Medusa Admin are generally undiscovered by our users? +- How much data do users manage through our Medusa Admin? Is it being used for large number of products, orders, and other types of data? +- What Node version is globally used? Should we focus our efforts on providing support for versions that we don’t currently support? + +*** + +## Medusa Application Analytics + +This section covers which data in the Medusa application are collected and how to opt out of it. + +### Collected Data in the Medusa Application + +The following data is being collected on your Medusa application: + +- Unique project ID generated with UUID. +- Unique machine ID generated with UUID. +- Operating system information including Node version or operating system platform used. +- The version of the Medusa application and Medusa CLI are used. + +Data is only collected when the Medusa application is run with the command `medusa start`. + +### How to Opt Out + +If you prefer to disable data collection, you can do it either by setting the following environment variable to true: + +```bash +MEDUSA_DISABLE_TELEMETRY=true +``` + +Or, you can run the following command in the root of your Medusa application project to disable it: + +```bash +npx medusa telemetry --disable +``` + +*** + +## Admin Analytics + +This section covers which data in the admin are collected and how to opt out of it. + +### Collected Data in Admin + +Users have the option to [enable or disable the anonymization](#how-to-enable-anonymization) of the collected data. + +The following data is being collected on your admin: + +- The name of the store. +- The email of the user. +- The total number of products, orders, discounts, and users. +- The number of regions and their names. +- The currencies used in the store. +- Errors that occur while using the admin. + +### How to Enable Anonymization + +To enable anonymization of your data from the Medusa Admin: + +1. Go to Settings → Personal Information. +2. In the Usage insights section, click on the “Edit preferences” button. +3. Enable the "Anonymize my usage data” toggle. +4. Click on the “Submit and close” button. + +### How to Opt-Out + +To opt out of analytics collection in the Medusa Admin, set the following environment variable: + +```bash +MEDUSA_FF_ANALYTICS=false +``` + + # Guide: Create Brand API Route In the previous two chapters, you created a [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) that added the concepts of brands to your application, then created a [workflow to create a brand](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md). In this chapter, you'll expose an API route that allows admin users to create a brand using the workflow from the previous chapter. @@ -4763,190 +4947,6 @@ Now that you have brands in your Medusa application, you want to associate a bra In the next chapters, you'll learn how to build associations between data models defined in different modules. -# Usage Information - -At Medusa, we strive to provide the best experience for developers using our platform. For that reason, Medusa collects anonymous and non-sensitive data that provides a global understanding of how users are using Medusa. - -*** - -## Purpose - -As an open source solution, we work closely and constantly interact with our community to ensure that we provide the best experience for everyone using Medusa. - -We are capable of getting a general understanding of how developers use Medusa and what general issues they run into through different means such as our Discord server, GitHub issues and discussions, and occasional one-on-one sessions. - -However, although these methods can be insightful, they’re not enough to get a full and global understanding of how developers are using Medusa, especially in production. - -Collecting this data allows us to understand certain details such as: - -- What operating system do most Medusa developers use? -- What version of Medusa is widely used? -- What parts of the Medusa Admin are generally undiscovered by our users? -- How much data do users manage through our Medusa Admin? Is it being used for large number of products, orders, and other types of data? -- What Node version is globally used? Should we focus our efforts on providing support for versions that we don’t currently support? - -*** - -## Medusa Application Analytics - -This section covers which data in the Medusa application are collected and how to opt out of it. - -### Collected Data in the Medusa Application - -The following data is being collected on your Medusa application: - -- Unique project ID generated with UUID. -- Unique machine ID generated with UUID. -- Operating system information including Node version or operating system platform used. -- The version of the Medusa application and Medusa CLI are used. - -Data is only collected when the Medusa application is run with the command `medusa start`. - -### How to Opt Out - -If you prefer to disable data collection, you can do it either by setting the following environment variable to true: - -```bash -MEDUSA_DISABLE_TELEMETRY=true -``` - -Or, you can run the following command in the root of your Medusa application project to disable it: - -```bash -npx medusa telemetry --disable -``` - -*** - -## Admin Analytics - -This section covers which data in the admin are collected and how to opt out of it. - -### Collected Data in Admin - -Users have the option to [enable or disable the anonymization](#how-to-enable-anonymization) of the collected data. - -The following data is being collected on your admin: - -- The name of the store. -- The email of the user. -- The total number of products, orders, discounts, and users. -- The number of regions and their names. -- The currencies used in the store. -- Errors that occur while using the admin. - -### How to Enable Anonymization - -To enable anonymization of your data from the Medusa Admin: - -1. Go to Settings → Personal Information. -2. In the Usage insights section, click on the “Edit preferences” button. -3. Enable the "Anonymize my usage data” toggle. -4. Click on the “Submit and close” button. - -### How to Opt-Out - -To opt out of analytics collection in the Medusa Admin, set the following environment variable: - -```bash -MEDUSA_FF_ANALYTICS=false -``` - - -# Scheduled Jobs - -In this chapter, you’ll learn about scheduled jobs and how to use them. - -## What is a Scheduled Job? - -When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day. - -In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling. - -Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP. - -- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, use the operating system's equivalent of a cron job. -- You want to execute the action once when the application loads. Use [loaders](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) instead. -- You want to execute the action if an event occurs. Use [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) instead. - -*** - -## How to Create a Scheduled Job? - -You create a scheduled job in a TypeScript or JavaScript file under the `src/jobs` directory. The file exports the asynchronous function to run, and the configurations indicating the schedule to run the function. - -For example, create the file `src/jobs/hello-world.ts` with the following content: - -![Example of scheduled job file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732866423/Medusa%20Book/scheduled-job-dir-overview_ediqgm.jpg) - -```ts title="src/jobs/hello-world.ts" highlights={highlights} -import { MedusaContainer } from "@medusajs/framework/types" - -export default async function greetingJob(container: MedusaContainer) { - const logger = container.resolve("logger") - - logger.info("Greeting!") -} - -export const config = { - name: "greeting-every-minute", - schedule: "* * * * *", -} -``` - -You export an asynchronous function that receives the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) as a parameter. In the function, you resolve the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) from the Medusa container and log a message. - -You also export a `config` object that has the following properties: - -- `name`: A unique name for the job. -- `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job. - -This scheduled job executes every minute and logs into the terminal `Greeting!`. - -### Test the Scheduled Job - -To test out your scheduled job, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -After a minute, the following message will be logged to the terminal: - -```bash -info: Greeting! -``` - -*** - -## Example: Sync Products Once a Day - -In this section, you'll find a brief example of how you use a scheduled job to sync products to a third-party service. - -When implementing flows spanning across systems or [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), you use [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more. - -You can learn how to create a workflow in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content: - -```ts title="src/jobs/sync-products.ts" -import { MedusaContainer } from "@medusajs/framework/types" -import { syncProductToErpWorkflow } from "../workflows/sync-products-to-erp" - -export default async function syncProductsJob(container: MedusaContainer) { - await syncProductToErpWorkflow(container) - .run() -} - -export const config = { - name: "sync-products-job", - schedule: "0 0 * * *", -} -``` - -In the scheduled job function, you execute the `syncProductToErpWorkflow` by invoking it and passing it the container, then invoking the `run` method. You also specify in the exported configurations the schedule `0 0 * * *` which indicates midnight time of every day. - -The next time you start the Medusa application, it will run this job every day at midnight. - - # Guide: Implement Brand Module In this chapter, you'll build a Brand Module that adds a `brand` table to the database and provides data-management features for it. @@ -5241,6 +5241,1175 @@ You now have a `createBrandWorkflow` that you can execute to create a brand. In the next chapter, you'll add an API route that allows admin users to create a brand. You'll learn how to create the API route, and execute in it the workflow you implemented in this chapter. +# Guide: Sync Brands from Medusa to Third-Party + +In the [previous chapter](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md), you created a CMS Module that integrates a dummy third-party system. You can now perform actions using that module within your custom flows. + +In another previous chapter, you [added a workflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) that creates a brand. After integrating the CMS, you want to sync that brand to the third-party system as well. + +Medusa has an event system that emits events when an operation is performed. It allows you to listen to those events and perform an asynchronous action in a function called a [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). This is useful to perform actions that aren't integral to the original flow, such as syncing data to a third-party system. + +Learn more about Medusa's event system and subscribers in [this chapter](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). + +In this chapter, you'll modify the `createBrandWorkflow` you created before to emit a custom event that indicates a brand was created. Then, you'll listen to that event in a subscriber to sync the brand to the third-party CMS. You'll implement the sync logic within a workflow that you execute in the subscriber. + +### Prerequisites + +- [createBrandWorkflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) +- [CMS Module](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md) + +## 1. Emit Event in createBrandWorkflow + +Since syncing the brand to the third-party system isn't integral to creating a brand, you'll emit a custom event indicating that a brand was created. + +Medusa provides an `emitEventStep` that allows you to emit an event in your workflows. So, in the `createBrandWorkflow` defined in `src/workflows/create-brand.ts`, use the `emitEventStep` helper step after the `createBrandStep`: + +```ts title="src/workflows/create-brand.ts" highlights={eventHighlights} +// other imports... +import { + emitEventStep, +} from "@medusajs/medusa/core-flows" + +// ... + +export const createBrandWorkflow = createWorkflow( + "create-brand", + (input: CreateBrandInput) => { + // ... + + emitEventStep({ + eventName: "brand.created", + data: { + id: brand.id, + }, + }) + + return new WorkflowResponse(brand) + } +) +``` + +The `emitEventStep` accepts an object parameter having two properties: + +- `eventName`: The name of the event to emit. You'll use this name later to listen to the event in a subscriber. +- `data`: The data payload to emit with the event. This data is passed to subscribers that listen to the event. You add the brand's ID to the data payload, informing the subscribers which brand was created. + +You'll learn how to handle this event in a later step. + +*** + +## 2. Create Sync to Third-Party System Workflow + +The subscriber that will listen to the `brand.created` event will sync the created brand to the third-party CMS. So, you'll implement the syncing logic in a workflow, then execute the workflow in the subscriber. + +Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution. + +Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). + +You'll create a `syncBrandToSystemWorkflow` that has two steps: + +- `useQueryGraphStep`: a step that Medusa provides to retrieve data using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). You'll use this to retrieve the brand's details using its ID. +- `syncBrandToCmsStep`: a step that you'll create to sync the brand to the CMS. + +### syncBrandToCmsStep + +To implement the step that syncs the brand to the CMS, create the file `src/workflows/sync-brands-to-cms.ts` with the following content: + +![Directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493547/Medusa%20Book/cms-dir-overview-4_u5t0ug.jpg) + +```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncStepHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" +import { InferTypeOf } from "@medusajs/framework/types" +import { Brand } from "../modules/brand/models/brand" +import { CMS_MODULE } from "../modules/cms" +import CmsModuleService from "../modules/cms/service" + +type SyncBrandToCmsStepInput = { + brand: InferTypeOf +} + +const syncBrandToCmsStep = createStep( + "sync-brand-to-cms", + async ({ brand }: SyncBrandToCmsStepInput, { container }) => { + const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) + + await cmsModuleService.createBrand(brand) + + return new StepResponse(null, brand.id) + }, + async (id, { container }) => { + if (!id) { + return + } + + const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) + + await cmsModuleService.deleteBrand(id) + } +) +``` + +You create the `syncBrandToCmsStep` that accepts a brand as an input. In the step, you resolve the CMS Module's service from the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) and use its `createBrand` method. This method will create the brand in the third-party CMS. + +You also pass the brand's ID to the step's compensation function. In this function, you delete the brand in the third-party CMS if an error occurs during the workflow's execution. + +Learn more about compensation functions in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md). + +### Create Workflow + +You can now create the workflow that uses the above step. Add the workflow to the same `src/workflows/sync-brands-to-cms.ts` file: + +```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncWorkflowHighlights} +// other imports... +import { + // ... + createWorkflow, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +type SyncBrandToCmsWorkflowInput = { + id: string +} + +export const syncBrandToCmsWorkflow = createWorkflow( + "sync-brand-to-cms", + (input: SyncBrandToCmsWorkflowInput) => { + // @ts-ignore + const { data: brands } = useQueryGraphStep({ + entity: "brand", + fields: ["*"], + filters: { + id: input.id, + }, + options: { + throwIfKeyNotFound: true, + }, + }) + + syncBrandToCmsStep({ + brand: brands[0], + } as SyncBrandToCmsStepInput) + + return new WorkflowResponse({}) + } +) +``` + +You create a `syncBrandToCmsWorkflow` that accepts the brand's ID as input. The workflow has the following steps: + +- `useQueryGraphStep`: Retrieve the brand's details using Query. You pass the brand's ID as a filter, and set the `throwIfKeyNotFound` option to true so that the step throws an error if a brand with the specified ID doesn't exist. +- `syncBrandToCmsStep`: Create the brand in the third-party CMS. + +You'll execute this workflow in the subscriber next. + +Learn more about `useQueryGraphStep` in [this reference](https://docs.medusajs.com/resources/references/helper-steps/useQueryGraphStep/index.html.md). + +*** + +## 3. Handle brand.created Event + +You now have a workflow with the logic to sync a brand to the CMS. You need to execute this workflow whenever the `brand.created` event is emitted. So, you'll create a subscriber that listens to and handle the event. + +Subscribers are created in a TypeScript or JavaScript file under the `src/subscribers` directory. So, create the file `src/subscribers/brand-created.ts` with the following content: + +![Directory structure of the Medusa application after adding the subscriber](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493774/Medusa%20Book/cms-dir-overview-5_iqqwvg.jpg) + +```ts title="src/subscribers/brand-created.ts" highlights={subscriberHighlights} +import type { + SubscriberConfig, + SubscriberArgs, +} from "@medusajs/framework" +import { syncBrandToCmsWorkflow } from "../workflows/sync-brands-to-cms" + +export default async function brandCreatedHandler({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + await syncBrandToCmsWorkflow(container).run({ + input: data, + }) +} + +export const config: SubscriberConfig = { + event: "brand.created", +} +``` + +A subscriber file must export: + +- The asynchronous function that's executed when the event is emitted. This must be the file's default export. +- An object that holds the subscriber's configurations. It has an `event` property that indicates the name of the event that the subscriber is listening to. + +The subscriber function accepts an object parameter that has two properties: + +- `event`: An object of event details. Its `data` property holds the event's data payload, which is the brand's ID. +- `container`: The Medusa container used to resolve Framework and commerce tools. + +In the function, you execute the `syncBrandToCmsWorkflow`, passing it the data payload as an input. So, everytime a brand is created, Medusa will execute this function, which in turn executes the workflow to sync the brand to the CMS. + +Learn more about subscribers in [this chapter](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). + +*** + +## Test it Out + +To test the subscriber and workflow out, you'll use the [Create Brand API route](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md) you created in a previous chapter. + +First, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users. So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route: + +```bash +curl -X POST 'http://localhost:9000/auth/user/emailpass' \ +-H 'Content-Type: application/json' \ +--data-raw '{ + "email": "admin@medusa-test.com", + "password": "supersecret" +}' +``` + +Make sure to replace the email and password with your admin user's credentials. + +Don't have an admin user? Refer to [this guide](https://docs.medusajs.com/learn/installation#create-medusa-admin-user/index.html.md). + +Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header: + +```bash +curl -X POST 'http://localhost:9000/admin/brands' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer {token}' \ +--data '{ + "name": "Acme" +}' +``` + +This request returns the created brand. If you check the logs, you'll find the `brand.created` event was emitted, and that the request to the third-party system was simulated: + +```plain +info: Processing brand.created which has 1 subscribers +http: POST /admin/brands ← - (200) - 16.418 ms +info: Sending a POST request to /brands. +info: Request Data: { + "id": "01JEDWENYD361P664WRQPMC3J8", + "name": "Acme", + "created_at": "2024-12-06T11:42:32.909Z", + "updated_at": "2024-12-06T11:42:32.909Z", + "deleted_at": null +} +info: API Key: "123" +``` + +*** + +## Next Chapter: Sync Brand from Third-Party CMS to Medusa + +You can also automate syncing data from a third-party system to Medusa at a regular interval. In the next chapter, you'll learn how to sync brands from the third-party CMS to Medusa once a day. + + +# Create Brands UI Route in Admin + +In this chapter, you'll add a UI route to the admin dashboard that shows all [brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) in a new page. You'll retrieve the brands from the server and display them in a table with pagination. + +### Prerequisites + +- [Brands Module](https://docs.medusajs.com/learn/customization/custom-features/modules/index.html.md) + +## 1. Get Brands API Route + +In a [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/query-linked-records/index.html.md), you learned how to add an API route that retrieves brands and their products using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). You'll expand that API route to support pagination, so that on the admin dashboard you can show the brands in a paginated table. + +Replace or create the `GET` API route at `src/api/admin/brands/route.ts` with the following: + +```ts title="src/api/admin/brands/route.ts" highlights={apiRouteHighlights} +// other imports... +import { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const query = req.scope.resolve("query") + + const { + data: brands, + metadata: { count, take, skip } = {}, + } = await query.graph({ + entity: "brand", + ...req.queryConfig, + }) + + res.json({ + brands, + count, + limit: take, + offset: skip, + }) +} +``` + +In the API route, you use Query's `graph` method to retrieve the brands. In the method's object parameter, you spread the `queryConfig` property of the request object. This property holds configurations for pagination and retrieved fields. + +The query configurations are combined from default configurations, which you'll add next, and the request's query parameters: + +- `fields`: The fields to retrieve in the brands. +- `limit`: The maximum number of items to retrieve. +- `offset`: The number of items to skip before retrieving the returned items. + +When you pass pagination configurations to the `graph` method, the returned object has the pagination's details in a `metadata` property, whose value is an object having the following properties: + +- `count`: The total count of items. +- `take`: The maximum number of items returned in the `data` array. +- `skip`: The number of items skipped before retrieving the returned items. + +You return in the response the retrieved brands and the pagination configurations. + +Learn more about pagination with Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-pagination/index.html.md). + +*** + +## 2. Add Default Query Configurations + +Next, you'll set the default query configurations of the above API route and allow passing query parameters to change the configurations. + +Medusa provides a `validateAndTransformQuery` middleware that validates the accepted query parameters for a request and sets the default Query configuration. So, in `src/api/middlewares.ts`, add a new middleware configuration object: + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + validateAndTransformQuery, +} from "@medusajs/framework/http" +import { createFindParams } from "@medusajs/medusa/api/utils/validators" +// other imports... + +export const GetBrandsSchema = createFindParams() + +export default defineMiddlewares({ + routes: [ + // ... + { + matcher: "/admin/brands", + method: "GET", + middlewares: [ + validateAndTransformQuery( + GetBrandsSchema, + { + defaults: [ + "id", + "name", + "products.*", + ], + isList: true, + } + ), + ], + }, + + ], +}) +``` + +You apply the `validateAndTransformQuery` middleware on the `GET /admin/brands` API route. The middleware accepts two parameters: + +- A [Zod](https://zod.dev/) schema that a request's query parameters must satisfy. Medusa provides `createFindParams` that generates a Zod schema with the following properties: + - `fields`: A comma-separated string indicating the fields to retrieve. + - `limit`: The maximum number of items to retrieve. + - `offset`: The number of items to skip before retrieving the returned items. + - `order`: The name of the field to sort the items by. Learn more about sorting in [the API reference](https://docs.medusajs.com/api/admin#sort-order) +- An object of Query configurations having the following properties: + - `defaults`: An array of default fields and relations to retrieve. + - `isList`: Whether the API route returns a list of items. + +By applying the above middleware, you can pass pagination configurations to `GET /admin/brands`, which will return a paginated list of brands. You'll see how it works when you create the UI route. + +Learn more about using the `validateAndTransformQuery` middleware to configure Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query#request-query-configurations/index.html.md). + +*** + +## 3. Initialize JS SDK + +In your custom UI route, you'll retrieve the brands by sending a request to the Medusa server. Medusa has a [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) that simplifies sending requests to the core API route. + +If you didn't follow the [previous chapter](https://docs.medusajs.com/learn/customization/customize-admin/widget/index.html.md), create the file `src/admin/lib/sdk.ts` with the following content: + +![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) + +```ts title="src/admin/lib/sdk.ts" +import Medusa from "@medusajs/js-sdk" + +export const sdk = new Medusa({ + baseUrl: import.meta.env.VITE_BACKEND_URL || "/", + debug: import.meta.env.DEV, + auth: { + type: "session", + }, +}) +``` + +You initialize the SDK passing it the following options: + +- `baseUrl`: The URL to the Medusa server. +- `debug`: Whether to enable logging debug messages. This should only be enabled in development. +- `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. + +Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). + +You can now use the SDK to send requests to the Medusa server. + +Learn more about the JS SDK and its options in [this reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). + +*** + +## 4. Add a UI Route to Show Brands + +You'll now add the UI route that shows the paginated list of brands. A UI route is a React component created in a `page.tsx` file under a sub-directory of `src/admin/routes`. The file's path relative to src/admin/routes determines its path in the dashboard. + +Learn more about UI routes in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). + +So, to add the UI route at the `localhost:9000/app/brands` path, create the file `src/admin/routes/brands/page.tsx` with the following content: + +![Directory structure of the Medusa application after adding the UI route.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733472011/Medusa%20Book/brands-admin-dir-overview-3_syytld.jpg) + +```tsx title="src/admin/routes/brands/page.tsx" highlights={uiRouteHighlights} +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { TagSolid } from "@medusajs/icons" +import { + Container, +} from "@medusajs/ui" +import { useQuery } from "@tanstack/react-query" +import { sdk } from "../../lib/sdk" +import { useMemo, useState } from "react" + +const BrandsPage = () => { + // TODO retrieve brands + + return ( + + {/* TODO show brands */} + + ) +} + +export const config = defineRouteConfig({ + label: "Brands", + icon: TagSolid, +}) + +export default BrandsPage +``` + +A route's file must export the React component that will be rendered in the new page. It must be the default export of the file. You can also export configurations that add a link in the sidebar for the UI route. You create these configurations using `defineRouteConfig` from the Admin Extension SDK. + +So far, you only show a container. In admin customizations, use components from the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md) to maintain a consistent user interface and design in the dashboard. + +### Retrieve Brands From API Route + +You'll now update the UI route to retrieve the brands from the API route you added earlier. + +First, add the following type in `src/admin/routes/brands/page.tsx`: + +```tsx title="src/admin/routes/brands/page.tsx" +type Brand = { + id: string + name: string +} +type BrandsResponse = { + brands: Brand[] + count: number + limit: number + offset: number +} +``` + +You define the type for a brand, and the type of expected response from the `GET /admin/brands` API route. + +To display the brands, you'll use Medusa UI's [DataTable](https://docs.medusajs.com/ui/components/data-table/index.html.md) component. So, add the following imports in `src/admin/routes/brands/page.tsx`: + +```tsx title="src/admin/routes/brands/page.tsx" +import { + // ... + Heading, + createDataTableColumnHelper, + DataTable, + DataTablePaginationState, + useDataTable, +} from "@medusajs/ui" +``` + +You import the `DataTable` component and the following utilities: + +- `createDataTableColumnHelper`: A utility to create columns for the data table. +- `DataTablePaginationState`: A type that holds the pagination state of the data table. +- `useDataTable`: A hook to initialize and configure the data table. + +You also import the `Heading` component to show a heading above the data table. + +Next, you'll define the table's columns. Add the following before the `BrandsPage` component: + +```tsx title="src/admin/routes/brands/page.tsx" +const columnHelper = createDataTableColumnHelper() + +const columns = [ + columnHelper.accessor("id", { + header: "ID", + }), + columnHelper.accessor("name", { + header: "Name", + }), +] +``` + +You use the `createDataTableColumnHelper` utility to create columns for the data table. You define two columns for the ID and name of the brands. + +Then, replace the `// TODO retrieve brands` in the component with the following: + +```tsx title="src/admin/routes/brands/page.tsx" highlights={queryHighlights} +const limit = 15 +const [pagination, setPagination] = useState({ + pageSize: limit, + pageIndex: 0, +}) +const offset = useMemo(() => { + return pagination.pageIndex * limit +}, [pagination]) + +const { data, isLoading } = useQuery({ + queryFn: () => sdk.client.fetch(`/admin/brands`, { + query: { + limit, + offset, + }, + }), + queryKey: [["brands", limit, offset]], +}) + +// TODO configure data table +``` + +To enable pagination in the `DataTable` component, you need to define a state variable of type `DataTablePaginationState`. It's an object having the following properties: + +- `pageSize`: The maximum number of items per page. You set it to `15`. +- `pageIndex`: A zero-based index of the current page of items. + +You also define a memoized `offset` value that indicates the number of items to skip before retrieving the current page's items. + +Then, you use `useQuery` from [Tanstack (React) Query](https://tanstack.com/query/latest) to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. + +Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. + +In the `queryFn` function that executes the query, you use the JS SDK's `client.fetch` method to send a request to your custom API route. The first parameter is the route's path, and the second is an object of request configuration and data. You pass the query parameters in the `query` property. + +This sends a request to the [Get Brands API route](#1-get-brands-api-route), passing the pagination query parameters. Whenever `currentPage` is updated, the `offset` is also updated, which will send a new request to retrieve the brands for the current page. + +### Display Brands Table + +Finally, you'll display the brands in a data table. Replace the `// TODO configure data table` in the component with the following: + +```tsx title="src/admin/routes/brands/page.tsx" +const table = useDataTable({ + columns, + data: data?.brands || [], + getRowId: (row) => row.id, + rowCount: data?.count || 0, + isLoading, + pagination: { + state: pagination, + onPaginationChange: setPagination, + }, +}) +``` + +You use the `useDataTable` hook to initialize and configure the data table. It accepts an object with the following properties: + +- `columns`: The columns of the data table. You created them using the `createDataTableColumnHelper` utility. +- `data`: The brands to display in the table. +- `getRowId`: A function that returns a unique identifier for a row. +- `rowCount`: The total count of items. This is used to determine the number of pages. +- `isLoading`: A boolean indicating whether the data is loading. +- `pagination`: An object to configure pagination. It accepts the following properties: + - `state`: The pagination state of the data table. + - `onPaginationChange`: A function to update the pagination state. + +Then, replace the `{/* TODO show brands */}` in the return statement with the following: + +```tsx title="src/admin/routes/brands/page.tsx" + + + Brands + + + + +``` + +This renders the data table that shows the brands with pagination. The `DataTable` component accepts the `instance` prop, which is the object returned by the `useDataTable` hook. + +*** + +## Test it Out + +To test out the UI route, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, open the admin dashboard at `http://localhost:9000/app`. After you log in, you'll find a new "Brands" sidebar item. Click on it to see the brands in your store. You can also go to `http://localhost:9000/app/brands` to see the page. + +![A new sidebar item is added for the new brands UI route. The UI route shows the table of brands with pagination.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733421074/Medusa%20Book/Screenshot_2024-12-05_at_7.46.52_PM_slcdqd.png) + +*** + +## Summary + +By following the previous chapters, you: + +- Injected a widget into the product details page to show the product's brand. +- Created a UI route in the Medusa Admin that shows the list of brands. + +*** + +## Next Steps: Integrate Third-Party Systems + +Your customizations often span across systems, where you need to retrieve data or perform operations in a third-party system. + +In the next chapters, you'll learn about the concepts that facilitate integrating third-party systems in your application. You'll integrate a dummy third-party system and sync the brands between it and the Medusa application. + + +# Guide: Add Product's Brand Widget in Admin + +In this chapter, you'll customize the product details page of the Medusa Admin dashboard to show the product's [brand](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md). You'll create a widget that is injected into a pre-defined zone in the page, and in the widget you'll retrieve the product's brand from the server and display it. + +### Prerequisites + +- [Brands linked to products](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) + +## 1. Initialize JS SDK + +In your custom widget, you'll retrieve the product's brand by sending a request to the Medusa server. Medusa has a [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) that simplifies sending requests to the server's API routes. + +So, you'll start by configuring the JS SDK. Create the file `src/admin/lib/sdk.ts` with the following content: + +![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) + +```ts title="src/admin/lib/sdk.ts" +import Medusa from "@medusajs/js-sdk" + +export const sdk = new Medusa({ + baseUrl: import.meta.env.VITE_BACKEND_URL || "/", + debug: import.meta.env.DEV, + auth: { + type: "session", + }, +}) +``` + +You initialize the SDK passing it the following options: + +- `baseUrl`: The URL to the Medusa server. +- `debug`: Whether to enable logging debug messages. This should only be enabled in development. +- `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. + +Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). + +You can now use the SDK to send requests to the Medusa server. + +Learn more about the JS SDK and its options in [this reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). + +*** + +## 2. Add Widget to Product Details Page + +You'll now add a widget to the product-details page. A widget is a React component that's injected into pre-defined zones in the Medusa Admin dashboard. It's created in a `.tsx` file under the `src/admin/widgets` directory. + +Learn more about widgets in [this documentation](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md). + +To create a widget that shows a product's brand in its details page, create the file `src/admin/widgets/product-brand.tsx` with the following content: + +![Directory structure of the Medusa application after adding the widget](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414684/Medusa%20Book/brands-admin-dir-overview-2_eq5xhi.jpg) + +```tsx title="src/admin/widgets/product-brand.tsx" highlights={highlights} +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { DetailWidgetProps, AdminProduct } from "@medusajs/framework/types" +import { clx, Container, Heading, Text } from "@medusajs/ui" +import { useQuery } from "@tanstack/react-query" +import { sdk } from "../lib/sdk" + +type AdminProductBrand = AdminProduct & { + brand?: { + id: string + name: string + } +} + +const ProductBrandWidget = ({ + data: product, +}: DetailWidgetProps) => { + const { data: queryResult } = useQuery({ + queryFn: () => sdk.admin.product.retrieve(product.id, { + fields: "+brand.*", + }), + queryKey: [["product", product.id]], + }) + const brandName = (queryResult?.product as AdminProductBrand)?.brand?.name + + return ( + +
+
+ Brand +
+
+
+ + Name + + + + {brandName || "-"} + +
+
+ ) +} + +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductBrandWidget +``` + +A widget's file must export: + +- A React component to be rendered in the specified injection zone. The component must be the file's default export. +- A configuration object created with `defineWidgetConfig` from the Admin Extension SDK. The function receives an object as a parameter that has a `zone` property, whose value is the zone to inject the widget to. + +Since the widget is injected at the top of the product details page, the widget receives the product's details as a parameter. + +In the widget, you use [Tanstack (React) Query](https://tanstack.com/query/latest) to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. In the `queryFn` function that executes the query, you use the JS SDK to send a request to the [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid), passing `+brand.*` in the `fields` query parameter to retrieve the product's brand. + +Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. + +You then render a section that shows the brand's name. In admin customizations, use components from the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md) to maintain a consistent user interface and design in the dashboard. + +*** + +## Test it Out + +To test out your widget, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, open the admin dashboard at `http://localhost:9000/app`. After you log in, open the page of a product that has a brand. You'll see a new section at the top showing the brand's name. + +![The widget is added as the first section of the product details page.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414415/Medusa%20Book/Screenshot_2024-12-05_at_5.59.25_PM_y85m14.png) + +*** + +## Admin Components Guides + +When building your widget, you may need more complicated components. For example, you may add a form to the above widget to set the product's brand. + +The [Admin Components guides](https://docs.medusajs.com/resources/admin-components/index.html.md) show you how to build and use common components in the Medusa Admin, such as forms, tables, JSON data viewer, and more. The components in the guides also follow the Medusa Admin's design convention. + +*** + +## Next Chapter: Add UI Route for Brands + +In the next chapter, you'll add a UI route that displays the list of brands in your application and allows admin users. + + +# Guide: Integrate Third-Party Brand System + +In the previous chapters, you've created a [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) that adds brands to your application. In this chapter, you'll integrate a dummy Content-Management System (CMS) in a new module. The module's service will provide methods to retrieve and manage brands in the CMS. You'll later use this service to sync data from and to the CMS. + +Learn more about modules in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). + +## 1. Create Module Directory + +You'll integrate the third-party system in a new CMS Module. So, create the directory `src/modules/cms` that will hold the module's resources. + +![Directory structure after adding the directory for the CMS Module](https://res.cloudinary.com/dza7lstvk/image/upload/v1733492447/Medusa%20Book/cms-dir-overview-1_gasguk.jpg) + +*** + +## 2. Create Module Service + +Next, you'll create the module's service. It will provide methods to connect and perform actions with the third-party system. + +Create the CMS Module's service at `src/modules/cms/service.ts` with the following content: + +![Directory structure after adding the CMS Module's service](https://res.cloudinary.com/dza7lstvk/image/upload/v1733492583/Medusa%20Book/cms-dir-overview-2_zwcwh3.jpg) + +```ts title="src/modules/cms/service.ts" highlights={serviceHighlights} +import { Logger, ConfigModule } from "@medusajs/framework/types" + +export type ModuleOptions = { + apiKey: string +} + +type InjectedDependencies = { + logger: Logger + configModule: ConfigModule +} + +class CmsModuleService { + private options_: ModuleOptions + private logger_: Logger + + constructor({ logger }: InjectedDependencies, options: ModuleOptions) { + this.logger_ = logger + this.options_ = options + + // TODO initialize SDK + } +} + +export default CmsModuleService +``` + +You create a `CmsModuleService` that will hold the methods to connect to the third-party CMS. A service's constructor accepts two parameters: + +1. The module's container. Since a module is [isolated](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md), it has a [local container](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md) different than the Medusa container you use in other customizations. This container holds Framework tools like the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) and resources within the module. +2. Options passed to the module when it's later added in Medusa's configurations. These options are useful to pass secret keys or configurations that ensure your module is re-usable across applications. For the CMS Module, you accept the API key to connect to the dummy CMS as an option. + +When integrating a third-party system that has a Node.js SDK or client, you can initialize that client in the constructor to be used in the service's methods. + +### Integration Methods + +Next, you'll add methods that simulate sending requests to a third-party CMS. You'll use these methods later to sync brands from and to the CMS. + +Add the following methods in the `CmsModuleService`: + +```ts title="src/modules/cms/service.ts" highlights={methodsHighlights} +export class CmsModuleService { + // ... + + // a dummy method to simulate sending a request, + // in a realistic scenario, you'd use an SDK, fetch, or axios clients + private async sendRequest(url: string, method: string, data?: any) { + this.logger_.info(`Sending a ${method} request to ${url}.`) + this.logger_.info(`Request Data: ${JSON.stringify(data, null, 2)}`) + this.logger_.info(`API Key: ${JSON.stringify(this.options_.apiKey, null, 2)}`) + } + + async createBrand(brand: Record) { + await this.sendRequest("/brands", "POST", brand) + } + + async deleteBrand(id: string) { + await this.sendRequest(`/brands/${id}`, "DELETE") + } + + async retrieveBrands(): Promise[]> { + await this.sendRequest("/brands", "GET") + + return [] + } +} +``` + +The `sendRequest` method sends requests to the third-party CMS. Since this guide isn't using a real CMS, it only simulates the sending by logging messages in the terminal. + +You also add three methods that use the `sendRequest` method: + +- `createBrand` that creates a brand in the third-party system. +- `deleteBrand` that deletes the brand in the third-party system. +- `retrieveBrands` to retrieve a brand from the third-party system. + +*** + +## 3. Export Module Definition + +After creating the module's service, you'll export the module definition indicating the module's name and service. + +Create the file `src/modules/cms/index.ts` with the following content: + +![Directory structure of the Medusa application after adding the module definition file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733492991/Medusa%20Book/cms-dir-overview-3_b0byks.jpg) + +```ts title="src/modules/cms/index.ts" +import { Module } from "@medusajs/framework/utils" +import CmsModuleService from "./service" + +export const CMS_MODULE = "cms" + +export default Module(CMS_MODULE, { + service: CmsModuleService, +}) +``` + +You use `Module` from the Modules SDK to export the module's defintion, indicating that the module's name is `cms` and its service is `CmsModuleService`. + +*** + +## 4. Add Module to Medusa's Configurations + +Finally, add the module to the Medusa configurations at `medusa-config.ts`: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + modules: [ + // ... + { + resolve: "./src/modules/cms", + options: { + apiKey: process.env.CMS_API_KEY, + }, + }, + ], +}) +``` + +The object passed in `modules` accept an `options` property, whose value is an object of options to pass to the module. These are the options you receive in the `CmsModuleService`'s constructor. + +You can add the `CMS_API_KEY` environment variable to `.env`: + +```bash +CMS_API_KEY=123 +``` + +*** + +## Next Steps: Sync Brand From Medusa to CMS + +You can now use the CMS Module's service to perform actions on the third-party CMS. + +In the next chapter, you'll learn how to emit an event when a brand is created, then handle that event to sync the brand from Medusa to the third-party service. + + +# Guide: Extend Create Product Flow + +After linking the [custom Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) and Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) in the [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md), you'll extend the create product workflow and API route to allow associating a brand with a product. + +Some API routes, including the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), accept an `additional_data` request body parameter. This parameter can hold custom data that's passed to the [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md) of the workflow executed in the API route, allowing you to consume those hooks and perform actions with the custom data. + +So, in this chapter, to extend the create product flow and associate a brand with a product, you will: + +- Consume the [productsCreated](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow#productsCreated/index.html.md) hook of the [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md), which is executed within the workflow after the product is created. You'll link the product with the brand passed in the `additional_data` parameter. +- Extend the Create Product API route to allow passing a brand ID in `additional_data`. + +To learn more about the `additional_data` property and the API routes that accept additional data, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md). + +### Prerequisites + +- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) +- [Defined link between the Brand and Product data models.](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) + +*** + +## 1. Consume the productsCreated Hook + +A workflow hook is a point in a workflow where you can inject a step to perform a custom functionality. Consuming a workflow hook allows you to extend the features of a workflow and, consequently, the API route that uses it. + +Learn more about the workflow hooks in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md). + +The [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) used in the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts) has a `productsCreated` hook that runs after the product is created. You'll consume this hook to link the created product with the brand specified in the request parameters. + +To consume the `productsCreated` hook, create the file `src/workflows/hooks/created-product.ts` with the following content: + +![Directory structure after creating the hook's file.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733384338/Medusa%20Book/brands-hook-dir-overview_ltwr5h.jpg) + +```ts title="src/workflows/hooks/created-product.ts" highlights={hook1Highlights} +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" +import { StepResponse } from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" +import { LinkDefinition } from "@medusajs/framework/types" +import { BRAND_MODULE } from "../../modules/brand" +import BrandModuleService from "../../modules/brand/service" + +createProductsWorkflow.hooks.productsCreated( + (async ({ products, additional_data }, { container }) => { + if (!additional_data?.brand_id) { + return new StepResponse([], []) + } + + const brandModuleService: BrandModuleService = container.resolve( + BRAND_MODULE + ) + // if the brand doesn't exist, an error is thrown. + await brandModuleService.retrieveBrand(additional_data.brand_id as string) + + // TODO link brand to product + }) +) +``` + +Workflows have a special `hooks` property to access its hooks and consume them. Each hook, such as `productsCreated`, accepts a step function as a parameter. The step function accepts the following parameters: + +1. An object having an `additional_data` property, which is the custom data passed in the request body under `additional_data`. The object will also have properties passed from the workflow to the hook, which in this case is the `products` property that holds an array of the created products. +2. An object of properties related to the step's context. It has a `container` property whose value is the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) to resolve Framework and commerce tools. + +In the step, if a brand ID is passed in `additional_data`, you resolve the Brand Module's service and use its generated `retrieveBrand` method to retrieve the brand by its ID. The `retrieveBrand` method will throw an error if the brand doesn't exist. + +### Link Brand to Product + +Next, you want to create a link between the created products and the brand. To do so, you use Link, which is a class from the Modules SDK that provides methods to manage linked records. + +Learn more about Link in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md). + +To use Link in the `productsCreated` hook, replace the `TODO` with the following: + +```ts title="src/workflows/hooks/created-product.ts" highlights={hook2Highlights} +const link = container.resolve("link") +const logger = container.resolve("logger") + +const links: LinkDefinition[] = [] + +for (const product of products) { + links.push({ + [Modules.PRODUCT]: { + product_id: product.id, + }, + [BRAND_MODULE]: { + brand_id: additional_data.brand_id, + }, + }) +} + +await link.create(links) + +logger.info("Linked brand to products") + +return new StepResponse(links, links) +``` + +You resolve Link from the container. Then you loop over the created products to assemble an array of links to be created. After that, you pass the array of links to Link's `create` method, which will link the product and brand records. + +Each property in the link object is the name of a module, and its value is an object having a `{model_name}_id` property, where `{model_name}` is the snake-case name of the module's data model. Its value is the ID of the record to be linked. The link object's properties must be set in the same order as the link configurations passed to `defineLink`. + +![Diagram showcasing how the order of defining a link affects creating the link](https://res.cloudinary.com/dza7lstvk/image/upload/v1733386156/Medusa%20Book/remote-link-brand-product-exp_fhjmg4.jpg) + +Finally, you return an instance of `StepResponse` returning the created links. + +### Dismiss Links in Compensation + +You can pass as a second parameter of the hook a compensation function that undoes what the step did. It receives as a first parameter the returned `StepResponse`'s second parameter, and the step context object as a second parameter. + +To undo creating the links in the hook, pass the following compensation function as a second parameter to `productsCreated`: + +```ts title="src/workflows/hooks/created-product.ts" +createProductsWorkflow.hooks.productsCreated( + // ... + (async (links, { container }) => { + if (!links?.length) { + return + } + + const link = container.resolve("link") + + await link.dismiss(links) + }) +) +``` + +In the compensation function, if the `links` parameter isn't empty, you resolve Link from the container and use its `dismiss` method. This method removes a link between two records. It accepts the same parameter as the `create` method. + +*** + +## 2. Configure Additional Data Validation + +Now that you've consumed the `productsCreated` hook, you want to configure the `/admin/products` API route that creates a new product to accept a brand ID in its `additional_data` parameter. + +You configure the properties accepted in `additional_data` in the `src/api/middlewares.ts` that exports middleware configurations. So, create the file (or, if already existing, add to the file) `src/api/middlewares.ts` the following content: + +![Directory structure after adding the middelwares file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733386868/Medusa%20Book/brands-middleware-dir-overview_uczos1.jpg) + +```ts title="src/api/middlewares.ts" +import { defineMiddlewares } from "@medusajs/framework/http" +import { z } from "zod" + +// ... + +export default defineMiddlewares({ + routes: [ + // ... + { + matcher: "/admin/products", + method: ["POST"], + additionalDataValidator: { + brand_id: z.string().optional(), + }, + }, + ], +}) +``` + +Objects in `routes` accept an `additionalDataValidator` property that configures the validation rules for custom properties passed in the `additional_data` request parameter. It accepts an object whose keys are custom property names, and their values are validation rules created using [Zod](https://zod.dev/). + +So, `POST` requests sent to `/admin/products` can now pass the ID of a brand in the `brand_id` property of `additional_data`. + +*** + +## Test it Out + +To test it out, first, retrieve the authentication token of your admin user by sending a `POST` request to `/auth/user/emailpass`: + +```bash +curl -X POST 'http://localhost:9000/auth/user/emailpass' \ +-H 'Content-Type: application/json' \ +--data-raw '{ + "email": "admin@medusa-test.com", + "password": "supersecret" +}' +``` + +Make sure to replace the email and password in the request body with your user's credentials. + +Then, send a `POST` request to `/admin/products` to create a product, and pass in the `additional_data` parameter a brand's ID: + +```bash +curl -X POST 'http://localhost:9000/admin/products' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer {token}' \ +--data '{ + "title": "Product 1", + "options": [ + { + "title": "Default option", + "values": ["Default option value"] + } + ], + "shipping_profile_id": "{shipping_profile_id}", + "additional_data": { + "brand_id": "{brand_id}" + } +}' +``` + +Make sure to replace `{token}` with the token you received from the previous request, `shipping_profile_id` with the ID of a shipping profile in your application, and `{brand_id}` with the ID of a brand in your application. You can retrieve the ID of a shipping profile either from the Medusa Admin, or the [List Shipping Profiles API route](https://docs.medusajs.com/api/admin#shipping-profiles_getshippingprofiles). + +The request creates a product and returns it. + +In the Medusa application's logs, you'll find the message `Linked brand to products`, indicating that the workflow hook handler ran and linked the brand to the products. + +*** + +## Next Steps: Query Linked Brands and Products + +Now that you've extending the create-product flow to link a brand to it, you want to retrieve the brand details of a product. You'll learn how to do so in the next chapter. + + # Guide: Define Module Link Between Brand and Product In this chapter, you'll learn how to define a module link between a brand defined in the [custom Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md), and a product defined in the [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) that's available in your Medusa application out-of-the-box. @@ -5660,6 +6829,959 @@ The `moduleIntegrationTestRunner` function creates a database with a random name To manage that database, such as changing its name or perform operations on it in your tests, refer to [the Test Tooling Reference](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md). +# Environment Variables in Admin Customizations + +In this chapter, you'll learn how to use environment variables in your admin customizations. + +To learn how environment variables are generally loaded in Medusa based on your application's environment, check out [this chapter](https://docs.medusajs.com/learn/fundamentals/environment-variables/index.html.md). + +## How to Set Environment Variables + +The Medusa Admin is built on top of [Vite](https://vite.dev/). To set an environment variable that you want to use in a widget or UI route, prefix the environment variable with `VITE_`. + +For example: + +```plain +VITE_MY_API_KEY=sk_123 +``` + +*** + +## How to Use Environment Variables + +To access or use an environment variable starting with `VITE_`, use the `import.meta.env` object. + +For example: + +```tsx highlights={[["8"]]} +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { Container, Heading } from "@medusajs/ui" + +const ProductWidget = () => { + return ( + +
+ API Key: {import.meta.env.VITE_MY_API_KEY} +
+
+ ) +} + +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductWidget +``` + +In this example, you display the API key in a widget using `import.meta.env.VITE_MY_API_KEY`. + +### Type Error on import.meta.env + +If you receive a type error on `import.meta.env`, create the file `src/admin/vite-env.d.ts` with the following content: + +```ts title="src/admin/vite-env.d.ts" +/// +``` + +This file tells TypeScript to recognize the `import.meta.env` object and enhances the types of your custom environment variables. + +*** + +## Check Node Environment in Admin Customizations + +To check the current environment, Vite exposes two variables: + +- `import.meta.env.DEV`: Returns `true` if the current environment is development. +- `import.meta.env.PROD`: Returns `true` if the current environment is production. + +Learn more about other Vite environment variables in the [Vite documentation](https://vite.dev/guide/env-and-mode). + +*** + +## Environment Variables in Production + +When you build the Medusa application, including the Medusa Admin, with the `build` command, the environment variables are inlined into the build. This means that you can't change the environment variables without rebuilding the application. + +For example, the `VITE_MY_API_KEY` environment variable in the example above will be replaced with the actual value during the build process. + + +# Admin Development Constraints + +This chapter lists some constraints of admin widgets and UI routes. + +## Arrow Functions + +Widget and UI route components must be created as arrow functions. + +```ts highlights={arrowHighlights} +// Don't +function ProductWidget() { + // ... +} + +// Do +const ProductWidget = () => { + // ... +} +``` + +*** + +## Widget Zone + +A widget zone's value must be wrapped in double or single quotes. It can't be a template literal or a variable. + +```ts highlights={zoneHighlights} +// Don't +export const config = defineWidgetConfig({ + zone: `product.details.before`, +}) + +// Don't +const ZONE = "product.details.after" +export const config = defineWidgetConfig({ + zone: ZONE, +}) + +// Do +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) +``` + + +# Admin Routing Customizations + +The Medusa Admin dashboard uses [React Router](https://reactrouter.com) under the hood to manage routing. So, you can have more flexibility in routing-related customizations using some of React Router's utilities, hooks, and components. + +In this chapter, you'll learn about routing-related customizations that you can use in your admin customizations using React Router. + +`react-router-dom` is available in your project by default through the Medusa packages. You don't need to install it separately. + +## Link to a Page + +To link to a page in your admin customizations, you can use the `Link` component from `react-router-dom`. For example: + +```tsx title="src/admin/widgets/product-widget.tsx" highlights={highlights} +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { Container } from "@medusajs/ui" +import { Link } from "react-router-dom" + +// The widget +const ProductWidget = () => { + return ( + + View Orders + + ) +} + +// The widget's configurations +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductWidget +``` + +This adds a widget to a product's details page with a link to the Orders page. The link's path must be without the `/app` prefix. + +*** + +## Admin Route Loader + +Route loaders are available starting from Medusa v2.5.1. + +In your UI route or any other custom admin route, you may need to retrieve data to use it in your route component. For example, you may want to fetch a list of products to display on a custom page. + +To do that, you can export a `loader` function in the route file, which is a [React Router loader](https://reactrouter.com/6.29.0/route/loader#loader). In this function, you can fetch and return data asynchronously. Then, in your route component, you can use the [useLoaderData](https://reactrouter.com/6.29.0/hooks/use-loader-data#useloaderdata) hook from React Router to access the data. + +For example, consider the following UI route created at `src/admin/routes/custom/page.tsx`: + +```tsx title="src/admin/routes/custom/page.tsx" highlights={loaderHighlights} +import { Container, Heading } from "@medusajs/ui" +import { + useLoaderData, +} from "react-router-dom" + +export async function loader() { + // TODO fetch products + + return { + products: [], + } +} + +const CustomPage = () => { + const { products } = useLoaderData() as Awaited> + + return ( +
+ +
+ Products count: {products.length} +
+
+
+ ) +} + +export default CustomPage +``` + +In this example, you first export a `loader` function that can be used to fetch data, such as products. The function returns an object with a `products` property. + +Then, in the `CustomPage` route component, you use the `useLoaderData` hook from React Router to access the data returned by the `loader` function. You can then use the data in your component. + +### Route Parameters + +You can also access route params in the loader function. For example, consider the following UI route created at `src/admin/routes/custom/[id]/page.tsx`: + +```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={loaderParamHighlights} +import { Container, Heading } from "@medusajs/ui" +import { + useLoaderData, + LoaderFunctionArgs, +} from "react-router-dom" + +export async function loader({ params }: LoaderFunctionArgs) { + const { id } = params + // TODO fetch product by id + + return { + id, + } +} + +const CustomPage = () => { + const { id } = useLoaderData() as Awaited> + + return ( +
+ +
+ Product ID: {id} +
+
+
+ ) +} + +export default CustomPage +``` + +Because the UI route has a route parameter `[id]`, you can access the `id` parameter in the `loader` function. The loader function accepts as a parameter an object of type `LoaderFunctionArgs` from React Router. This object has a `params` property that contains the route parameters. + +In the loader, you can fetch data asynchronously using the route parameter and return it. Then, in the route component, you can access the data using the `useLoaderData` hook. + +### When to Use Route Loaders + +A route loader is executed before the route is loaded. So, it will block navigation until the loader function is resolved. + +Only use route loaders when the route component needs data essential before rendering. Otherwise, use the JS SDK with Tanstack (React) Query as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/tips#send-requests-to-api-routes/index.html.md). This way, you can fetch data asynchronously and update the UI when the data is available. You can also use a loader to prepare some initial data that's used in the route component before the data is retrieved. + +*** + +## Other React Router Utilities + +### Route Handles + +Route handles are available starting from Medusa v2.5.1. + +In your UI route or any route file, you can export a `handle` object to define [route handles](https://reactrouter.com/start/framework/route-module#handle). The object is passed to the loader and route contexts. + +For example: + +```tsx title="src/admin/routes/custom/page.tsx" +export const handle = { + sandbox: true, +} +``` + +### React Router Components and Hooks + +Refer to [react-router-dom’s documentation](https://reactrouter.com/en/6.29.0) for components and hooks that you can use in your admin customizations. + + +# Admin UI Routes + +In this chapter, you’ll learn how to create a UI route in the admin dashboard. + +## What is a UI Route? + +The Medusa Admin dashboard is customizable, allowing you to add new pages, called UI routes. You create a UI route as a React component showing custom content that allow admin users to perform custom actions. + +For example, you can add a new page to show and manage product reviews, which aren't available natively in Medusa. + +*** + +## How to Create a UI Route? + +### Prerequisites + +- [Medusa application installed](https://docs.medusajs.com/learn/installation/index.html.md) + +You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/routes` directory. The file's path relative to `src/admin/routes` determines its path in the dashboard. The file’s default export must be the UI route’s React component. + +For example, create the file `src/admin/routes/custom/page.tsx` with the following content: + +![Example of UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg) + +```tsx title="src/admin/routes/custom/page.tsx" +import { Container, Heading } from "@medusajs/ui" + +const CustomPage = () => { + return ( + +
+ This is my custom route +
+
+ ) +} + +export default CustomPage +``` + +You add a new route at `http://localhost:9000/app/custom`. The `CustomPage` component holds the page's content, which currently only shows a heading. + +In the route, you use [Medusa UI](https://docs.medusajs.com/ui/index.html.md), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it. + +The UI route component must be created as an arrow function. + +### Test the UI Route + +To test the UI route, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, after logging into the admin dashboard, open the page `http://localhost:9000/app/custom` to see your custom page. + +*** + +## Show UI Route in the Sidebar + +To add a sidebar item for your custom UI route, export a configuration object in the UI route's file: + +```tsx title="src/admin/routes/custom/page.tsx" highlights={highlights} +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { ChatBubbleLeftRight } from "@medusajs/icons" +import { Container, Heading } from "@medusajs/ui" + +const CustomPage = () => { + return ( + +
+ This is my custom route +
+
+ ) +} + +export const config = defineRouteConfig({ + label: "Custom Route", + icon: ChatBubbleLeftRight, +}) + +export default CustomPage +``` + +The configuration object is created using `defineRouteConfig` from the Medusa Framework. It accepts the following properties: + +- `label`: the sidebar item’s label. +- `icon`: an optional React component used as an icon in the sidebar. + +The above example adds a new sidebar item with the label `Custom Route` and an icon from the [Medusa UI Icons package](https://docs.medusajs.com/ui/icons/overview/index.html.md). + +### Nested UI Routes + +Consider that along the UI route above at `src/admin/routes/custom/page.tsx` you create a nested UI route at `src/admin/routes/custom/nested/page.tsx` that also exports route configurations: + +![Example of nested UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg) + +```tsx title="src/admin/routes/custom/nested/page.tsx" +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { Container, Heading } from "@medusajs/ui" + +const NestedCustomPage = () => { + return ( + +
+ This is my nested custom route +
+
+ ) +} + +export const config = defineRouteConfig({ + label: "Nested Route", +}) + +export default NestedCustomPage +``` + +This UI route is shown in the sidebar as an item nested in the parent "Custom Route" item. Nested items are only shown when the parent sidebar items (in this case, "Custom Route") are clicked. + +#### Caveats + +Some caveats for nested UI routes in the sidebar: + +- Nested dynamic UI routes, such as one created at `src/admin/routes/custom/[id]/page.tsx` aren't added to the sidebar as it's not possible to link to a dynamic route. If the dynamic route exports route configurations, a warning is logged in the browser's console. +- Nested routes in setting pages aren't shown in the sidebar to follow the admin's design conventions. +- The `icon` configuration is ignored for the sidebar item of nested UI route to follow the admin's design conventions. + +### Route Under Existing Admin Route + +You can add a custom UI route under an existing route. For example, you can add a route under the orders route: + +```tsx title="src/admin/routes/orders/nested/page.tsx" +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { Container, Heading } from "@medusajs/ui" + +const NestedOrdersPage = () => { + return ( + +
+ Nested Orders Page +
+
+ ) +} + +export const config = defineRouteConfig({ + label: "Nested Orders", + nested: "/orders", +}) + +export default NestedOrdersPage +``` + +The `nested` property passed to `defineRouteConfig` specifies which route this custom route is nested under. This route will now show in the sidebar under the existing "Orders" sidebar item. + +*** + +## Create Settings Page + +To create a page under the settings section of the admin dashboard, create a UI route under the path `src/admin/routes/settings`. + +For example, create a UI route at `src/admin/routes/settings/custom/page.tsx`: + +![Example of settings UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867435/Medusa%20Book/setting-ui-route-dir-overview_kytbh8.jpg) + +```tsx title="src/admin/routes/settings/custom/page.tsx" +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { Container, Heading } from "@medusajs/ui" + +const CustomSettingPage = () => { + return ( + +
+ Custom Setting Page +
+
+ ) +} + +export const config = defineRouteConfig({ + label: "Custom", +}) + +export default CustomSettingPage +``` + +This adds a page under the path `/app/settings/custom`. An item is also added to the settings sidebar with the label `Custom`. + +*** + +## Path Parameters + +A UI route can accept path parameters if the name of any of the directories in its path is of the format `[param]`. + +For example, create the file `src/admin/routes/custom/[id]/page.tsx` with the following content: + +![Example of UI route file with path parameters in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867748/Medusa%20Book/path-param-ui-route-dir-overview_kcfbev.jpg) + +```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={[["5", "", "Retrieve the path parameter."], ["10", "{id}", "Show the path parameter."]]} +import { useParams } from "react-router-dom" +import { Container, Heading } from "@medusajs/ui" + +const CustomPage = () => { + const { id } = useParams() + + return ( + +
+ Passed ID: {id} +
+
+ ) +} + +export default CustomPage +``` + +You access the passed parameter using `react-router-dom`'s [useParams hook](https://reactrouter.com/en/main/hooks/use-params). + +If you run the Medusa application and go to `localhost:9000/app/custom/123`, you'll see `123` printed in the page. + +*** + +## Admin Components List + +To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components. + +*** + +## More Routes Customizations + +For more customizations related to routes, refer to the [Routing Customizations chapter](https://docs.medusajs.com/learn/fundamentals/admin/routing/index.html.md). + + +# Admin Development Tips + +In this chapter, you'll find some tips for your admin development. + +## Send Requests to API Routes + +To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default. + +Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. + +First, create the file `src/admin/lib/config.ts` to setup the SDK for use in your customizations: + +```ts +import Medusa from "@medusajs/js-sdk" + +export const sdk = new Medusa({ + baseUrl: import.meta.env.VITE_BACKEND_URL || "/", + debug: import.meta.env.DEV, + auth: { + type: "session", + }, +}) +``` + +Notice that you use `import.meta.env` to access environment variables in your customizations, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). + +Learn more about the JS SDK's configurations [this documentation](https://docs.medusajs.com/resources/js-sdk#js-sdk-configurations/index.html.md). + +Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests. + +For example: + +### Query + +```tsx title="src/admin/widgets/product-widget.ts" highlights={queryHighlights} +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { Button, Container } from "@medusajs/ui" +import { useQuery } from "@tanstack/react-query" +import { sdk } from "../lib/config" +import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types" + +const ProductWidget = () => { + const { data, isLoading } = useQuery({ + queryFn: () => sdk.admin.product.list(), + queryKey: ["products"], + }) + + return ( + + {isLoading && Loading...} + {data?.products && ( +
    + {data.products.map((product) => ( +
  • {product.title}
  • + ))} +
+ )} +
+ ) +} + +export const config = defineWidgetConfig({ + zone: "product.list.before", +}) + +export default ProductWidget +``` + +### Mutation + +```tsx title="src/admin/widgets/product-widget.ts" highlights={mutationHighlights} +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { Button, Container } from "@medusajs/ui" +import { useMutation } from "@tanstack/react-query" +import { sdk } from "../lib/config" +import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types" + +const ProductWidget = ({ + data: productData, +}: DetailWidgetProps) => { + const { mutateAsync } = useMutation({ + mutationFn: (payload: HttpTypes.AdminUpdateProduct) => + sdk.admin.product.update(productData.id, payload), + onSuccess: () => alert("updated product"), + }) + + const handleUpdate = () => { + mutateAsync({ + title: "New Product Title", + }) + } + + return ( + + + + ) +} + +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductWidget +``` + +You can also send requests to custom routes as explained in the [JS SDK reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). + +### Use Route Loaders for Initial Data + +You may need to retrieve data before your component is rendered, or you may need to pass some initial data to your component to be used while data is being fetched. In those cases, you can use a [route loader](https://docs.medusajs.com/learn/fundamentals/admin/routing/index.html.md). + +*** + +## Global Variables in Admin Customizations + +In your admin customizations, you can use the following global variables: + +- `__BASE__`: The base path of the Medusa Admin, as set in the [admin.path](https://docs.medusajs.com/learn/configurations/medusa-config#path/index.html.md) configuration in `medusa-config.ts`. +- `__BACKEND_URL__`: The URL to the Medusa backend, as set in the [admin.backendUrl](https://docs.medusajs.com/learn/configurations/medusa-config#backendurl/index.html.md) configuration in `medusa-config.ts`. +- `__STOREFRONT_URL__`: The URL to the storefront, as set in the [admin.storefrontUrl](https://docs.medusajs.com/learn/configurations/medusa-config#storefrontUrl/index.html.md) configuration in `medusa-config.ts`. + +*** + +## Admin Translations + +The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions. + +Learn how to add a new language translation for the Medusa Admin in [this guide](https://docs.medusajs.com/learn/resources/contribution-guidelines/admin-translations/index.html.md). + + +# Pass Additional Data to Medusa's API Route + +In this chapter, you'll learn how to pass additional data in requests to Medusa's API Route. + +## Why Pass Additional Data? + +Some of Medusa's API Routes accept an `additional_data` parameter whose type is an object. The API Route passes the `additional_data` to the workflow, which in turn passes it to its hooks. + +This is useful when you have a link from your custom module to a Commerce Module, and you want to perform an additional action when a request is sent to an existing API route. + +For example, the [Create Product API Route](https://docs.medusajs.com/api/admin#products_postproducts) accepts an `additional_data` parameter. If you have a data model linked to it, you consume the `productsCreated` hook to create a record of the data model using the custom data and link it to the product. + +### API Routes Accepting Additional Data + +### API Routes List + +- Campaigns + - [Create Campaign](https://docs.medusajs.com/api/admin#campaigns_postcampaigns) + - [Update Campaign](https://docs.medusajs.com/api/admin#campaigns_postcampaignsid) +- Cart + - [Create Cart](https://docs.medusajs.com/api/store#carts_postcarts) + - [Update Cart](https://docs.medusajs.com/api/store#carts_postcartsid) +- Collections + - [Create Collection](https://docs.medusajs.com/api/admin#collections_postcollections) + - [Update Collection](https://docs.medusajs.com/api/admin#collections_postcollectionsid) +- Customers + - [Create Customer](https://docs.medusajs.com/api/admin#customers_postcustomers) + - [Update Customer](https://docs.medusajs.com/api/admin#customers_postcustomersid) + - [Create Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddresses) + - [Update Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddressesaddress_id) +- Draft Orders + - [Create Draft Order](https://docs.medusajs.com/api/admin#draft-orders_postdraftorders) +- Orders + - [Complete Orders](https://docs.medusajs.com/api/admin#orders_postordersidcomplete) + - [Cancel Order's Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idcancel) + - [Create Shipment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idshipments) + - [Create Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillments) +- Products + - [Create Product](https://docs.medusajs.com/api/admin#products_postproducts) + - [Update Product](https://docs.medusajs.com/api/admin#products_postproductsid) + - [Create Product Variant](https://docs.medusajs.com/api/admin#products_postproductsidvariants) + - [Update Product Variant](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_id) + - [Create Product Option](https://docs.medusajs.com/api/admin#products_postproductsidoptions) + - [Update Product Option](https://docs.medusajs.com/api/admin#products_postproductsidoptionsoption_id) +- Product Tags + - [Create Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttags) + - [Update Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttagsid) +- Product Types + - [Create Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypes) + - [Update Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypesid) +- Promotions + - [Create Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotions) + - [Update Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotionsid) + +*** + +## How to Pass Additional Data + +### 1. Specify Validation of Additional Data + +Before passing custom data in the `additional_data` object parameter, you must specify validation rules for the allowed properties in the object. + +To do that, use the middleware route object defined in `src/api/middlewares.ts`. + +For example, create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" +import { defineMiddlewares } from "@medusajs/framework/http" +import { z } from "zod" + +export default defineMiddlewares({ + routes: [ + { + method: "POST", + matcher: "/admin/products", + additionalDataValidator: { + brand: z.string().optional(), + }, + }, + ], +}) +``` + +The middleware route object accepts an optional parameter `additionalDataValidator` whose value is an object of key-value pairs. The keys indicate the name of accepted properties in the `additional_data` parameter, and the value is [Zod](https://zod.dev/) validation rules of the property. + +In this example, you indicate that the `additional_data` parameter accepts a `brand` property whose value is an optional string. + +Refer to [Zod's documentation](https://zod.dev) for all available validation rules. + +### 2. Pass the Additional Data in a Request + +You can now pass a `brand` property in the `additional_data` parameter of a request to the Create Product API Route. + +For example: + +```bash +curl -X POST 'http://localhost:9000/admin/products' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer {token}' \ +--data '{ + "title": "Product 1", + "options": [ + { + "title": "Default option", + "values": ["Default option value"] + } + ], + "shipping_profile_id": "{shipping_profile_id}", + "additional_data": { + "brand": "Acme" + } +}' +``` + +Make sure to replace the `{token}` in the authorization header with an admin user's authentication token, and `{shipping_profile_id}` with an existing shipping profile's ID. + +In this request, you pass in the `additional_data` parameter a `brand` property and set its value to `Acme`. + +The `additional_data` is then passed to hooks in the `createProductsWorkflow` used by the API route. + +*** + +## Use Additional Data in a Hook + +Learn about workflow hooks in [this guide](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md). + +Step functions consuming the workflow hook can access the `additional_data` in the first parameter. + +For example, consider you want to store the data passed in `additional_data` in the product's `metadata` property. + +To do that, create the file `src/workflows/hooks/product-created.ts` with the following content: + +```ts title="src/workflows/hooks/product-created.ts" +import { StepResponse } from "@medusajs/framework/workflows-sdk" +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" +import { Modules } from "@medusajs/framework/utils" + +createProductsWorkflow.hooks.productsCreated( + async ({ products, additional_data }, { container }) => { + if (!additional_data?.brand) { + return + } + + const productModuleService = container.resolve( + Modules.PRODUCT + ) + + await productModuleService.upsertProducts( + products.map((product) => ({ + ...product, + metadata: { + ...product.metadata, + brand: additional_data.brand, + }, + })) + ) + + return new StepResponse(products, { + products, + additional_data, + }) + } +) +``` + +This consumes the `productsCreated` hook, which runs after the products are created. + +If `brand` is passed in `additional_data`, you resolve the Product Module's main service and use its `upsertProducts` method to update the products, adding the brand to the `metadata` property. + +### Compensation Function + +Hooks also accept a compensation function as a second parameter to undo the actions made by the step function. + +For example, pass the following second parameter to the `productsCreated` hook: + +```ts title="src/workflows/hooks/product-created.ts" +createProductsWorkflow.hooks.productsCreated( + async ({ products, additional_data }, { container }) => { + // ... + }, + async ({ products, additional_data }, { container }) => { + if (!additional_data.brand) { + return + } + + const productModuleService = container.resolve( + Modules.PRODUCT + ) + + await productModuleService.upsertProducts( + products + ) + } +) +``` + +This updates the products to their original state before adding the brand to their `metadata` property. + + +# Handling CORS in API Routes + +In this chapter, you’ll learn about the CORS middleware and how to configure it for custom API routes. + +## CORS Overview + +Cross-Origin Resource Sharing (CORS) allows only configured origins to access your API Routes. + +For example, if you allow only origins starting with `http://localhost:7001` to access your Admin API Routes, other origins accessing those routes get a CORS error. + +### CORS Configurations + +The `storeCors` and `adminCors` properties of Medusa's `http` configuration set the allowed origins for routes starting with `/store` and `/admin` respectively. + +These configurations accept a URL pattern to identify allowed origins. + +For example: + +```js title="medusa-config.ts" +module.exports = defineConfig({ + projectConfig: { + http: { + storeCors: "http://localhost:8000", + adminCors: "http://localhost:7001", + // ... + }, + }, +}) +``` + +This allows the `http://localhost:7001` origin to access the Admin API Routes, and the `http://localhost:8000` origin to access Store API Routes. + +Learn more about the CORS configurations in [this resource guide](https://docs.medusajs.com/learn/configurations/medusa-config#http/index.html.md). + +*** + +## CORS in Store and Admin Routes + +To disable the CORS middleware for a route, export a `CORS` variable in the route file with its value set to `false`. + +For example: + +```ts title="src/api/store/custom/route.ts" highlights={[["15"]]} +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +export const GET = ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: "[GET] Hello world!", + }) +} + +export const CORS = false +``` + +This disables the CORS middleware on API Routes at the path `/store/custom`. + +*** + +## CORS in Custom Routes + +If you create a route that doesn’t start with `/store` or `/admin`, you must apply the CORS middleware manually. Otherwise, all requests to your API route lead to a CORS error. + +You can do that in the exported middlewares configurations in `src/api/middlewares.ts`. + +For example: + +```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-10" expandButtonLabel="Show Imports" +import { defineMiddlewares } from "@medusajs/framework/http" +import type { + MedusaNextFunction, + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { ConfigModule } from "@medusajs/framework/types" +import { parseCorsOrigins } from "@medusajs/framework/utils" +import cors from "cors" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom*", + middlewares: [ + ( + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction + ) => { + const configModule: ConfigModule = + req.scope.resolve("configModule") + + return cors({ + origin: parseCorsOrigins( + configModule.projectConfig.http.storeCors + ), + credentials: true, + })(req, res, next) + }, + ], + }, + ], +}) +``` + +This retrieves the configurations exported from `medusa-config.ts` and applies the `storeCors` to routes starting with `/custom`. + + # Guide: Schedule Syncing Brands from Third-Party In the previous chapters, you've [integrated a third-party CMS](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md) and implemented the logic to [sync created brands](https://docs.medusajs.com/learn/customization/integrate-systems/handle-event/index.html.md) from Medusa to the CMS. @@ -5969,666 +8091,20 @@ By following the previous chapters, you utilized the Medusa Framework and orches With Medusa, you can integrate any service from your commerce ecosystem with ease. You don't have to set up separate applications to manage your different customizations, or worry about data inconsistency across systems. Your efforts only go into implementing the business logic that ties your systems together. -# Guide: Integrate Third-Party Brand System +# HTTP Methods -In the previous chapters, you've created a [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) that adds brands to your application. In this chapter, you'll integrate a dummy Content-Management System (CMS) in a new module. The module's service will provide methods to retrieve and manage brands in the CMS. You'll later use this service to sync data from and to the CMS. +In this chapter, you'll learn about how to add new API routes for each HTTP method. -Learn more about modules in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). +## HTTP Method Handler -## 1. Create Module Directory +An API route is created for every HTTP method you export a handler function for in a route file. -You'll integrate the third-party system in a new CMS Module. So, create the directory `src/modules/cms` that will hold the module's resources. +Allowed HTTP methods are: `GET`, `POST`, `DELETE`, `PUT`, `PATCH`, `OPTIONS`, and `HEAD`. -![Directory structure after adding the directory for the CMS Module](https://res.cloudinary.com/dza7lstvk/image/upload/v1733492447/Medusa%20Book/cms-dir-overview-1_gasguk.jpg) +For example, create the file `src/api/hello-world/route.ts` with the following content: -*** - -## 2. Create Module Service - -Next, you'll create the module's service. It will provide methods to connect and perform actions with the third-party system. - -Create the CMS Module's service at `src/modules/cms/service.ts` with the following content: - -![Directory structure after adding the CMS Module's service](https://res.cloudinary.com/dza7lstvk/image/upload/v1733492583/Medusa%20Book/cms-dir-overview-2_zwcwh3.jpg) - -```ts title="src/modules/cms/service.ts" highlights={serviceHighlights} -import { Logger, ConfigModule } from "@medusajs/framework/types" - -export type ModuleOptions = { - apiKey: string -} - -type InjectedDependencies = { - logger: Logger - configModule: ConfigModule -} - -class CmsModuleService { - private options_: ModuleOptions - private logger_: Logger - - constructor({ logger }: InjectedDependencies, options: ModuleOptions) { - this.logger_ = logger - this.options_ = options - - // TODO initialize SDK - } -} - -export default CmsModuleService -``` - -You create a `CmsModuleService` that will hold the methods to connect to the third-party CMS. A service's constructor accepts two parameters: - -1. The module's container. Since a module is [isolated](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md), it has a [local container](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md) different than the Medusa container you use in other customizations. This container holds Framework tools like the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) and resources within the module. -2. Options passed to the module when it's later added in Medusa's configurations. These options are useful to pass secret keys or configurations that ensure your module is re-usable across applications. For the CMS Module, you accept the API key to connect to the dummy CMS as an option. - -When integrating a third-party system that has a Node.js SDK or client, you can initialize that client in the constructor to be used in the service's methods. - -### Integration Methods - -Next, you'll add methods that simulate sending requests to a third-party CMS. You'll use these methods later to sync brands from and to the CMS. - -Add the following methods in the `CmsModuleService`: - -```ts title="src/modules/cms/service.ts" highlights={methodsHighlights} -export class CmsModuleService { - // ... - - // a dummy method to simulate sending a request, - // in a realistic scenario, you'd use an SDK, fetch, or axios clients - private async sendRequest(url: string, method: string, data?: any) { - this.logger_.info(`Sending a ${method} request to ${url}.`) - this.logger_.info(`Request Data: ${JSON.stringify(data, null, 2)}`) - this.logger_.info(`API Key: ${JSON.stringify(this.options_.apiKey, null, 2)}`) - } - - async createBrand(brand: Record) { - await this.sendRequest("/brands", "POST", brand) - } - - async deleteBrand(id: string) { - await this.sendRequest(`/brands/${id}`, "DELETE") - } - - async retrieveBrands(): Promise[]> { - await this.sendRequest("/brands", "GET") - - return [] - } -} -``` - -The `sendRequest` method sends requests to the third-party CMS. Since this guide isn't using a real CMS, it only simulates the sending by logging messages in the terminal. - -You also add three methods that use the `sendRequest` method: - -- `createBrand` that creates a brand in the third-party system. -- `deleteBrand` that deletes the brand in the third-party system. -- `retrieveBrands` to retrieve a brand from the third-party system. - -*** - -## 3. Export Module Definition - -After creating the module's service, you'll export the module definition indicating the module's name and service. - -Create the file `src/modules/cms/index.ts` with the following content: - -![Directory structure of the Medusa application after adding the module definition file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733492991/Medusa%20Book/cms-dir-overview-3_b0byks.jpg) - -```ts title="src/modules/cms/index.ts" -import { Module } from "@medusajs/framework/utils" -import CmsModuleService from "./service" - -export const CMS_MODULE = "cms" - -export default Module(CMS_MODULE, { - service: CmsModuleService, -}) -``` - -You use `Module` from the Modules SDK to export the module's defintion, indicating that the module's name is `cms` and its service is `CmsModuleService`. - -*** - -## 4. Add Module to Medusa's Configurations - -Finally, add the module to the Medusa configurations at `medusa-config.ts`: - -```ts title="medusa-config.ts" -module.exports = defineConfig({ - // ... - modules: [ - // ... - { - resolve: "./src/modules/cms", - options: { - apiKey: process.env.CMS_API_KEY, - }, - }, - ], -}) -``` - -The object passed in `modules` accept an `options` property, whose value is an object of options to pass to the module. These are the options you receive in the `CmsModuleService`'s constructor. - -You can add the `CMS_API_KEY` environment variable to `.env`: - -```bash -CMS_API_KEY=123 -``` - -*** - -## Next Steps: Sync Brand From Medusa to CMS - -You can now use the CMS Module's service to perform actions on the third-party CMS. - -In the next chapter, you'll learn how to emit an event when a brand is created, then handle that event to sync the brand from Medusa to the third-party service. - - -# Guide: Sync Brands from Medusa to Third-Party - -In the [previous chapter](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md), you created a CMS Module that integrates a dummy third-party system. You can now perform actions using that module within your custom flows. - -In another previous chapter, you [added a workflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) that creates a brand. After integrating the CMS, you want to sync that brand to the third-party system as well. - -Medusa has an event system that emits events when an operation is performed. It allows you to listen to those events and perform an asynchronous action in a function called a [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). This is useful to perform actions that aren't integral to the original flow, such as syncing data to a third-party system. - -Learn more about Medusa's event system and subscribers in [this chapter](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). - -In this chapter, you'll modify the `createBrandWorkflow` you created before to emit a custom event that indicates a brand was created. Then, you'll listen to that event in a subscriber to sync the brand to the third-party CMS. You'll implement the sync logic within a workflow that you execute in the subscriber. - -### Prerequisites - -- [createBrandWorkflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) -- [CMS Module](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md) - -## 1. Emit Event in createBrandWorkflow - -Since syncing the brand to the third-party system isn't integral to creating a brand, you'll emit a custom event indicating that a brand was created. - -Medusa provides an `emitEventStep` that allows you to emit an event in your workflows. So, in the `createBrandWorkflow` defined in `src/workflows/create-brand.ts`, use the `emitEventStep` helper step after the `createBrandStep`: - -```ts title="src/workflows/create-brand.ts" highlights={eventHighlights} -// other imports... -import { - emitEventStep, -} from "@medusajs/medusa/core-flows" - -// ... - -export const createBrandWorkflow = createWorkflow( - "create-brand", - (input: CreateBrandInput) => { - // ... - - emitEventStep({ - eventName: "brand.created", - data: { - id: brand.id, - }, - }) - - return new WorkflowResponse(brand) - } -) -``` - -The `emitEventStep` accepts an object parameter having two properties: - -- `eventName`: The name of the event to emit. You'll use this name later to listen to the event in a subscriber. -- `data`: The data payload to emit with the event. This data is passed to subscribers that listen to the event. You add the brand's ID to the data payload, informing the subscribers which brand was created. - -You'll learn how to handle this event in a later step. - -*** - -## 2. Create Sync to Third-Party System Workflow - -The subscriber that will listen to the `brand.created` event will sync the created brand to the third-party CMS. So, you'll implement the syncing logic in a workflow, then execute the workflow in the subscriber. - -Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution. - -Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). - -You'll create a `syncBrandToSystemWorkflow` that has two steps: - -- `useQueryGraphStep`: a step that Medusa provides to retrieve data using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). You'll use this to retrieve the brand's details using its ID. -- `syncBrandToCmsStep`: a step that you'll create to sync the brand to the CMS. - -### syncBrandToCmsStep - -To implement the step that syncs the brand to the CMS, create the file `src/workflows/sync-brands-to-cms.ts` with the following content: - -![Directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493547/Medusa%20Book/cms-dir-overview-4_u5t0ug.jpg) - -```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncStepHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" -import { InferTypeOf } from "@medusajs/framework/types" -import { Brand } from "../modules/brand/models/brand" -import { CMS_MODULE } from "../modules/cms" -import CmsModuleService from "../modules/cms/service" - -type SyncBrandToCmsStepInput = { - brand: InferTypeOf -} - -const syncBrandToCmsStep = createStep( - "sync-brand-to-cms", - async ({ brand }: SyncBrandToCmsStepInput, { container }) => { - const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) - - await cmsModuleService.createBrand(brand) - - return new StepResponse(null, brand.id) - }, - async (id, { container }) => { - if (!id) { - return - } - - const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) - - await cmsModuleService.deleteBrand(id) - } -) -``` - -You create the `syncBrandToCmsStep` that accepts a brand as an input. In the step, you resolve the CMS Module's service from the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) and use its `createBrand` method. This method will create the brand in the third-party CMS. - -You also pass the brand's ID to the step's compensation function. In this function, you delete the brand in the third-party CMS if an error occurs during the workflow's execution. - -Learn more about compensation functions in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md). - -### Create Workflow - -You can now create the workflow that uses the above step. Add the workflow to the same `src/workflows/sync-brands-to-cms.ts` file: - -```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncWorkflowHighlights} -// other imports... -import { - // ... - createWorkflow, - WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -type SyncBrandToCmsWorkflowInput = { - id: string -} - -export const syncBrandToCmsWorkflow = createWorkflow( - "sync-brand-to-cms", - (input: SyncBrandToCmsWorkflowInput) => { - // @ts-ignore - const { data: brands } = useQueryGraphStep({ - entity: "brand", - fields: ["*"], - filters: { - id: input.id, - }, - options: { - throwIfKeyNotFound: true, - }, - }) - - syncBrandToCmsStep({ - brand: brands[0], - } as SyncBrandToCmsStepInput) - - return new WorkflowResponse({}) - } -) -``` - -You create a `syncBrandToCmsWorkflow` that accepts the brand's ID as input. The workflow has the following steps: - -- `useQueryGraphStep`: Retrieve the brand's details using Query. You pass the brand's ID as a filter, and set the `throwIfKeyNotFound` option to true so that the step throws an error if a brand with the specified ID doesn't exist. -- `syncBrandToCmsStep`: Create the brand in the third-party CMS. - -You'll execute this workflow in the subscriber next. - -Learn more about `useQueryGraphStep` in [this reference](https://docs.medusajs.com/resources/references/helper-steps/useQueryGraphStep/index.html.md). - -*** - -## 3. Handle brand.created Event - -You now have a workflow with the logic to sync a brand to the CMS. You need to execute this workflow whenever the `brand.created` event is emitted. So, you'll create a subscriber that listens to and handle the event. - -Subscribers are created in a TypeScript or JavaScript file under the `src/subscribers` directory. So, create the file `src/subscribers/brand-created.ts` with the following content: - -![Directory structure of the Medusa application after adding the subscriber](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493774/Medusa%20Book/cms-dir-overview-5_iqqwvg.jpg) - -```ts title="src/subscribers/brand-created.ts" highlights={subscriberHighlights} +```ts title="src/api/hello-world/route.ts" import type { - SubscriberConfig, - SubscriberArgs, -} from "@medusajs/framework" -import { syncBrandToCmsWorkflow } from "../workflows/sync-brands-to-cms" - -export default async function brandCreatedHandler({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - await syncBrandToCmsWorkflow(container).run({ - input: data, - }) -} - -export const config: SubscriberConfig = { - event: "brand.created", -} -``` - -A subscriber file must export: - -- The asynchronous function that's executed when the event is emitted. This must be the file's default export. -- An object that holds the subscriber's configurations. It has an `event` property that indicates the name of the event that the subscriber is listening to. - -The subscriber function accepts an object parameter that has two properties: - -- `event`: An object of event details. Its `data` property holds the event's data payload, which is the brand's ID. -- `container`: The Medusa container used to resolve Framework and commerce tools. - -In the function, you execute the `syncBrandToCmsWorkflow`, passing it the data payload as an input. So, everytime a brand is created, Medusa will execute this function, which in turn executes the workflow to sync the brand to the CMS. - -Learn more about subscribers in [this chapter](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). - -*** - -## Test it Out - -To test the subscriber and workflow out, you'll use the [Create Brand API route](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md) you created in a previous chapter. - -First, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users. So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route: - -```bash -curl -X POST 'http://localhost:9000/auth/user/emailpass' \ --H 'Content-Type: application/json' \ ---data-raw '{ - "email": "admin@medusa-test.com", - "password": "supersecret" -}' -``` - -Make sure to replace the email and password with your admin user's credentials. - -Don't have an admin user? Refer to [this guide](https://docs.medusajs.com/learn/installation#create-medusa-admin-user/index.html.md). - -Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header: - -```bash -curl -X POST 'http://localhost:9000/admin/brands' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {token}' \ ---data '{ - "name": "Acme" -}' -``` - -This request returns the created brand. If you check the logs, you'll find the `brand.created` event was emitted, and that the request to the third-party system was simulated: - -```plain -info: Processing brand.created which has 1 subscribers -http: POST /admin/brands ← - (200) - 16.418 ms -info: Sending a POST request to /brands. -info: Request Data: { - "id": "01JEDWENYD361P664WRQPMC3J8", - "name": "Acme", - "created_at": "2024-12-06T11:42:32.909Z", - "updated_at": "2024-12-06T11:42:32.909Z", - "deleted_at": null -} -info: API Key: "123" -``` - -*** - -## Next Chapter: Sync Brand from Third-Party CMS to Medusa - -You can also automate syncing data from a third-party system to Medusa at a regular interval. In the next chapter, you'll learn how to sync brands from the third-party CMS to Medusa once a day. - - -# Guide: Extend Create Product Flow - -After linking the [custom Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) and Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) in the [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md), you'll extend the create product workflow and API route to allow associating a brand with a product. - -Some API routes, including the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), accept an `additional_data` request body parameter. This parameter can hold custom data that's passed to the [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md) of the workflow executed in the API route, allowing you to consume those hooks and perform actions with the custom data. - -So, in this chapter, to extend the create product flow and associate a brand with a product, you will: - -- Consume the [productsCreated](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow#productsCreated/index.html.md) hook of the [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md), which is executed within the workflow after the product is created. You'll link the product with the brand passed in the `additional_data` parameter. -- Extend the Create Product API route to allow passing a brand ID in `additional_data`. - -To learn more about the `additional_data` property and the API routes that accept additional data, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md). - -### Prerequisites - -- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) -- [Defined link between the Brand and Product data models.](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) - -*** - -## 1. Consume the productsCreated Hook - -A workflow hook is a point in a workflow where you can inject a step to perform a custom functionality. Consuming a workflow hook allows you to extend the features of a workflow and, consequently, the API route that uses it. - -Learn more about the workflow hooks in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md). - -The [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) used in the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts) has a `productsCreated` hook that runs after the product is created. You'll consume this hook to link the created product with the brand specified in the request parameters. - -To consume the `productsCreated` hook, create the file `src/workflows/hooks/created-product.ts` with the following content: - -![Directory structure after creating the hook's file.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733384338/Medusa%20Book/brands-hook-dir-overview_ltwr5h.jpg) - -```ts title="src/workflows/hooks/created-product.ts" highlights={hook1Highlights} -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" -import { StepResponse } from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" -import { LinkDefinition } from "@medusajs/framework/types" -import { BRAND_MODULE } from "../../modules/brand" -import BrandModuleService from "../../modules/brand/service" - -createProductsWorkflow.hooks.productsCreated( - (async ({ products, additional_data }, { container }) => { - if (!additional_data?.brand_id) { - return new StepResponse([], []) - } - - const brandModuleService: BrandModuleService = container.resolve( - BRAND_MODULE - ) - // if the brand doesn't exist, an error is thrown. - await brandModuleService.retrieveBrand(additional_data.brand_id as string) - - // TODO link brand to product - }) -) -``` - -Workflows have a special `hooks` property to access its hooks and consume them. Each hook, such as `productsCreated`, accepts a step function as a parameter. The step function accepts the following parameters: - -1. An object having an `additional_data` property, which is the custom data passed in the request body under `additional_data`. The object will also have properties passed from the workflow to the hook, which in this case is the `products` property that holds an array of the created products. -2. An object of properties related to the step's context. It has a `container` property whose value is the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) to resolve Framework and commerce tools. - -In the step, if a brand ID is passed in `additional_data`, you resolve the Brand Module's service and use its generated `retrieveBrand` method to retrieve the brand by its ID. The `retrieveBrand` method will throw an error if the brand doesn't exist. - -### Link Brand to Product - -Next, you want to create a link between the created products and the brand. To do so, you use Link, which is a class from the Modules SDK that provides methods to manage linked records. - -Learn more about Link in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md). - -To use Link in the `productsCreated` hook, replace the `TODO` with the following: - -```ts title="src/workflows/hooks/created-product.ts" highlights={hook2Highlights} -const link = container.resolve("link") -const logger = container.resolve("logger") - -const links: LinkDefinition[] = [] - -for (const product of products) { - links.push({ - [Modules.PRODUCT]: { - product_id: product.id, - }, - [BRAND_MODULE]: { - brand_id: additional_data.brand_id, - }, - }) -} - -await link.create(links) - -logger.info("Linked brand to products") - -return new StepResponse(links, links) -``` - -You resolve Link from the container. Then you loop over the created products to assemble an array of links to be created. After that, you pass the array of links to Link's `create` method, which will link the product and brand records. - -Each property in the link object is the name of a module, and its value is an object having a `{model_name}_id` property, where `{model_name}` is the snake-case name of the module's data model. Its value is the ID of the record to be linked. The link object's properties must be set in the same order as the link configurations passed to `defineLink`. - -![Diagram showcasing how the order of defining a link affects creating the link](https://res.cloudinary.com/dza7lstvk/image/upload/v1733386156/Medusa%20Book/remote-link-brand-product-exp_fhjmg4.jpg) - -Finally, you return an instance of `StepResponse` returning the created links. - -### Dismiss Links in Compensation - -You can pass as a second parameter of the hook a compensation function that undoes what the step did. It receives as a first parameter the returned `StepResponse`'s second parameter, and the step context object as a second parameter. - -To undo creating the links in the hook, pass the following compensation function as a second parameter to `productsCreated`: - -```ts title="src/workflows/hooks/created-product.ts" -createProductsWorkflow.hooks.productsCreated( - // ... - (async (links, { container }) => { - if (!links?.length) { - return - } - - const link = container.resolve("link") - - await link.dismiss(links) - }) -) -``` - -In the compensation function, if the `links` parameter isn't empty, you resolve Link from the container and use its `dismiss` method. This method removes a link between two records. It accepts the same parameter as the `create` method. - -*** - -## 2. Configure Additional Data Validation - -Now that you've consumed the `productsCreated` hook, you want to configure the `/admin/products` API route that creates a new product to accept a brand ID in its `additional_data` parameter. - -You configure the properties accepted in `additional_data` in the `src/api/middlewares.ts` that exports middleware configurations. So, create the file (or, if already existing, add to the file) `src/api/middlewares.ts` the following content: - -![Directory structure after adding the middelwares file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733386868/Medusa%20Book/brands-middleware-dir-overview_uczos1.jpg) - -```ts title="src/api/middlewares.ts" -import { defineMiddlewares } from "@medusajs/framework/http" -import { z } from "zod" - -// ... - -export default defineMiddlewares({ - routes: [ - // ... - { - matcher: "/admin/products", - method: ["POST"], - additionalDataValidator: { - brand_id: z.string().optional(), - }, - }, - ], -}) -``` - -Objects in `routes` accept an `additionalDataValidator` property that configures the validation rules for custom properties passed in the `additional_data` request parameter. It accepts an object whose keys are custom property names, and their values are validation rules created using [Zod](https://zod.dev/). - -So, `POST` requests sent to `/admin/products` can now pass the ID of a brand in the `brand_id` property of `additional_data`. - -*** - -## Test it Out - -To test it out, first, retrieve the authentication token of your admin user by sending a `POST` request to `/auth/user/emailpass`: - -```bash -curl -X POST 'http://localhost:9000/auth/user/emailpass' \ --H 'Content-Type: application/json' \ ---data-raw '{ - "email": "admin@medusa-test.com", - "password": "supersecret" -}' -``` - -Make sure to replace the email and password in the request body with your user's credentials. - -Then, send a `POST` request to `/admin/products` to create a product, and pass in the `additional_data` parameter a brand's ID: - -```bash -curl -X POST 'http://localhost:9000/admin/products' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {token}' \ ---data '{ - "title": "Product 1", - "options": [ - { - "title": "Default option", - "values": ["Default option value"] - } - ], - "shipping_profile_id": "{shipping_profile_id}", - "additional_data": { - "brand_id": "{brand_id}" - } -}' -``` - -Make sure to replace `{token}` with the token you received from the previous request, `shipping_profile_id` with the ID of a shipping profile in your application, and `{brand_id}` with the ID of a brand in your application. You can retrieve the ID of a shipping profile either from the Medusa Admin, or the [List Shipping Profiles API route](https://docs.medusajs.com/api/admin#shipping-profiles_getshippingprofiles). - -The request creates a product and returns it. - -In the Medusa application's logs, you'll find the message `Linked brand to products`, indicating that the workflow hook handler ran and linked the brand to the products. - -*** - -## Next Steps: Query Linked Brands and Products - -Now that you've extending the create-product flow to link a brand to it, you want to retrieve the brand details of a product. You'll learn how to do so in the next chapter. - - -# Create Brands UI Route in Admin - -In this chapter, you'll add a UI route to the admin dashboard that shows all [brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) in a new page. You'll retrieve the brands from the server and display them in a table with pagination. - -### Prerequisites - -- [Brands Module](https://docs.medusajs.com/learn/customization/custom-features/modules/index.html.md) - -## 1. Get Brands API Route - -In a [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/query-linked-records/index.html.md), you learned how to add an API route that retrieves brands and their products using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). You'll expand that API route to support pagination, so that on the admin dashboard you can show the brands in a paginated table. - -Replace or create the `GET` API route at `src/api/admin/brands/route.ts` with the following: - -```ts title="src/api/admin/brands/route.ts" highlights={apiRouteHighlights} -// other imports... -import { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" @@ -6637,1219 +8113,1431 @@ export const GET = async ( req: MedusaRequest, res: MedusaResponse ) => { - const query = req.scope.resolve("query") - - const { - data: brands, - metadata: { count, take, skip } = {}, - } = await query.graph({ - entity: "brand", - ...req.queryConfig, + res.json({ + message: "[GET] Hello world!", }) +} - res.json({ - brands, - count, - limit: take, - offset: skip, +export const POST = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: "[POST] Hello world!", }) } ``` -In the API route, you use Query's `graph` method to retrieve the brands. In the method's object parameter, you spread the `queryConfig` property of the request object. This property holds configurations for pagination and retrieved fields. +This adds two API Routes: -The query configurations are combined from default configurations, which you'll add next, and the request's query parameters: +- A `GET` route at `http://localhost:9000/hello-world`. +- A `POST` route at `http://localhost:9000/hello-world`. -- `fields`: The fields to retrieve in the brands. -- `limit`: The maximum number of items to retrieve. -- `offset`: The number of items to skip before retrieving the returned items. -When you pass pagination configurations to the `graph` method, the returned object has the pagination's details in a `metadata` property, whose value is an object having the following properties: +# Throwing and Handling Errors -- `count`: The total count of items. -- `take`: The maximum number of items returned in the `data` array. -- `skip`: The number of items skipped before retrieving the returned items. +In this guide, you'll learn how to throw errors in your Medusa application, how it affects an API route's response, and how to change the default error handler of your Medusa application. -You return in the response the retrieved brands and the pagination configurations. +## Throw MedusaError -Learn more about pagination with Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-pagination/index.html.md). +When throwing an error in your API routes, middlewares, workflows, or any customization, throw a `MedusaError` from the Medusa Framework. + +The Medusa application's API route error handler then wraps your thrown error in a uniform object and returns it in the response. + +For example: + +```ts +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { MedusaError } from "@medusajs/framework/utils" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + if (!req.query.q) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "The `q` query parameter is required." + ) + } + + // ... +} +``` + +The `MedusaError` class accepts in its constructor two parameters: + +1. The first is the error's type. `MedusaError` has a static property `Types` that you can use. `Types` is an enum whose possible values are explained in the next section. +2. The second is the message to show in the error response. + +### Error Object in Response + +The error object returned in the response has two properties: + +- `type`: The error's type. +- `message`: The error message, if available. +- `code`: A common snake-case code. Its values can be: + - `invalid_request_error` for the `DUPLICATE_ERROR` type. + - `api_error`: for the `DB_ERROR` type. + - `invalid_state_error` for `CONFLICT` error type. + - `unknown_error` for any unidentified error type. + - For other error types, this property won't be available unless you provide a code as a third parameter to the `MedusaError` constructor. + +### MedusaError Types + +|Type|Description|Status Code| +|---|---|---|---|---| +|\`DB\_ERROR\`|Indicates a database error.|\`500\`| +|\`DUPLICATE\_ERROR\`|Indicates a duplicate of a record already exists. For example, when trying to create a customer whose email is registered by another customer.|\`422\`| +|\`INVALID\_ARGUMENT\`|Indicates an error that occurred due to incorrect arguments or other unexpected state.|\`500\`| +|\`INVALID\_DATA\`|Indicates a validation error.|\`400\`| +|\`UNAUTHORIZED\`|Indicates that a user is not authorized to perform an action or access a route.|\`401\`| +|\`NOT\_FOUND\`|Indicates that the requested resource, such as a route or a record, isn't found.|\`404\`| +|\`NOT\_ALLOWED\`|Indicates that an operation isn't allowed.|\`400\`| +|\`CONFLICT\`|Indicates that a request conflicts with another previous or ongoing request. The error message in this case is ignored for a default message.|\`409\`| +|\`PAYMENT\_AUTHORIZATION\_ERROR\`|Indicates an error has occurred while authorizing a payment.|\`422\`| +|Other error types|Any other error type results in an |\`500\`| *** -## 2. Add Default Query Configurations +## Override Error Handler -Next, you'll set the default query configurations of the above API route and allow passing query parameters to change the configurations. +The `defineMiddlewares` function used to apply middlewares on routes accepts an `errorHandler` in its object parameter. Use it to override the default error handler for API routes. -Medusa provides a `validateAndTransformQuery` middleware that validates the accepted query parameters for a request and sets the default Query configuration. So, in `src/api/middlewares.ts`, add a new middleware configuration object: +This error handler will also be used for errors thrown in Medusa's API routes and resources. + +For example, create `src/api/middlewares.ts` with the following: + +```ts title="src/api/middlewares.ts" collapsibleLines="1-8" expandMoreLabel="Show Imports" +import { + defineMiddlewares, + MedusaNextFunction, + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { MedusaError } from "@medusajs/framework/utils" + +export default defineMiddlewares({ + errorHandler: ( + error: MedusaError | any, + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction + ) => { + res.status(400).json({ + error: "Something happened.", + }) + }, +}) +``` + +The `errorHandler` property's value is a function that accepts four parameters: + +1. The error thrown. Its type can be `MedusaError` or any other thrown error type. +2. A request object of type `MedusaRequest`. +3. A response object of type `MedusaResponse`. +4. A function of type MedusaNextFunction that executes the next middleware in the stack. + +This example overrides Medusa's default error handler with a handler that always returns a `400` status code with the same message. + + +# Middlewares + +In this chapter, you’ll learn about middlewares and how to create them. + +## What is a Middleware? + +A middleware is a function executed when a request is sent to an API Route. It's executed before the route handler function. + +Middlewares are used to guard API routes, parse request content types other than `application/json`, manipulate request data, and more. + +As Medusa's server is based on Express, you can use any [Express middleware](https://expressjs.com/en/resources/middleware.html). + +### Middleware Types + +There are two types of middlewares: + +1. Global Middleware: A middleware that applies to all routes matching a specified pattern. +2. Route Middleware: A middleware that applies to routes matching a specified pattern and HTTP method(s). + +These middlewares generally have the same definition and usage, but they differ in the routes they apply to. You'll learn how to create both types in the following sections. + +*** + +## How to Create a Global Middleware? + +Middlewares of all types are defined in the special file `src/api/middlewares.ts`. Use the `defineMiddlewares` function from the Medusa Framework to define the middlewares, and export its value. + +For example: ```ts title="src/api/middlewares.ts" import { defineMiddlewares, - validateAndTransformQuery, + MedusaNextFunction, + MedusaRequest, + MedusaResponse, } from "@medusajs/framework/http" -import { createFindParams } from "@medusajs/medusa/api/utils/validators" -// other imports... - -export const GetBrandsSchema = createFindParams() export default defineMiddlewares({ routes: [ - // ... { - matcher: "/admin/brands", - method: "GET", + matcher: "/custom*", middlewares: [ - validateAndTransformQuery( - GetBrandsSchema, - { - defaults: [ - "id", - "name", - "products.*", - ], - isList: true, - } - ), + ( + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction + ) => { + console.log("Received a request!") + + next() + }, ], }, - ], }) ``` -You apply the `validateAndTransformQuery` middleware on the `GET /admin/brands` API route. The middleware accepts two parameters: +The `defineMiddlewares` function accepts a middleware configurations object that has the property `routes`. `routes`'s value is an array of middleware route objects, each having the following properties: -- A [Zod](https://zod.dev/) schema that a request's query parameters must satisfy. Medusa provides `createFindParams` that generates a Zod schema with the following properties: - - `fields`: A comma-separated string indicating the fields to retrieve. - - `limit`: The maximum number of items to retrieve. - - `offset`: The number of items to skip before retrieving the returned items. - - `order`: The name of the field to sort the items by. Learn more about sorting in [the API reference](https://docs.medusajs.com/api/admin#sort-order) -- An object of Query configurations having the following properties: - - `defaults`: An array of default fields and relations to retrieve. - - `isList`: Whether the API route returns a list of items. +- `matcher`: a string or regular expression indicating the API route path to apply the middleware on. The regular expression must be compatible with [path-to-regexp](https://github.com/pillarjs/path-to-regexp). +- `middlewares`: An array of global and route middleware functions. -By applying the above middleware, you can pass pagination configurations to `GET /admin/brands`, which will return a paginated list of brands. You'll see how it works when you create the UI route. +In the example above, you define a global middleware that logs the message `Received a request!` whenever a request is sent to an API route path starting with `/custom`. -Learn more about using the `validateAndTransformQuery` middleware to configure Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query#request-query-configurations/index.html.md). +### Test the Global Middleware + +To test the middleware: + +1. Start the application: + +```bash npm2yarn +npm run dev +``` + +2. Send a request to any API route starting with `/custom`. +3. See the following message in the terminal: + +```bash +Received a request! +``` *** -## 3. Initialize JS SDK +## How to Create a Route Middleware? -In your custom UI route, you'll retrieve the brands by sending a request to the Medusa server. Medusa has a [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) that simplifies sending requests to the core API route. +In the previous section, you learned how to create a global middleware. You define the route middleware in the same way in `src/api/middlewares.ts`, but you specify an additional property `method` in the middleware route object. Its value is one or more HTTP methods to apply the middleware to. -If you didn't follow the [previous chapter](https://docs.medusajs.com/learn/customization/customize-admin/widget/index.html.md), create the file `src/admin/lib/sdk.ts` with the following content: +For example: -![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) - -```ts title="src/admin/lib/sdk.ts" -import Medusa from "@medusajs/js-sdk" - -export const sdk = new Medusa({ - baseUrl: import.meta.env.VITE_BACKEND_URL || "/", - debug: import.meta.env.DEV, - auth: { - type: "session", - }, -}) -``` - -You initialize the SDK passing it the following options: - -- `baseUrl`: The URL to the Medusa server. -- `debug`: Whether to enable logging debug messages. This should only be enabled in development. -- `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. - -Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). - -You can now use the SDK to send requests to the Medusa server. - -Learn more about the JS SDK and its options in [this reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). - -*** - -## 4. Add a UI Route to Show Brands - -You'll now add the UI route that shows the paginated list of brands. A UI route is a React component created in a `page.tsx` file under a sub-directory of `src/admin/routes`. The file's path relative to src/admin/routes determines its path in the dashboard. - -Learn more about UI routes in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). - -So, to add the UI route at the `localhost:9000/app/brands` path, create the file `src/admin/routes/brands/page.tsx` with the following content: - -![Directory structure of the Medusa application after adding the UI route.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733472011/Medusa%20Book/brands-admin-dir-overview-3_syytld.jpg) - -```tsx title="src/admin/routes/brands/page.tsx" highlights={uiRouteHighlights} -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { TagSolid } from "@medusajs/icons" +```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports" import { - Container, -} from "@medusajs/ui" -import { useQuery } from "@tanstack/react-query" -import { sdk } from "../../lib/sdk" -import { useMemo, useState } from "react" + MedusaNextFunction, + MedusaRequest, + MedusaResponse, + defineMiddlewares, +} from "@medusajs/framework/http" -const BrandsPage = () => { - // TODO retrieve brands +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom*", + method: ["POST", "PUT"], + middlewares: [ + ( + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction + ) => { + console.log("Received a request!") - return ( - - {/* TODO show brands */} - - ) -} - -export const config = defineRouteConfig({ - label: "Brands", - icon: TagSolid, -}) - -export default BrandsPage -``` - -A route's file must export the React component that will be rendered in the new page. It must be the default export of the file. You can also export configurations that add a link in the sidebar for the UI route. You create these configurations using `defineRouteConfig` from the Admin Extension SDK. - -So far, you only show a container. In admin customizations, use components from the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md) to maintain a consistent user interface and design in the dashboard. - -### Retrieve Brands From API Route - -You'll now update the UI route to retrieve the brands from the API route you added earlier. - -First, add the following type in `src/admin/routes/brands/page.tsx`: - -```tsx title="src/admin/routes/brands/page.tsx" -type Brand = { - id: string - name: string -} -type BrandsResponse = { - brands: Brand[] - count: number - limit: number - offset: number -} -``` - -You define the type for a brand, and the type of expected response from the `GET /admin/brands` API route. - -To display the brands, you'll use Medusa UI's [DataTable](https://docs.medusajs.com/ui/components/data-table/index.html.md) component. So, add the following imports in `src/admin/routes/brands/page.tsx`: - -```tsx title="src/admin/routes/brands/page.tsx" -import { - // ... - Heading, - createDataTableColumnHelper, - DataTable, - DataTablePaginationState, - useDataTable, -} from "@medusajs/ui" -``` - -You import the `DataTable` component and the following utilities: - -- `createDataTableColumnHelper`: A utility to create columns for the data table. -- `DataTablePaginationState`: A type that holds the pagination state of the data table. -- `useDataTable`: A hook to initialize and configure the data table. - -You also import the `Heading` component to show a heading above the data table. - -Next, you'll define the table's columns. Add the following before the `BrandsPage` component: - -```tsx title="src/admin/routes/brands/page.tsx" -const columnHelper = createDataTableColumnHelper() - -const columns = [ - columnHelper.accessor("id", { - header: "ID", - }), - columnHelper.accessor("name", { - header: "Name", - }), -] -``` - -You use the `createDataTableColumnHelper` utility to create columns for the data table. You define two columns for the ID and name of the brands. - -Then, replace the `// TODO retrieve brands` in the component with the following: - -```tsx title="src/admin/routes/brands/page.tsx" highlights={queryHighlights} -const limit = 15 -const [pagination, setPagination] = useState({ - pageSize: limit, - pageIndex: 0, -}) -const offset = useMemo(() => { - return pagination.pageIndex * limit -}, [pagination]) - -const { data, isLoading } = useQuery({ - queryFn: () => sdk.client.fetch(`/admin/brands`, { - query: { - limit, - offset, + next() + }, + ], }, - }), - queryKey: [["brands", limit, offset]], -}) - -// TODO configure data table -``` - -To enable pagination in the `DataTable` component, you need to define a state variable of type `DataTablePaginationState`. It's an object having the following properties: - -- `pageSize`: The maximum number of items per page. You set it to `15`. -- `pageIndex`: A zero-based index of the current page of items. - -You also define a memoized `offset` value that indicates the number of items to skip before retrieving the current page's items. - -Then, you use `useQuery` from [Tanstack (React) Query](https://tanstack.com/query/latest) to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. - -Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. - -In the `queryFn` function that executes the query, you use the JS SDK's `client.fetch` method to send a request to your custom API route. The first parameter is the route's path, and the second is an object of request configuration and data. You pass the query parameters in the `query` property. - -This sends a request to the [Get Brands API route](#1-get-brands-api-route), passing the pagination query parameters. Whenever `currentPage` is updated, the `offset` is also updated, which will send a new request to retrieve the brands for the current page. - -### Display Brands Table - -Finally, you'll display the brands in a data table. Replace the `// TODO configure data table` in the component with the following: - -```tsx title="src/admin/routes/brands/page.tsx" -const table = useDataTable({ - columns, - data: data?.brands || [], - getRowId: (row) => row.id, - rowCount: data?.count || 0, - isLoading, - pagination: { - state: pagination, - onPaginationChange: setPagination, - }, + ], }) ``` -You use the `useDataTable` hook to initialize and configure the data table. It accepts an object with the following properties: +This example applies the middleware only when a `POST` or `PUT` request is sent to an API route path starting with `/custom`, changing the middleware from a global middleware to a route middleware. -- `columns`: The columns of the data table. You created them using the `createDataTableColumnHelper` utility. -- `data`: The brands to display in the table. -- `getRowId`: A function that returns a unique identifier for a row. -- `rowCount`: The total count of items. This is used to determine the number of pages. -- `isLoading`: A boolean indicating whether the data is loading. -- `pagination`: An object to configure pagination. It accepts the following properties: - - `state`: The pagination state of the data table. - - `onPaginationChange`: A function to update the pagination state. +### Test the Route Middleware -Then, replace the `{/* TODO show brands */}` in the return statement with the following: +To test the middleware: -```tsx title="src/admin/routes/brands/page.tsx" - - - Brands - - - - -``` - -This renders the data table that shows the brands with pagination. The `DataTable` component accepts the `instance` prop, which is the object returned by the `useDataTable` hook. - -*** - -## Test it Out - -To test out the UI route, start the Medusa application: +1. Start the application: ```bash npm2yarn npm run dev ``` -Then, open the admin dashboard at `http://localhost:9000/app`. After you log in, you'll find a new "Brands" sidebar item. Click on it to see the brands in your store. You can also go to `http://localhost:9000/app/brands` to see the page. +2. Send a `POST` request to any API route starting with `/custom`. +3. See the following message in the terminal: -![A new sidebar item is added for the new brands UI route. The UI route shows the table of brands with pagination.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733421074/Medusa%20Book/Screenshot_2024-12-05_at_7.46.52_PM_slcdqd.png) - -*** - -## Summary - -By following the previous chapters, you: - -- Injected a widget into the product details page to show the product's brand. -- Created a UI route in the Medusa Admin that shows the list of brands. - -*** - -## Next Steps: Integrate Third-Party Systems - -Your customizations often span across systems, where you need to retrieve data or perform operations in a third-party system. - -In the next chapters, you'll learn about the concepts that facilitate integrating third-party systems in your application. You'll integrate a dummy third-party system and sync the brands between it and the Medusa application. - - -# Admin Development Constraints - -This chapter lists some constraints of admin widgets and UI routes. - -## Arrow Functions - -Widget and UI route components must be created as arrow functions. - -```ts highlights={arrowHighlights} -// Don't -function ProductWidget() { - // ... -} - -// Do -const ProductWidget = () => { - // ... -} +```bash +Received a request! ``` *** -## Widget Zone +## When to Use Middlewares -A widget zone's value must be wrapped in double or single quotes. It can't be a template literal or a variable. - -```ts highlights={zoneHighlights} -// Don't -export const config = defineWidgetConfig({ - zone: `product.details.before`, -}) - -// Don't -const ZONE = "product.details.after" -export const config = defineWidgetConfig({ - zone: ZONE, -}) - -// Do -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) -``` - - -# Guide: Add Product's Brand Widget in Admin - -In this chapter, you'll customize the product details page of the Medusa Admin dashboard to show the product's [brand](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md). You'll create a widget that is injected into a pre-defined zone in the page, and in the widget you'll retrieve the product's brand from the server and display it. - -### Prerequisites - -- [Brands linked to products](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) - -## 1. Initialize JS SDK - -In your custom widget, you'll retrieve the product's brand by sending a request to the Medusa server. Medusa has a [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) that simplifies sending requests to the server's API routes. - -So, you'll start by configuring the JS SDK. Create the file `src/admin/lib/sdk.ts` with the following content: - -![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) - -```ts title="src/admin/lib/sdk.ts" -import Medusa from "@medusajs/js-sdk" - -export const sdk = new Medusa({ - baseUrl: import.meta.env.VITE_BACKEND_URL || "/", - debug: import.meta.env.DEV, - auth: { - type: "session", - }, -}) -``` - -You initialize the SDK passing it the following options: - -- `baseUrl`: The URL to the Medusa server. -- `debug`: Whether to enable logging debug messages. This should only be enabled in development. -- `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. - -Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). - -You can now use the SDK to send requests to the Medusa server. - -Learn more about the JS SDK and its options in [this reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). +- You want to protect API routes by a custom condition. +- You're modifying the request body. *** -## 2. Add Widget to Product Details Page +## Middleware Function Parameters -You'll now add a widget to the product-details page. A widget is a React component that's injected into pre-defined zones in the Medusa Admin dashboard. It's created in a `.tsx` file under the `src/admin/widgets` directory. +The middleware function accepts three parameters: -Learn more about widgets in [this documentation](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md). +1. A request object of type `MedusaRequest`. +2. A response object of type `MedusaResponse`. +3. A function of type `MedusaNextFunction` that executes the next middleware in the stack. -To create a widget that shows a product's brand in its details page, create the file `src/admin/widgets/product-brand.tsx` with the following content: - -![Directory structure of the Medusa application after adding the widget](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414684/Medusa%20Book/brands-admin-dir-overview-2_eq5xhi.jpg) - -```tsx title="src/admin/widgets/product-brand.tsx" highlights={highlights} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { DetailWidgetProps, AdminProduct } from "@medusajs/framework/types" -import { clx, Container, Heading, Text } from "@medusajs/ui" -import { useQuery } from "@tanstack/react-query" -import { sdk } from "../lib/sdk" - -type AdminProductBrand = AdminProduct & { - brand?: { - id: string - name: string - } -} - -const ProductBrandWidget = ({ - data: product, -}: DetailWidgetProps) => { - const { data: queryResult } = useQuery({ - queryFn: () => sdk.admin.product.retrieve(product.id, { - fields: "+brand.*", - }), - queryKey: [["product", product.id]], - }) - const brandName = (queryResult?.product as AdminProductBrand)?.brand?.name - - return ( - -
-
- Brand -
-
-
- - Name - - - - {brandName || "-"} - -
-
- ) -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductBrandWidget -``` - -A widget's file must export: - -- A React component to be rendered in the specified injection zone. The component must be the file's default export. -- A configuration object created with `defineWidgetConfig` from the Admin Extension SDK. The function receives an object as a parameter that has a `zone` property, whose value is the zone to inject the widget to. - -Since the widget is injected at the top of the product details page, the widget receives the product's details as a parameter. - -In the widget, you use [Tanstack (React) Query](https://tanstack.com/query/latest) to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. In the `queryFn` function that executes the query, you use the JS SDK to send a request to the [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid), passing `+brand.*` in the `fields` query parameter to retrieve the product's brand. - -Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. - -You then render a section that shows the brand's name. In admin customizations, use components from the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md) to maintain a consistent user interface and design in the dashboard. +You must call the `next` function in the middleware. Otherwise, other middlewares and the API route handler won’t execute. *** -## Test it Out +## Middleware for Routes with Path Parameters -To test out your widget, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, open the admin dashboard at `http://localhost:9000/app`. After you log in, open the page of a product that has a brand. You'll see a new section at the top showing the brand's name. - -![The widget is added as the first section of the product details page.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414415/Medusa%20Book/Screenshot_2024-12-05_at_5.59.25_PM_y85m14.png) - -*** - -## Admin Components Guides - -When building your widget, you may need more complicated components. For example, you may add a form to the above widget to set the product's brand. - -The [Admin Components guides](https://docs.medusajs.com/resources/admin-components/index.html.md) show you how to build and use common components in the Medusa Admin, such as forms, tables, JSON data viewer, and more. The components in the guides also follow the Medusa Admin's design convention. - -*** - -## Next Chapter: Add UI Route for Brands - -In the next chapter, you'll add a UI route that displays the list of brands in your application and allows admin users. - - -# Admin Routing Customizations - -The Medusa Admin dashboard uses [React Router](https://reactrouter.com) under the hood to manage routing. So, you can have more flexibility in routing-related customizations using some of React Router's utilities, hooks, and components. - -In this chapter, you'll learn about routing-related customizations that you can use in your admin customizations using React Router. - -`react-router-dom` is available in your project by default through the Medusa packages. You don't need to install it separately. - -## Link to a Page - -To link to a page in your admin customizations, you can use the `Link` component from `react-router-dom`. For example: - -```tsx title="src/admin/widgets/product-widget.tsx" highlights={highlights} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Container } from "@medusajs/ui" -import { Link } from "react-router-dom" - -// The widget -const ProductWidget = () => { - return ( - - View Orders - - ) -} - -// The widget's configurations -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget -``` - -This adds a widget to a product's details page with a link to the Orders page. The link's path must be without the `/app` prefix. - -*** - -## Admin Route Loader - -Route loaders are available starting from Medusa v2.5.1. - -In your UI route or any other custom admin route, you may need to retrieve data to use it in your route component. For example, you may want to fetch a list of products to display on a custom page. - -To do that, you can export a `loader` function in the route file, which is a [React Router loader](https://reactrouter.com/6.29.0/route/loader#loader). In this function, you can fetch and return data asynchronously. Then, in your route component, you can use the [useLoaderData](https://reactrouter.com/6.29.0/hooks/use-loader-data#useloaderdata) hook from React Router to access the data. - -For example, consider the following UI route created at `src/admin/routes/custom/page.tsx`: - -```tsx title="src/admin/routes/custom/page.tsx" highlights={loaderHighlights} -import { Container, Heading } from "@medusajs/ui" -import { - useLoaderData, -} from "react-router-dom" - -export async function loader() { - // TODO fetch products - - return { - products: [], - } -} - -const CustomPage = () => { - const { products } = useLoaderData() as Awaited> - - return ( -
- -
- Products count: {products.length} -
-
-
- ) -} - -export default CustomPage -``` - -In this example, you first export a `loader` function that can be used to fetch data, such as products. The function returns an object with a `products` property. - -Then, in the `CustomPage` route component, you use the `useLoaderData` hook from React Router to access the data returned by the `loader` function. You can then use the data in your component. - -### Route Parameters - -You can also access route params in the loader function. For example, consider the following UI route created at `src/admin/routes/custom/[id]/page.tsx`: - -```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={loaderParamHighlights} -import { Container, Heading } from "@medusajs/ui" -import { - useLoaderData, - LoaderFunctionArgs, -} from "react-router-dom" - -export async function loader({ params }: LoaderFunctionArgs) { - const { id } = params - // TODO fetch product by id - - return { - id, - } -} - -const CustomPage = () => { - const { id } = useLoaderData() as Awaited> - - return ( -
- -
- Product ID: {id} -
-
-
- ) -} - -export default CustomPage -``` - -Because the UI route has a route parameter `[id]`, you can access the `id` parameter in the `loader` function. The loader function accepts as a parameter an object of type `LoaderFunctionArgs` from React Router. This object has a `params` property that contains the route parameters. - -In the loader, you can fetch data asynchronously using the route parameter and return it. Then, in the route component, you can access the data using the `useLoaderData` hook. - -### When to Use Route Loaders - -A route loader is executed before the route is loaded. So, it will block navigation until the loader function is resolved. - -Only use route loaders when the route component needs data essential before rendering. Otherwise, use the JS SDK with Tanstack (React) Query as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/tips#send-requests-to-api-routes/index.html.md). This way, you can fetch data asynchronously and update the UI when the data is available. You can also use a loader to prepare some initial data that's used in the route component before the data is retrieved. - -*** - -## Other React Router Utilities - -### Route Handles - -Route handles are available starting from Medusa v2.5.1. - -In your UI route or any route file, you can export a `handle` object to define [route handles](https://reactrouter.com/start/framework/route-module#handle). The object is passed to the loader and route contexts. +To indicate a path parameter in a middleware's `matcher` pattern, use the format `:{param-name}`. For example: -```tsx title="src/admin/routes/custom/page.tsx" -export const handle = { - sandbox: true, -} -``` +```ts title="src/api/middlewares.ts" collapsibleLines="1-7" expandMoreLabel="Show Imports" highlights={pathParamHighlights} +import { + MedusaNextFunction, + MedusaRequest, + MedusaResponse, + defineMiddlewares, +} from "@medusajs/framework/http" -### React Router Components and Hooks - -Refer to [react-router-dom’s documentation](https://reactrouter.com/en/6.29.0) for components and hooks that you can use in your admin customizations. - - -# Admin Development Tips - -In this chapter, you'll find some tips for your admin development. - -## Send Requests to API Routes - -To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default. - -Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. - -First, create the file `src/admin/lib/config.ts` to setup the SDK for use in your customizations: - -```ts -import Medusa from "@medusajs/js-sdk" - -export const sdk = new Medusa({ - baseUrl: import.meta.env.VITE_BACKEND_URL || "/", - debug: import.meta.env.DEV, - auth: { - type: "session", - }, +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom/:id", + middlewares: [ + // ... + ], + }, + ], }) ``` -Notice that you use `import.meta.env` to access environment variables in your customizations, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). - -Learn more about the JS SDK's configurations [this documentation](https://docs.medusajs.com/resources/js-sdk#js-sdk-configurations/index.html.md). - -Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests. - -For example: - -### Query - -```tsx title="src/admin/widgets/product-widget.ts" highlights={queryHighlights} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Button, Container } from "@medusajs/ui" -import { useQuery } from "@tanstack/react-query" -import { sdk } from "../lib/config" -import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types" - -const ProductWidget = () => { - const { data, isLoading } = useQuery({ - queryFn: () => sdk.admin.product.list(), - queryKey: ["products"], - }) - - return ( - - {isLoading && Loading...} - {data?.products && ( -
    - {data.products.map((product) => ( -
  • {product.title}
  • - ))} -
- )} -
- ) -} - -export const config = defineWidgetConfig({ - zone: "product.list.before", -}) - -export default ProductWidget -``` - -### Mutation - -```tsx title="src/admin/widgets/product-widget.ts" highlights={mutationHighlights} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Button, Container } from "@medusajs/ui" -import { useMutation } from "@tanstack/react-query" -import { sdk } from "../lib/config" -import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types" - -const ProductWidget = ({ - data: productData, -}: DetailWidgetProps) => { - const { mutateAsync } = useMutation({ - mutationFn: (payload: HttpTypes.AdminUpdateProduct) => - sdk.admin.product.update(productData.id, payload), - onSuccess: () => alert("updated product"), - }) - - const handleUpdate = () => { - mutateAsync({ - title: "New Product Title", - }) - } - - return ( - - - - ) -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget -``` - -You can also send requests to custom routes as explained in the [JS SDK reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). - -### Use Route Loaders for Initial Data - -You may need to retrieve data before your component is rendered, or you may need to pass some initial data to your component to be used while data is being fetched. In those cases, you can use a [route loader](https://docs.medusajs.com/learn/fundamentals/admin/routing/index.html.md). +This applies a middleware to the routes defined in the file `src/api/custom/[id]/route.ts`. *** -## Global Variables in Admin Customizations +## Request URLs with Trailing Backslashes -In your admin customizations, you can use the following global variables: +A middleware whose `matcher` pattern doesn't end with a backslash won't be applied for requests to URLs with a trailing backslash. -- `__BASE__`: The base path of the Medusa Admin, as set in the [admin.path](https://docs.medusajs.com/learn/configurations/medusa-config#path/index.html.md) configuration in `medusa-config.ts`. -- `__BACKEND_URL__`: The URL to the Medusa backend, as set in the [admin.backendUrl](https://docs.medusajs.com/learn/configurations/medusa-config#backendurl/index.html.md) configuration in `medusa-config.ts`. -- `__STOREFRONT_URL__`: The URL to the storefront, as set in the [admin.storefrontUrl](https://docs.medusajs.com/learn/configurations/medusa-config#storefrontUrl/index.html.md) configuration in `medusa-config.ts`. +For example, consider you have the following middleware: -*** +```ts title="src/api/middlewares.ts" collapsibleLines="1-7" expandMoreLabel="Show Imports" +import { + MedusaNextFunction, + MedusaRequest, + MedusaResponse, + defineMiddlewares, +} from "@medusajs/framework/http" -## Admin Translations +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom", + middlewares: [ + ( + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction + ) => { + console.log("Received a request!") -The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions. - -Learn how to add a new language translation for the Medusa Admin in [this guide](https://docs.medusajs.com/learn/resources/contribution-guidelines/admin-translations/index.html.md). - - -# Admin UI Routes - -In this chapter, you’ll learn how to create a UI route in the admin dashboard. - -## What is a UI Route? - -The Medusa Admin dashboard is customizable, allowing you to add new pages, called UI routes. You create a UI route as a React component showing custom content that allow admin users to perform custom actions. - -For example, you can add a new page to show and manage product reviews, which aren't available natively in Medusa. - -*** - -## How to Create a UI Route? - -### Prerequisites - -- [Medusa application installed](https://docs.medusajs.com/learn/installation/index.html.md) - -You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/routes` directory. The file's path relative to `src/admin/routes` determines its path in the dashboard. The file’s default export must be the UI route’s React component. - -For example, create the file `src/admin/routes/custom/page.tsx` with the following content: - -![Example of UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg) - -```tsx title="src/admin/routes/custom/page.tsx" -import { Container, Heading } from "@medusajs/ui" - -const CustomPage = () => { - return ( - -
- This is my custom route -
-
- ) -} - -export default CustomPage -``` - -You add a new route at `http://localhost:9000/app/custom`. The `CustomPage` component holds the page's content, which currently only shows a heading. - -In the route, you use [Medusa UI](https://docs.medusajs.com/ui/index.html.md), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it. - -The UI route component must be created as an arrow function. - -### Test the UI Route - -To test the UI route, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, after logging into the admin dashboard, open the page `http://localhost:9000/app/custom` to see your custom page. - -*** - -## Show UI Route in the Sidebar - -To add a sidebar item for your custom UI route, export a configuration object in the UI route's file: - -```tsx title="src/admin/routes/custom/page.tsx" highlights={highlights} -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { ChatBubbleLeftRight } from "@medusajs/icons" -import { Container, Heading } from "@medusajs/ui" - -const CustomPage = () => { - return ( - -
- This is my custom route -
-
- ) -} - -export const config = defineRouteConfig({ - label: "Custom Route", - icon: ChatBubbleLeftRight, + next() + }, + ], + }, + ], }) - -export default CustomPage ``` -The configuration object is created using `defineRouteConfig` from the Medusa Framework. It accepts the following properties: +If you send a request to `http://localhost:9000/custom`, the middleware will run. -- `label`: the sidebar item’s label. -- `icon`: an optional React component used as an icon in the sidebar. +However, if you send a request to `http://localhost:9000/custom/`, the middleware won't run. -The above example adds a new sidebar item with the label `Custom Route` and an icon from the [Medusa UI Icons package](https://docs.medusajs.com/ui/icons/overview/index.html.md). - -### Nested UI Routes - -Consider that along the UI route above at `src/admin/routes/custom/page.tsx` you create a nested UI route at `src/admin/routes/custom/nested/page.tsx` that also exports route configurations: - -![Example of nested UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg) - -```tsx title="src/admin/routes/custom/nested/page.tsx" -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { Container, Heading } from "@medusajs/ui" - -const NestedCustomPage = () => { - return ( - -
- This is my nested custom route -
-
- ) -} - -export const config = defineRouteConfig({ - label: "Nested Route", -}) - -export default NestedCustomPage -``` - -This UI route is shown in the sidebar as an item nested in the parent "Custom Route" item. Nested items are only shown when the parent sidebar items (in this case, "Custom Route") are clicked. - -#### Caveats - -Some caveats for nested UI routes in the sidebar: - -- Nested dynamic UI routes, such as one created at `src/admin/routes/custom/[id]/page.tsx` aren't added to the sidebar as it's not possible to link to a dynamic route. If the dynamic route exports route configurations, a warning is logged in the browser's console. -- Nested routes in setting pages aren't shown in the sidebar to follow the admin's design conventions. -- The `icon` configuration is ignored for the sidebar item of nested UI route to follow the admin's design conventions. - -### Route Under Existing Admin Route - -You can add a custom UI route under an existing route. For example, you can add a route under the orders route: - -```tsx title="src/admin/routes/orders/nested/page.tsx" -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { Container, Heading } from "@medusajs/ui" - -const NestedOrdersPage = () => { - return ( - -
- Nested Orders Page -
-
- ) -} - -export const config = defineRouteConfig({ - label: "Nested Orders", - nested: "/orders", -}) - -export default NestedOrdersPage -``` - -The `nested` property passed to `defineRouteConfig` specifies which route this custom route is nested under. This route will now show in the sidebar under the existing "Orders" sidebar item. +In general, avoid adding trailing backslashes when sending requests to API routes. *** -## Create Settings Page +## Middlewares and Route Ordering -To create a page under the settings section of the admin dashboard, create a UI route under the path `src/admin/routes/settings`. +The ordering explained in this section was added in [Medusa v2.6](https://github.com/medusajs/medusa/releases/tag/v2.6) -For example, create a UI route at `src/admin/routes/settings/custom/page.tsx`: +The Medusa application registers middlewares and API route handlers in the following order: -![Example of settings UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867435/Medusa%20Book/setting-ui-route-dir-overview_kytbh8.jpg) +1. Global middlewares in the following order: + 1. Global middleware defined in the Medusa's core. + 2. Global middleware defined in the plugins (in the order the plugins are registered in). + 3. Global middleware you define in the application. +2. Route middlewares in the following order: + 1. Route middleware defined in the Medusa's core. + 2. Route middleware defined in the plugins (in the order the plugins are registered in). + 3. Route middleware you define in the application. +3. API routes in the following order: + 1. API routes defined in the Medusa's core. + 2. API routes defined in the plugins (in the order the plugins are registered in). + 3. API routes you define in the application. -```tsx title="src/admin/routes/settings/custom/page.tsx" -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { Container, Heading } from "@medusajs/ui" +### Middlewares Sorting -const CustomSettingPage = () => { - return ( - -
- Custom Setting Page -
-
- ) -} +On top of the previous ordering, Medusa sorts global and route middlewares based on their matcher pattern in the following order: -export const config = defineRouteConfig({ - label: "Custom", +1. Wildcard matchers. For example, `/custom*`. +2. Regex matchers. For example, `/custom/(products|collections)`. +3. Static matchers without parameters. For example, `/custom`. +4. Static matchers with parameters. For example, `/custom/:id`. + +For example, if you have the following middlewares: + +```ts title="src/api/middlewares.ts" +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom/:id", + middlewares: [/* ... */], + }, + { + matcher: "/custom", + middlewares: [/* ... */], + }, + { + matcher: "/custom*", + method: ["GET"], + middlewares: [/* ... */], + }, + { + matcher: "/custom/:id", + method: ["GET"], + middlewares: [/* ... */], + }, + ], }) - -export default CustomSettingPage ``` -This adds a page under the path `/app/settings/custom`. An item is also added to the settings sidebar with the label `Custom`. +The global middlewares are sorted into the following order before they're registered: + +1. Global middleware `/custom`. +2. Global middleware `/custom/:id`. + +And the route middlewares are sorted into the following order before they're registered: + +1. Route middleware `/custom*`. +2. Route middleware `/custom/:id`. + +Then, the middlwares are registered in the order mentioned earlier, with global middlewares first, then the route middlewares. + +### Middlewares and Route Execution Order + +When a request is sent to an API route, the global middlewares are executed first, then the route middlewares, and finally the route handler. + +For example, consider you have the following middlewares: + +```ts title="src/api/middlewares.ts" +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom", + middlewares: [ + (req, res, next) => { + console.log("Global middleware") + next() + }, + ], + }, + { + matcher: "/custom", + method: ["GET"], + middlewares: [ + (req, res, next) => { + console.log("Route middleware") + next() + }, + ], + }, + ], +}) +``` + +When you send a request to `/custom` route, the following messages are logged in the terminal: + +```bash +Global middleware +Route middleware +Hello from custom! # message logged from API route handler +``` + +The global middleware runs first, then the route middleware, and finally the route handler, assuming that it logs the message `Hello from custom!`. *** +## Overriding Middlewares + +A middleware can not override an existing middleware. Instead, middlewares are added to the end of the middleware stack. + +For example, if you define a custom validation middleware, such as `validateAndTransformBody`, on an existing route, then both the original and the custom validation middleware will run. + + +# API Route Parameters + +In this chapter, you’ll learn about path, query, and request body parameters. + ## Path Parameters -A UI route can accept path parameters if the name of any of the directories in its path is of the format `[param]`. +To create an API route that accepts a path parameter, create a directory within the route file's path whose name is of the format `[param]`. -For example, create the file `src/admin/routes/custom/[id]/page.tsx` with the following content: +For example, to create an API Route at the path `/hello-world/:id`, where `:id` is a path parameter, create the file `src/api/hello-world/[id]/route.ts` with the following content: -![Example of UI route file with path parameters in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867748/Medusa%20Book/path-param-ui-route-dir-overview_kcfbev.jpg) +```ts title="src/api/hello-world/[id]/route.ts" highlights={singlePathHighlights} +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" -```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={[["5", "", "Retrieve the path parameter."], ["10", "{id}", "Show the path parameter."]]} -import { useParams } from "react-router-dom" -import { Container, Heading } from "@medusajs/ui" +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: `[GET] Hello ${req.params.id}!`, + }) +} +``` -const CustomPage = () => { - const { id } = useParams() +The `MedusaRequest` object has a `params` property. `params` holds the path parameters in key-value pairs. - return ( - -
- Passed ID: {id} -
-
- ) +### Multiple Path Parameters + +To create an API route that accepts multiple path parameters, create within the file's path multiple directories whose names are of the format `[param]`. + +For example, to create an API route at `/hello-world/:id/name/:name`, create the file `src/api/hello-world/[id]/name/[name]/route.ts` with the following content: + +```ts title="src/api/hello-world/[id]/name/[name]/route.ts" highlights={multiplePathHighlights} +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: `[GET] Hello ${ + req.params.id + } - ${req.params.name}!`, + }) +} +``` + +You access the `id` and `name` path parameters using the `req.params` property. + +*** + +## Query Parameters + +You can access all query parameters in the `query` property of the `MedusaRequest` object. `query` is an object of key-value pairs, where the key is a query parameter's name, and the value is its value. + +For example: + +```ts title="src/api/hello-world/route.ts" highlights={queryHighlights} +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: `Hello ${req.query.name}`, + }) +} +``` + +The value of `req.query.name` is the value passed in `?name=John`, for example. + +### Validate Query Parameters + +You can apply validation rules on received query parameters to ensure they match specified rules and types. + +Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-query-paramters/index.html.md). + +*** + +## Request Body Parameters + +The Medusa application parses the body of any request having a JSON, URL-encoded, or text request content types. The request body parameters are set in the `MedusaRequest`'s `body` property. + +Learn more about configuring body parsing in [this guide](https://docs.medusajs.com/learn/fundamentals/api-routes/parse-body/index.html.md). + +For example: + +```ts title="src/api/hello-world/route.ts" highlights={bodyHighlights} +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +type HelloWorldReq = { + name: string } -export default CustomPage +export const POST = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: `[POST] Hello ${req.body.name}!`, + }) +} ``` -You access the passed parameter using `react-router-dom`'s [useParams hook](https://reactrouter.com/en/main/hooks/use-params). +In this example, you use the `name` request body parameter to create the message in the returned response. -If you run the Medusa application and go to `localhost:9000/app/custom/123`, you'll see `123` printed in the page. +The `MedusaRequest` type accepts a type argument that indicates the type of the request body. This is useful for auto-completion and to avoid typing errors. -*** - -## Admin Components List - -To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components. - -*** - -## More Routes Customizations - -For more customizations related to routes, refer to the [Routing Customizations chapter](https://docs.medusajs.com/learn/fundamentals/admin/routing/index.html.md). - - -# Add Data Model Check Constraints - -In this chapter, you'll learn how to add check constraints to your data model. - -## What is a Check Constraint? - -A check constraint is a condition that must be satisfied by records inserted into a database table, otherwise an error is thrown. - -For example, if you have a data model with a `price` property, you want to only allow positive number values. So, you add a check constraint that fails when inserting a record with a negative price value. - -*** - -## How to Set a Check Constraint? - -To set check constraints on a data model, use the `checks` method. This method accepts an array of check constraints to apply on the data model. - -For example, to set a check constraint on a `price` property that ensures its value can only be a positive number: - -```ts highlights={checks1Highlights} -import { model } from "@medusajs/framework/utils" - -const CustomProduct = model.define("custom_product", { - // ... - price: model.bigNumber(), -}) -.checks([ - (columns) => `${columns.price} >= 0`, -]) -``` - -The item passed in the array parameter of `checks` can be a callback function that accepts as a parameter an object whose keys are the names of the properties in the data model schema, and values the respective column name in the database. - -The function returns a string indicating the [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). In the expression, use the `columns` parameter to access a property's column name. - -You can also pass an object to the `checks` method: - -```ts highlights={checks2Highlights} -import { model } from "@medusajs/framework/utils" - -const CustomProduct = model.define("custom_product", { - // ... - price: model.bigNumber(), -}) -.checks([ - { - name: "custom_product_price_check", - expression: (columns) => `${columns.price} >= 0`, - }, -]) -``` - -The object accepts the following properties: - -- `name`: The check constraint's name. -- `expression`: A function similar to the one that can be passed to the array. It accepts an object of columns and returns an [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). - -*** - -## Apply in Migrations - -After adding the check constraint, make sure to generate and run migrations if you already have the table in the database. Otherwise, the check constraint won't be reflected. - -To generate a migration for the data model's module then reflect it on the database, run the following command: +To test it out, send the following request to your Medusa application: ```bash -npx medusa db:generate custom_module -npx medusa db:migrate +curl -X POST 'http://localhost:9000/hello-world' \ +-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "John" +}' ``` -The first command generates the migration under the `migrations` directory of your module's directory, and the second reflects it on the database. +This returns the following JSON object: + +```json +{ + "message": "[POST] Hello John!" +} +``` + +### Validate Body Parameters + +You can apply validation rules on received body parameters to ensure they match specified rules and types. + +Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-body/index.html.md). -# Environment Variables in Admin Customizations +# Configure Request Body Parser -In this chapter, you'll learn how to use environment variables in your admin customizations. +In this chapter, you'll learn how to configure the request body parser for your API routes. -To learn how environment variables are generally loaded in Medusa based on your application's environment, check out [this chapter](https://docs.medusajs.com/learn/fundamentals/environment-variables/index.html.md). +## Default Body Parser Configuration -## How to Set Environment Variables +The Medusa application configures the body parser by default to parse JSON, URL-encoded, and text request content types. You can parse other data types by adding the relevant [Express middleware](https://expressjs.com/en/guide/using-middleware.html) or preserve the raw body data by configuring the body parser, which is useful for webhook requests. -The Medusa Admin is built on top of [Vite](https://vite.dev/). To set an environment variable that you want to use in a widget or UI route, prefix the environment variable with `VITE_`. +This chapter shares some examples of configuring the body parser for different data types or use cases. -For example: +*** -```plain -VITE_MY_API_KEY=sk_123 +## Preserve Raw Body Data for Webhooks + +If your API route receives webhook requests, you might want to preserve the raw body data. To do this, you can configure the body parser to parse the raw body data and store it in the `req.rawBody` property. + +To do that, create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" highlights={preserveHighlights} +import { defineMiddlewares } from "@medusajs/framework/http" + +export default defineMiddlewares({ + routes: [ + { + method: ["POST"], + bodyParser: { preserveRawBody: true }, + matcher: "/custom", + }, + ], +}) +``` + +The middleware route object passed to `routes` accepts a `bodyParser` property whose value is an object of configuration for the default body parser. By enabling the `preserveRawBody` property, the raw body data is preserved and stored in the `req.rawBody` property. + +Learn more about [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). + +You can then access the raw body data in your API route handler: + +```ts title="src/api/custom/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +) { + console.log(req.rawBody) + + // TODO use raw body +} ``` *** -## How to Use Environment Variables +## Configure Request Body Size Limit -To access or use an environment variable starting with `VITE_`, use the `import.meta.env` object. +By default, the body parser limits the request body size to `100kb`. If a request body exceeds that size, the Medusa application throws an error. + +You can configure the body parser to accept larger request bodies by setting the `sizeLimit` property of the `bodyParser` object in a middleware route object. For example: + +```ts title="src/api/middlewares.ts" highlights={sizeLimitHighlights} +import { defineMiddlewares } from "@medusajs/framework/http" + +export default defineMiddlewares({ + routes: [ + { + method: ["POST"], + bodyParser: { sizeLimit: "2mb" }, + matcher: "/custom", + }, + ], +}) +``` + +The `sizeLimit` property accepts one of the following types of values: + +- A string representing the size limit in bytes (For example, `100kb`, `2mb`, `5gb`). It is passed to the [bytes](https://www.npmjs.com/package/bytes) library to parse the size. +- A number representing the size limit in bytes. For example, `1024` for 1kb. + +*** + +## Configure File Uploads + +To accept file uploads in your API routes, you can configure the [Express Multer middleware](https://expressjs.com/en/resources/middleware/multer.html) on your route. + +The `multer` package is available through the `@medusajs/medusa` package, so you don't need to install it. However, for better typing support, install the `@types/multer` package as a development dependency: + +```bash npm2yarn +npm install --save-dev @types/multer +``` + +Then, to configure file upload for your route, create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" highlights={uploadHighlights} +import { defineMiddlewares } from "@medusajs/framework/http" +import multer from "multer" + +const upload = multer({ storage: multer.memoryStorage() }) + +export default defineMiddlewares({ + routes: [ + { + method: ["POST"], + matcher: "/custom", + middlewares: [ + // @ts-ignore + upload.array("files"), + ], + }, + ], +}) +``` + +In the example above, you configure the `multer` middleware to store the uploaded files in memory. Then, you apply the `upload.array("files")` middleware to the route to accept file uploads. By using the `array` method, you accept multiple file uploads with the same `files` field name. + +You can then access the uploaded files in your API route handler: + +```ts title="src/api/custom/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +) { + const files = req.files as Express.Multer.File[] + + // TODO handle files +} +``` + +The uploaded files are stored in the `req.files` property as an array of Multer file objects that have properties like `filename` and `mimetype`. + +### Uploading Files using File Module Provider + +The recommended way to upload the files to storage using the configured [File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file/index.html.md) is to use the [uploadFilesWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md): + +```ts title="src/api/custom/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { MedusaError } from "@medusajs/framework/utils" +import { uploadFilesWorkflow } from "@medusajs/medusa/core-flows" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +) { + const files = req.files as Express.Multer.File[] + + if (!files?.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "No files were uploaded" + ) + } + + const { result } = await uploadFilesWorkflow(req.scope).run({ + input: { + files: files?.map((f) => ({ + filename: f.originalname, + mimeType: f.mimetype, + content: f.buffer.toString("binary"), + access: "public", + })), + }, + }) + + res.status(200).json({ files: result }) +} +``` + +Check out the [uploadFilesWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md) for details on the expected input and output of the workflow. + + +# Retrieve Custom Links from Medusa's API Route + +In this chapter, you'll learn how to retrieve custom data models linked to existing Medusa data models from Medusa's API routes. + +## Why Retrieve Custom Linked Data Models? + +Often, you'll link custom data models to existing Medusa data models to implement custom features or expand on existing ones. + +For example, to add brands for products, you can create a `Brand` data model in a Brand Module, then [define a link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) to the [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md)'s `Product` data model. + +When you implement this customization, you might need to retrieve the brand of a product using the existing [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid). You can do this by passing the linked data model's name in the `fields` query parameter of the API route. + +*** + +## How to Retrieve Custom Linked Data Models Using `fields`? + +Most of Medusa's API routes accept a `fields` query parameter that allows you to specify the fields and relations to retrieve in the resource, such as a product. + +For example, to retrieve the brand of a product, you can pass the `brand` field in the `fields` query parameter of the [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid): + +```bash +curl 'http://localhost:9000/admin/products/{id}?fields=*brand' \ +-H 'Authorization: Bearer {access_token}' +``` + +The `fields` query parameter accepts a comma-separated list of fields and relations to retrieve. To learn more about using the `fields` query parameter, refer to the [API Reference](https://docs.medusajs.com/api/store#select-fields-and-relations). + +By prefixing `brand` with an asterisk (`*`), you retrieve all the default fields of the product, including the `brand` field. If you don't include the `*` prefix, the response will only include the product's brand. + +*** + +## API Routes that Restrict Retrievable Fields + +Some of Medusa's API routes restrict the fields and relations you can retrieve, which means you can't pass your custom linked data models in the `fields` query parameter. Medusa makes this restriction to ensure the API routes are performant and secure. + +The API routes that restrict the fields and relations you can retrieve are: + +- [Customer Store API Routes](https://docs.medusajs.com/api/store#customers) +- [Customer Admin API Routes](https://docs.medusajs.com/api/admin#customers) +- [Product Category Admin API Routes](https://docs.medusajs.com/api/admin#product-categories) + +### How to Override Allowed Fields and Relations + +For these routes, you need to override the allowed fields and relations to be retrieved. You can do this by adding a [middleware](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md) to those routes. + +For example, to allow retrieving the `b2b_company` of a customer using the [Get Customer Admin API Route](https://docs.medusajs.com/api/admin#customers_getcustomersid), create the file `src/api/middlewares.ts` with the following content: + +Learn how to create a middleware in the [Middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md) chapter. + +```ts title="src/api/middlewares.ts" highlights={highlights} +import { defineMiddlewares } from "@medusajs/medusa" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/store/customers/me", + method: "GET", + middlewares: [ + (req, res, next) => { + req.allowed?.push("b2b_company") + next() + }, + ], + }, + ], +}) +``` + +In this example, you apply a middleware to the [Get Customer Admin API Route](https://docs.medusajs.com/api/admin#customers_getcustomersid). + +The request object passed to middlewares has an `allowed` property that contains the fields and relations that can be retrieved. So, you modify the `allowed` array to include the `b2b_company` field. + +You can now retrieve the `b2b_company` field using the `fields` query parameter of the [Get Customer Admin API Route](https://docs.medusajs.com/api/admin#customers_getcustomersid): + +```bash +curl 'http://localhost:9000/admin/customers/{id}?fields=*b2b_company' \ +-H 'Authorization: Bearer {access_token}' +``` + +In this example, you retrieve the `b2b_company` relation of the customer using the `fields` query parameter. + + +# API Route Response + +In this chapter, you'll learn how to send a response in your API route. + +## Send a JSON Response + +To send a JSON response, use the `json` method of the `MedusaResponse` object passed as the second parameter of your API route handler. For example: -```tsx highlights={[["8"]]} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Container, Heading } from "@medusajs/ui" +```ts title="src/api/custom/route.ts" highlights={jsonHighlights} +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -const ProductWidget = () => { - return ( - -
- API Key: {import.meta.env.VITE_MY_API_KEY} -
-
- ) +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: "Hello, World!", + }) +} +``` + +This API route returns the following JSON object: + +```json +{ + "message": "Hello, World!" +} +``` + +*** + +## Set Response Status Code + +By default, setting the JSON data using the `json` method returns a response with a `200` status code. + +To change the status code, use the `status` method of the `MedusaResponse` object. + +For example: + +```ts title="src/api/custom/route.ts" highlights={statusHighlight} +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.status(201).json({ + message: "Hello, World!", + }) +} +``` + +The response of this API route has the status code `201`. + +*** + +## Change Response Content Type + +To return response data other than a JSON object, use the `writeHead` method of the `MedusaResponse` object. It allows you to set the response headers, including the content type. + +For example, to create an API route that returns an event stream: + +```ts highlights={streamHighlights} +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.writeHead(200, { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }) + + const interval = setInterval(() => { + res.write("Streaming data...\n") + }, 3000) + + req.on("end", () => { + clearInterval(interval) + res.end() + }) +} +``` + +The `writeHead` method accepts two parameters: + +1. The first one is the response's status code. +2. The second is an object of key-value pairs to set the headers of the response. + +This API route opens a stream by setting the `Content-Type` in the header to `text/event-stream`. It then simulates a stream by creating an interval that writes the stream data every three seconds. + +*** + +## Do More with Responses + +The `MedusaResponse` type is based on [Express's Response](https://expressjs.com/en/api.html#res). Refer to their API reference for other uses of responses. + + +# Protected Routes + +In this chapter, you’ll learn how to create protected routes. + +## What is a Protected Route? + +A protected route is a route that requires requests to be user-authenticated before performing the route's functionality. Otherwise, the request fails, and the user is prevented access. + +*** + +## Default Protected Routes + +Medusa applies an authentication guard on routes starting with `/admin`, including custom API routes. + +Requests to `/admin` must be user-authenticated to access the route. + +Refer to the API Reference for [Admin](https://docs.medusajs.com/api/admin#authentication) and [Store](https://docs.medusajs.com/api/store#authentication) authentication methods. + +*** + +## Protect Custom API Routes + +To protect custom API Routes to only allow authenticated customer or admin users, use the `authenticate` middleware from the Medusa Framework. + +For example: + +```ts title="src/api/middlewares.ts" highlights={highlights} +import { + defineMiddlewares, + authenticate, +} from "@medusajs/framework/http" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom/admin*", + middlewares: [authenticate("user", ["session", "bearer", "api-key"])], + }, + { + matcher: "/custom/customer*", + middlewares: [authenticate("customer", ["session", "bearer"])], + }, + ], +}) +``` + +The `authenticate` middleware function accepts three parameters: + +1. The type of user authenticating. Use `user` for authenticating admin users, and `customer` for authenticating customers. You can also pass `*` to allow all types of users, or pass an array of actor types. +2. An array of types of authentication methods allowed. Both `user` and `customer` scopes support `session` and `bearer`. The `admin` scope also supports the `api-key` authentication method. +3. An optional object of configurations accepting the following properties: + - `allowUnauthenticated`: (default: `false`) A boolean indicating whether authentication is required. For example, you may have an API route where you want to access the logged-in customer if available, but guest customers can still access it too. + - `allowUnregistered` (default: `false`): A boolean indicating if unregistered users should be allowed access. This is useful when you want to allow users who aren’t registered to access certain routes. + +### Example: Custom Actor Type + +For example, to require authentication of a custom actor type `manager` to an API route: + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + authenticate, +} from "@medusajs/framework/http" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/manager*", + middlewares: [authenticate("manager", ["session", "bearer"])], + }, + ], +}) +``` + +Refer to the [Custom Actor-Type Guide](https://docs.medusajs.com/resources/commerce-modules/auth/create-actor-type/index.html.md) for detailed explanation on how to create a custom actor type and apply authentication middlewares. + +### Example: Allow Multiple Actor Types + +To allow multiple actor types to access an API route, pass an array of actor types to the `authenticate` middleware: + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + authenticate, +} from "@medusajs/framework/http" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom*", + middlewares: [authenticate(["user", "customer"], ["session", "bearer"])], + }, + ], +}) +``` + +*** + +## Authentication Opt-Out + +To disable the authentication guard on custom routes under the `/admin` path prefix, export an `AUTHENTICATE` variable in the route file with its value set to `false`. + +For example: + +```ts title="src/api/admin/custom/route.ts" highlights={[["15"]]} +import type { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + res.json({ + message: "Hello", + }) } -export const config = defineWidgetConfig({ - zone: "product.details.before", +export const AUTHENTICATE = false +``` + +Now, any request sent to the `/admin/custom` API route is allowed, regardless if the admin user is authenticated. + +*** + +## Authenticated Request Type + +To access the authentication details in an API route, such as the logged-in user's ID, set the type of the first request parameter to `AuthenticatedMedusaRequest`. It extends `MedusaRequest`. + +The `auth_context.actor_id` property of `AuthenticatedMedusaRequest` holds the ID of the authenticated user or customer. If there isn't any authenticated user or customer, `auth_context` is `undefined`. + +If you opt-out of authentication in a route as mentioned in the [previous section](#authentication-opt-out), you can't access the authenticated user or customer anymore. Use the [authenticate middleware](#protect-custom-api-routes) instead. + +### Retrieve Logged-In Customer's Details + +You can access the logged-in customer’s ID in all API routes starting with `/store` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object. + +For example: + +```ts title="src/api/store/custom/route.ts" highlights={[["19", "req.auth_context.actor_id", "Access the logged-in customer's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" +import type { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { Modules } from "@medusajs/framework/utils" +import { ICustomerModuleService } from "@medusajs/framework/types" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + if (req.auth_context?.actor_id) { + // retrieve customer + const customerModuleService: ICustomerModuleService = req.scope.resolve( + Modules.CUSTOMER + ) + + const customer = await customerModuleService.retrieveCustomer( + req.auth_context.actor_id + ) + } + + // ... +} +``` + +In this example, you resolve the Customer Module's main service, then use it to retrieve the logged-in customer, if available. + +### Retrieve Logged-In Admin User's Details + +You can access the logged-in admin user’s ID in all API Routes starting with `/admin` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object. + +For example: + +```ts title="src/api/admin/custom/route.ts" highlights={[["17", "req.auth_context.actor_id", "Access the logged-in admin user's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" +import type { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { Modules } from "@medusajs/framework/utils" +import { IUserModuleService } from "@medusajs/framework/types" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const userModuleService: IUserModuleService = req.scope.resolve( + Modules.USER + ) + + const user = await userModuleService.retrieveUser( + req.auth_context.actor_id + ) + + // ... +} +``` + +In the route handler, you resolve the User Module's main service, then use it to retrieve the logged-in admin user. + + +# Request Body and Query Parameter Validation + +In this chapter, you'll learn how to validate request body and query parameters in your custom API route. + +## Request Validation + +Consider you're creating a `POST` API route at `/custom`. It accepts two parameters `a` and `b` that are required numbers, and returns their sum. + +Medusa provides two middlewares to validate the request body and query paramters of incoming requests to your custom API routes: + +- `validateAndTransformBody` to validate the request's body parameters against a schema. +- `validateAndTransformQuery` to validate the request's query parameters against a schema. + +Both middlewares accept a [Zod](https://zod.dev/) schema as a parameter, which gives you flexibility in how you define your validation schema with complex rules. + +The next steps explain how to add request body and query parameter validation to the API route mentioned earlier. + +*** + +## How to Validate Request Body + +### Step 1: Create Validation Schema + +Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters. + +To create a validation schema with Zod, create a `validators.ts` file in any `src/api` subfolder. This file holds Zod schemas for each of your API routes. + +For example, create the file `src/api/custom/validators.ts` with the following content: + +```ts title="src/api/custom/validators.ts" +import { z } from "zod" + +export const PostStoreCustomSchema = z.object({ + a: z.number(), + b: z.number(), }) - -export default ProductWidget ``` -In this example, you display the API key in a widget using `import.meta.env.VITE_MY_API_KEY`. +The `PostStoreCustomSchema` variable is a Zod schema that indicates the request body is valid if: -### Type Error on import.meta.env +1. It's an object. +2. It has a property `a` that is a required number. +3. It has a property `b` that is a required number. -If you receive a type error on `import.meta.env`, create the file `src/admin/vite-env.d.ts` with the following content: +### Step 2: Add Request Body Validation Middleware -```ts title="src/admin/vite-env.d.ts" -/// +To use this schema for validating the body parameters of requests to `/custom`, use the `validateAndTransformBody` middleware provided by `@medusajs/framework/http`. It accepts the Zod schema as a parameter. + +For example, create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + validateAndTransformBody, +} from "@medusajs/framework/http" +import { PostStoreCustomSchema } from "./custom/validators" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom", + method: "POST", + middlewares: [ + validateAndTransformBody(PostStoreCustomSchema), + ], + }, + ], +}) ``` -This file tells TypeScript to recognize the `import.meta.env` object and enhances the types of your custom environment variables. +This applies the `validateAndTransformBody` middleware on `POST` requests to `/custom`. It uses the `PostStoreCustomSchema` as the validation schema. + +#### How the Validation Works + +If a request's body parameters don't pass the validation, the `validateAndTransformBody` middleware throws an error indicating the validation errors. + +If a request's body parameters are validated successfully, the middleware sets the validated body parameters in the `validatedBody` property of `MedusaRequest`. + +### Step 3: Use Validated Body in API Route + +In your API route, consume the validated body using the `validatedBody` property of `MedusaRequest`. + +For example, create the file `src/api/custom/route.ts` with the following content: + +```ts title="src/api/custom/route.ts" highlights={routeHighlights} +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { z } from "zod" +import { PostStoreCustomSchema } from "./validators" + +type PostStoreCustomSchemaType = z.infer< + typeof PostStoreCustomSchema +> + +export const POST = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + res.json({ + sum: req.validatedBody.a + req.validatedBody.b, + }) +} +``` + +In the API route, you use the `validatedBody` property of `MedusaRequest` to access the values of the `a` and `b` properties. + +To pass the request body's type as a type parameter to `MedusaRequest`, use Zod's `infer` type that accepts the type of a schema as a parameter. + +### Test it Out + +To test out the validation, send a `POST` request to `/custom` passing `a` and `b` body parameters. You can try sending incorrect request body parameters to test out the validation. + +For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data: + +```json +{ + "type": "invalid_data", + "message": "Invalid request: Field 'a' is required" +} +``` *** -## Check Node Environment in Admin Customizations +## How to Validate Request Query Parameters -To check the current environment, Vite exposes two variables: +The steps to validate the request query parameters are the similar to that of [validating the body](#how-to-validate-request-body). -- `import.meta.env.DEV`: Returns `true` if the current environment is development. -- `import.meta.env.PROD`: Returns `true` if the current environment is production. +### Step 1: Create Validation Schema -Learn more about other Vite environment variables in the [Vite documentation](https://vite.dev/guide/env-and-mode). +The first step is to create a schema with Zod with the rules of the accepted query parameters. + +Consider that the API route accepts two query parameters `a` and `b` that are numbers, similar to the previous section. + +Create the file `src/api/custom/validators.ts` with the following content: + +```ts title="src/api/custom/validators.ts" +import { z } from "zod" + +export const PostStoreCustomSchema = z.object({ + a: z.preprocess( + (val) => { + if (val && typeof val === "string") { + return parseInt(val) + } + return val + }, + z + .number() + ), + b: z.preprocess( + (val) => { + if (val && typeof val === "string") { + return parseInt(val) + } + return val + }, + z + .number() + ), +}) +``` + +Since a query parameter's type is originally a string or array of strings, you have to use Zod's `preprocess` method to validate other query types, such as numbers. + +For both `a` and `b`, you transform the query parameter's value to an integer first if it's a string, then, you check that the resulting value is a number. + +### Step 2: Add Request Query Validation Middleware + +Next, you'll use the schema to validate incoming requests' query parameters to the `/custom` API route. + +Add the `validateAndTransformQuery` middleware to the API route in the file `src/api/middlewares.ts`: + +```ts title="src/api/middlewares.ts" +import { + validateAndTransformQuery, + defineMiddlewares, +} from "@medusajs/framework/http" +import { PostStoreCustomSchema } from "./custom/validators" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom", + method: "POST", + middlewares: [ + validateAndTransformQuery( + PostStoreCustomSchema, + {} + ), + ], + }, + ], +}) +``` + +The `validateAndTransformQuery` accepts two parameters: + +- The first one is the Zod schema to validate the query parameters against. +- The second one is an object of options for retrieving data using Query, which you can learn more about in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). + +#### How the Validation Works + +If a request's query parameters don't pass the validation, the `validateAndTransformQuery` middleware throws an error indicating the validation errors. + +If a request's query parameters are validated successfully, the middleware sets the validated query parameters in the `validatedQuery` property of `MedusaRequest`. + +### Step 3: Use Validated Query in API Route + +Finally, use the validated query in the API route. The `MedusaRequest` parameter has a `validatedQuery` parameter that you can use to access the validated parameters. + +For example, create the file `src/api/custom/route.ts` with the following content: + +```ts title="src/api/custom/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const a = req.validatedQuery.a as number + const b = req.validatedQuery.b as number + + res.json({ + sum: a + b, + }) +} +``` + +In the API route, you use the `validatedQuery` property of `MedusaRequest` to access the values of the `a` and `b` properties as numbers, then return in the response their sum. + +### Test it Out + +To test out the validation, send a `POST` request to `/custom` with `a` and `b` query parameters. You can try sending incorrect query parameters to see how the validation works. + +For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data: + +```json +{ + "type": "invalid_data", + "message": "Invalid request: Field 'a' is required" +} +``` *** -## Environment Variables in Production +## Learn More About Validation Schemas -When you build the Medusa application, including the Medusa Admin, with the `build` command, the environment variables are inlined into the build. This means that you can't change the environment variables without rebuilding the application. - -For example, the `VITE_MY_API_KEY` environment variable in the example above will be replaced with the actual value during the build process. +To see different examples and learn more about creating a validation schema, refer to [Zod's documentation](https://zod.dev). # Admin Widgets @@ -7971,6 +9659,266 @@ Refer to [this reference](https://docs.medusajs.com/resources/admin-widget-injec To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components. +# Seed Data with Custom CLI Script + +In this chapter, you'll learn how to seed data using a custom CLI script. + +## How to Seed Data + +To seed dummy data for development or demo purposes, use a custom CLI script. + +In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md), to seed data. + +### Example: Seed Dummy Products + +In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products. + +First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script: + +```bash npm2yarn +npm install --save-dev @faker-js/faker +``` + +Then, create the file `src/scripts/demo-products.ts` with the following content: + +```ts title="src/scripts/demo-products.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" +import { ExecArgs } from "@medusajs/framework/types" +import { faker } from "@faker-js/faker" +import { + ContainerRegistrationKeys, + Modules, + ProductStatus, +} from "@medusajs/framework/utils" +import { + createInventoryLevelsWorkflow, + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" + +export default async function seedDummyProducts({ + container, +}: ExecArgs) { + const salesChannelModuleService = container.resolve( + Modules.SALES_CHANNEL + ) + const logger = container.resolve( + ContainerRegistrationKeys.LOGGER + ) + const query = container.resolve( + ContainerRegistrationKeys.QUERY + ) + + const defaultSalesChannel = await salesChannelModuleService + .listSalesChannels({ + name: "Default Sales Channel", + }) + + const sizeOptions = ["S", "M", "L", "XL"] + const colorOptions = ["Black", "White"] + const currency_code = "eur" + const productsNum = 50 + + // TODO seed products +} +``` + +So far, in the script, you: + +- Resolve the Sales Channel Module's main service to retrieve the application's default sales channel. This is the sales channel the dummy products will be available in. +- Resolve the Logger to log messages in the terminal, and Query to later retrieve data useful for the seeded products. +- Initialize some default data to use when seeding the products next. + +Next, replace the `TODO` with the following: + +```ts title="src/scripts/demo-products.ts" +const productsData = new Array(productsNum).fill(0).map((_, index) => { + const title = faker.commerce.product() + "_" + index + return { + title, + is_giftcard: true, + description: faker.commerce.productDescription(), + status: ProductStatus.PUBLISHED, + options: [ + { + title: "Size", + values: sizeOptions, + }, + { + title: "Color", + values: colorOptions, + }, + ], + images: [ + { + url: faker.image.urlPlaceholder({ + text: title, + }), + }, + { + url: faker.image.urlPlaceholder({ + text: title, + }), + }, + ], + variants: new Array(10).fill(0).map((_, variantIndex) => ({ + title: `${title} ${variantIndex}`, + sku: `variant-${variantIndex}${index}`, + prices: new Array(10).fill(0).map((_, priceIndex) => ({ + currency_code, + amount: 10 * priceIndex, + })), + options: { + Size: sizeOptions[Math.floor(Math.random() * 3)], + }, + })), + shipping_profile_id: "sp_123", + sales_channels: [ + { + id: defaultSalesChannel[0].id, + }, + ], + } +}) + +// TODO seed products +``` + +You generate fifty products using the sales channel and variables you initialized, and using Faker for random data, such as the product's title or images. + +Then, replace the new `TODO` with the following: + +```ts title="src/scripts/demo-products.ts" +const { result: products } = await createProductsWorkflow(container).run({ + input: { + products: productsData, + }, +}) + +logger.info(`Seeded ${products.length} products.`) + +// TODO add inventory levels +``` + +You create the generated products using the `createProductsWorkflow` imported previously from `@medusajs/medusa/core-flows`. It accepts the product data as input, and returns the created products. + +Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following: + +```ts title="src/scripts/demo-products.ts" +logger.info("Seeding inventory levels.") + +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: ["id"], +}) + +const { data: inventoryItems } = await query.graph({ + entity: "inventory_item", + fields: ["id"], +}) + +const inventoryLevels = inventoryItems.map((inventoryItem) => ({ + location_id: stockLocations[0].id, + stocked_quantity: 1000000, + inventory_item_id: inventoryItem.id, +})) + +await createInventoryLevelsWorkflow(container).run({ + input: { + inventory_levels: inventoryLevels, + }, +}) + +logger.info("Finished seeding inventory levels data.") +``` + +You use Query to retrieve the stock location, to use the first location in the application, and the inventory items. + +Then, you generate inventory levels for each inventory item, associating it with the first stock location. + +Finally, you use the `createInventoryLevelsWorkflow` from Medusa's core workflows to create the inventory levels. + +### Test Script + +To test out the script, run the following command in your project's directory: + +```bash +npx medusa exec ./src/scripts/demo-products.ts +``` + +This seeds the products to your database. If you run your Medusa application and view the products in the dashboard, you'll find fifty new products. + + +# Add Data Model Check Constraints + +In this chapter, you'll learn how to add check constraints to your data model. + +## What is a Check Constraint? + +A check constraint is a condition that must be satisfied by records inserted into a database table, otherwise an error is thrown. + +For example, if you have a data model with a `price` property, you want to only allow positive number values. So, you add a check constraint that fails when inserting a record with a negative price value. + +*** + +## How to Set a Check Constraint? + +To set check constraints on a data model, use the `checks` method. This method accepts an array of check constraints to apply on the data model. + +For example, to set a check constraint on a `price` property that ensures its value can only be a positive number: + +```ts highlights={checks1Highlights} +import { model } from "@medusajs/framework/utils" + +const CustomProduct = model.define("custom_product", { + // ... + price: model.bigNumber(), +}) +.checks([ + (columns) => `${columns.price} >= 0`, +]) +``` + +The item passed in the array parameter of `checks` can be a callback function that accepts as a parameter an object whose keys are the names of the properties in the data model schema, and values the respective column name in the database. + +The function returns a string indicating the [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). In the expression, use the `columns` parameter to access a property's column name. + +You can also pass an object to the `checks` method: + +```ts highlights={checks2Highlights} +import { model } from "@medusajs/framework/utils" + +const CustomProduct = model.define("custom_product", { + // ... + price: model.bigNumber(), +}) +.checks([ + { + name: "custom_product_price_check", + expression: (columns) => `${columns.price} >= 0`, + }, +]) +``` + +The object accepts the following properties: + +- `name`: The check constraint's name. +- `expression`: A function similar to the one that can be passed to the array. It accepts an object of columns and returns an [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). + +*** + +## Apply in Migrations + +After adding the check constraint, make sure to generate and run migrations if you already have the table in the database. Otherwise, the check constraint won't be reflected. + +To generate a migration for the data model's module then reflect it on the database, run the following command: + +```bash +npx medusa db:generate custom_module +npx medusa db:migrate +``` + +The first command generates the migration under the `migrations` directory of your module's directory, and the second reflects it on the database. + + # Data Model Database Index In this chapter, you’ll learn how to define a database index on a data model. @@ -8598,398 +10546,6 @@ The `cascades` method accepts an object. Its key is the operation’s name, such In the example above, when a store is deleted, its associated products are also deleted. -# Infer Type of Data Model - -In this chapter, you'll learn how to infer the type of a data model. - -## How to Infer Type of Data Model? - -Consider you have a `Post` data model. You can't reference this data model in a type, such as a workflow input or service method output types, since it's a variable. - -Instead, Medusa provides `InferTypeOf` that transforms your data model to a type. - -For example: - -```ts -import { InferTypeOf } from "@medusajs/framework/types" -import { Post } from "../modules/blog/models/post" // relative path to the model - -export type Post = InferTypeOf -``` - -`InferTypeOf` accepts as a type argument the type of the data model. - -Since the `Post` data model is a variable, use the `typeof` operator to pass the data model as a type argument to `InferTypeOf`. - -You can now use the `Post` type to reference a data model in other types, such as in workflow inputs or service method outputs: - -```ts title="Example Service" -// other imports... -import { InferTypeOf } from "@medusajs/framework/types" -import { Post } from "../models/post" - -type Post = InferTypeOf - -class BlogModuleService extends MedusaService({ Post }) { - async doSomething(): Promise { - // ... - } -} -``` - - -# Data Model Properties - -In this chapter, you'll learn about the different property types you can use in a data model and how to configure a data model's properties. - -## Data Model's Default Properties - -By default, Medusa creates the following properties for every data model: - -- `created_at`: A [dateTime](#dateTime) property that stores when a record of the data model was created. -- `updated_at`: A [dateTime](#dateTime) property that stores when a record of the data model was updated. -- `deleted_at`: A [dateTime](#dateTime) property that stores when a record of the data model was deleted. When you soft-delete a record, Medusa sets the `deleted_at` property to the current date. - -*** - -## Property Types - -This section covers the different property types you can define in a data model's schema using the `model` methods. - -### id - -The `id` method defines an automatically generated string ID property. The generated ID is a unique string that has a mix of letters and numbers. - -For example: - -```ts highlights={idHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - id: model.id(), - // ... -}) - -export default Post -``` - -### text - -The `text` method defines a string property. - -For example: - -```ts highlights={textHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - name: model.text(), - // ... -}) - -export default Post -``` - -### number - -The `number` method defines a number property. - -For example: - -```ts highlights={numberHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - age: model.number(), - // ... -}) - -export default Post -``` - -### float - -This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2). - -The `float` method defines a number property that allows for values with decimal places. - -Use this property type when it's less important to have high precision for numbers with large decimal places. Alternatively, for higher percision, use the [bigNumber property](#bignumber). - -For example: - -```ts highlights={floatHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - rating: model.float(), - // ... -}) - -export default Post -``` - -### bigNumber - -The `bigNumber` method defines a number property that expects large numbers, such as prices. - -Use this property type when it's important to have high precision for numbers with large decimal places. Alternatively, for less percision, use the [float property](#float). - -For example: - -```ts highlights={bigNumberHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - price: model.bigNumber(), - // ... -}) - -export default Post -``` - -### boolean - -The `boolean` method defines a boolean property. - -For example: - -```ts highlights={booleanHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - hasAccount: model.boolean(), - // ... -}) - -export default Post -``` - -### enum - -The `enum` method defines a property whose value can only be one of the specified values. - -For example: - -```ts highlights={enumHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - color: model.enum(["black", "white"]), - // ... -}) - -export default Post -``` - -The `enum` method accepts an array of possible string values. - -### dateTime - -The `dateTime` method defines a timestamp property. - -For example: - -```ts highlights={dateTimeHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - date_of_birth: model.dateTime(), - // ... -}) - -export default Post -``` - -### json - -The `json` method defines a property whose value is a stringified JSON object. - -For example: - -```ts highlights={jsonHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - metadata: model.json(), - // ... -}) - -export default Post -``` - -### array - -The `array` method defines an array of strings property. - -For example: - -```ts highlights={arrHightlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - names: model.array(), - // ... -}) - -export default Post -``` - -### Properties Reference - -Refer to the [Data Model Language (DML) reference](https://docs.medusajs.com/resources/references/data-model/index.html.md) for a full reference of the properties. - -*** - -## Set Primary Key Property - -To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method. - -For example: - -```ts highlights={highlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - id: model.id().primaryKey(), - // ... -}) - -export default Post -``` - -In the example above, the `id` property is defined as the data model's primary key. - -*** - -## Property Default Value - -Use the `default` method on a property's definition to specify the default value of a property. - -For example: - -```ts highlights={defaultHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - color: model - .enum(["black", "white"]) - .default("black"), - age: model - .number() - .default(0), - // ... -}) - -export default Post -``` - -In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`. - -*** - -## Make Property Optional - -Use the `nullable` method to indicate that a property’s value can be `null`. This is useful when you want a property to be optional. - -For example: - -```ts highlights={nullableHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - price: model.bigNumber().nullable(), - // ... -}) - -export default Post -``` - -In the example above, the `price` property is configured to allow `null` values, making it optional. - -*** - -## Unique Property - -The `unique` method indicates that a property’s value must be unique in the database through a unique index. - -For example: - -```ts highlights={uniqueHighlights} -import { model } from "@medusajs/framework/utils" - -const User = model.define("user", { - email: model.text().unique(), - // ... -}) - -export default User -``` - -In this example, multiple users can’t have the same email. - -*** - -## Define Database Index on Property - -Use the `index` method on a property's definition to define a database index. - -For example: - -```ts highlights={dbIndexHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - id: model.id().primaryKey(), - name: model.text().index( - "IDX_MY_CUSTOM_NAME" - ), -}) - -export default Post -``` - -The `index` method optionally accepts the name of the index as a parameter. - -In this example, you define an index on the `name` property. - -*** - -## Define a Searchable Property - -Methods generated by the [service factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters. - -When the `q` filter is passed, the data model's searchable properties are queried to find matching records. - -Use the `searchable` method on a `text` property to indicate that it's searchable. - -For example: - -```ts highlights={searchableHighlights} -import { model } from "@medusajs/framework/utils" - -const Post = model.define("post", { - title: model.text().searchable(), - // ... -}) - -export default Post -``` - -In this example, the `title` property is searchable. - -### Search Example - -If you pass a `q` filter to the `listPosts` method: - -```ts -const posts = await blogModuleService.listPosts({ - q: "New Products", -}) -``` - -This retrieves records that include `New Products` in their `title` property. - - # Migrations In this chapter, you'll learn what a migration is and how to generate a migration or write it manually. @@ -9256,49 +10812,162 @@ If you execute the `performAction` method of your service, the event is emitted Any subscribers listening to the event are also executed. -# Event Data Payload +# Add Columns to a Link Table -In this chapter, you'll learn how subscribers receive an event's data payload. +In this chapter, you'll learn how to add custom columns to a link definition's table and manage them. -## Access Event's Data Payload +## Link Table's Default Columns -When events are emitted, they’re emitted with a data payload. +When you define a link between two data models, Medusa creates a link table in the database to store the IDs of the linked records. You can learn more about the created table in the [Module Links chapter](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). -The object that the subscriber function receives as a parameter has an `event` property, which is an object holding the event payload in a `data` property with additional context. +In various cases, you might need to store additional data in the link table. For example, if you define a link between a `product` and a `post`, you might want to store the publish date of the product's post in the link table. + +In those cases, you can add a custom column to a link's table in the link definition. You can later set that column whenever you create or update a link between the linked records. + +*** + +## How to Add Custom Columns to a Link's Table? + +The `defineLink` function used to define a link accepts a third parameter, which is an object of options. + +To add custom columns to a link's table, pass in the third parameter of `defineLink` a `database` property: + +```ts highlights={linkHighlights} +import BlogModule from "../modules/blog" +import ProductModule from "@medusajs/medusa/product" +import { defineLink } from "@medusajs/framework/utils" + +export default defineLink( + ProductModule.linkable.product, + BlogModule.linkable.blog, + { + database: { + extraColumns: { + metadata: { + type: "json", + }, + }, + }, + } +) +``` + +This adds to the table created for the link between `product` and `blog` a `metadata` column of type `json`. + +### Database Options + +The `database` property defines configuration for the table created in the database. + +Its `extraColumns` property defines custom columns to create in the link's table. + +`extraColumns`'s value is an object whose keys are the names of the columns, and values are the column's configurations as an object. + +### Column Configurations + +The column's configurations object accepts the following properties: + +- `type`: The column's type. Possible values are: + - `string` + - `text` + - `integer` + - `boolean` + - `date` + - `time` + - `datetime` + - `enum` + - `json` + - `array` + - `enumArray` + - `float` + - `double` + - `decimal` + - `bigint` + - `mediumint` + - `smallint` + - `tinyint` + - `blob` + - `uuid` + - `uint8array` +- `defaultValue`: The column's default value. +- `nullable`: Whether the column can have `null` values. + +*** + +## Set Custom Column when Creating Link + +The object you pass to Link's `create` method accepts a `data` property. Its value is an object whose keys are custom column names, and values are the value of the custom column for this link. For example: -```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" -import type { - SubscriberArgs, - SubscriberConfig, -} from "@medusajs/framework" +Learn more about Link, how to resolve it, and its methods in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md). -export default async function productCreateHandler({ - event, -}: SubscriberArgs<{ id: string }>) { - const productId = event.data.id - console.log(`The product ${productId} was created`) -} - -export const config: SubscriberConfig = { - event: "product.created", -} +```ts +await link.create({ + [Modules.PRODUCT]: { + product_id: "123", + }, + [BLOG_MODULE]: { + post_id: "321", + }, + data: { + metadata: { + test: true, + }, + }, +}) ``` -The `event` object has the following properties: +*** -- data: (\`object\`) The data payload of the event. Its properties are different for each event. -- name: (string) The name of the triggered event. -- metadata: (\`object\`) Additional data and context of the emitted event. +## Retrieve Custom Column with Link -This logs the product ID received in the `product.created` event’s data payload to the console. +To retrieve linked records with their custom columns, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). A module link's definition, exported by a file under `src/links`, has a special `entryPoint` property. Use this property when specifying the `entity` property in Query's `graph` method. -{/* --- +For example: -## List of Events with Data Payload +```ts highlights={retrieveHighlights} +import productPostLink from "../links/product-post" -Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */} +// ... + +const { data } = await query.graph({ + entity: productPostLink.entryPoint, + fields: ["metadata", "product.*", "post.*"], + filters: { + product_id: "prod_123", + }, +}) +``` + +This retrieves the product of id `prod_123` and its linked `post` records. + +In the `fields` array you pass `metadata`, which is the custom column to retrieve of the link. + +*** + +## Update Custom Column's Value + +Link's `create` method updates a link's data if the link between the specified records already exists. + +So, to update the value of a custom column in a created link, use the `create` method again passing it a new value for the custom column. + +For example: + +```ts +await link.create({ + [Modules.PRODUCT]: { + product_id: "123", + }, + [BLOG_MODULE]: { + post_id: "321", + }, + data: { + metadata: { + test: false, + }, + }, +}) +``` # Module Link Direction @@ -9362,6 +11031,210 @@ export default defineLink( ``` +# Link + +In this chapter, you’ll learn what Link is and how to use it to manage links. + +As of [Medusa v2.2.0](https://github.com/medusajs/medusa/releases/tag/v2.2.0), Remote Link has been deprecated in favor of Link. They have the same usage, so you only need to change the key used to resolve the tool from the Medusa container as explained below. + +## What is Link? + +Link is a class with utility methods to manage links between data models. It’s registered in the Medusa container under the `link` registration name. + +For example: + +```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" +import { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +): Promise { + const link = req.scope.resolve( + ContainerRegistrationKeys.LINK + ) + + // ... +} +``` + +You can use its methods to manage links, such as create or delete links. + +*** + +## Create Link + +To create a link between records of two data models, use the `create` method of Link. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) +``` + +The `create` method accepts as a parameter an object. The object’s keys are the names of the linked modules. + +The keys (names of linked modules) must be in the same [direction](https://docs.medusajs.com/learn/fundamentals/module-links/directions/index.html.md) of the link definition. + +The value of each module’s property is an object, whose keys are of the format `{data_model_snake_name}_id`, and values are the IDs of the linked record. + +So, in the example above, you link a record of the `MyCustom` data model in a `hello` module to a `Product` record in the Product Module. + +### Enforced Integrity Constraints on Link Creation + +Medusa enforces integrity constraints on links based on the link's relation type. So, an error is thrown in the following scenarios: + +- If the link is one-to-one and one of the linked records already has a link to another record of the same data model. For example: + +```ts +// no error +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) + +// throws an error because `prod_123` already has a link to `mc_123` +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_456", + }, +}) +``` + +- If the link is one-to-many and the "one" side already has a link to another record of the same data model. For example, if a product can have many `MyCustom` records, but a `MyCustom` record can only have one product: + +```ts +// no error +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) + +// also no error +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_456", + }, +}) + +// throws an error because `mc_123` already has a link to `prod_123` +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_456", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) +``` + +There are no integrity constraints in a many-to-many link, so you can create multiple links between the same records. + +*** + +## Dismiss Link + +To remove a link between records of two data models, use the `dismiss` method of Link. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.dismiss({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) +``` + +The `dismiss` method accepts the same parameter type as the [create method](#create-link). + +The keys (names of linked modules) must be in the same [direction](https://docs.medusajs.com/learn/fundamentals/module-links/directions/index.html.md) of the link definition. + +*** + +## Cascade Delete Linked Records + +If a record is deleted, use the `delete` method of Link to delete all linked records. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await productModuleService.deleteVariants([variant.id]) + +await link.delete({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, +}) +``` + +This deletes all records linked to the deleted product. + +*** + +## Restore Linked Records + +If a record that was previously soft-deleted is now restored, use the `restore` method of Link to restore all linked records. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await productModuleService.restoreProducts(["prod_123"]) + +await link.restore({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, +}) +``` + + # Query In this chapter, you’ll learn about Query and how to use it to fetch data from modules. @@ -9918,6 +11791,51 @@ Try passing one of the Query configuration parameters, like `fields` or `limit`, Learn more about [specifing fields and relations](https://docs.medusajs.com/api/store#select-fields-and-relations) and [pagination](https://docs.medusajs.com/api/store#pagination) in the API reference. +# Event Data Payload + +In this chapter, you'll learn how subscribers receive an event's data payload. + +## Access Event's Data Payload + +When events are emitted, they’re emitted with a data payload. + +The object that the subscriber function receives as a parameter has an `event` property, which is an object holding the event payload in a `data` property with additional context. + +For example: + +```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" +import type { + SubscriberArgs, + SubscriberConfig, +} from "@medusajs/framework" + +export default async function productCreateHandler({ + event, +}: SubscriberArgs<{ id: string }>) { + const productId = event.data.id + console.log(`The product ${productId} was created`) +} + +export const config: SubscriberConfig = { + event: "product.created", +} +``` + +The `event` object has the following properties: + +- data: (\`object\`) The data payload of the event. Its properties are different for each event. +- name: (string) The name of the triggered event. +- metadata: (\`object\`) Additional data and context of the emitted event. + +This logs the product ID received in the `product.created` event’s data payload to the console. + +{/* --- + +## List of Events with Data Payload + +Refer to [this reference](!resources!/references/events) for a full list of events emitted by Medusa and their data payloads. */} + + # Query Context In this chapter, you'll learn how to pass contexts when retrieving data with [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). @@ -10144,209 +12062,357 @@ In this example, you retrieve products and their associated posts. You also pass To handle the context, you override the generated `listPosts` method of the Blog Module as explained [previously](#how-to-use-query-context). -# Link +# Data Model Properties -In this chapter, you’ll learn what Link is and how to use it to manage links. +In this chapter, you'll learn about the different property types you can use in a data model and how to configure a data model's properties. -As of [Medusa v2.2.0](https://github.com/medusajs/medusa/releases/tag/v2.2.0), Remote Link has been deprecated in favor of Link. They have the same usage, so you only need to change the key used to resolve the tool from the Medusa container as explained below. +## Data Model's Default Properties -## What is Link? +By default, Medusa creates the following properties for every data model: -Link is a class with utility methods to manage links between data models. It’s registered in the Medusa container under the `link` registration name. +- `created_at`: A [dateTime](#dateTime) property that stores when a record of the data model was created. +- `updated_at`: A [dateTime](#dateTime) property that stores when a record of the data model was updated. +- `deleted_at`: A [dateTime](#dateTime) property that stores when a record of the data model was deleted. When you soft-delete a record, Medusa sets the `deleted_at` property to the current date. + +*** + +## Property Types + +This section covers the different property types you can define in a data model's schema using the `model` methods. + +### id + +The `id` method defines an automatically generated string ID property. The generated ID is a unique string that has a mix of letters and numbers. For example: -```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" -import { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { - ContainerRegistrationKeys, -} from "@medusajs/framework/utils" +```ts highlights={idHighlights} +import { model } from "@medusajs/framework/utils" -export async function POST( - req: MedusaRequest, - res: MedusaResponse -): Promise { - const link = req.scope.resolve( - ContainerRegistrationKeys.LINK - ) - +const Post = model.define("post", { + id: model.id(), // ... -} +}) + +export default Post ``` -You can use its methods to manage links, such as create or delete links. +### text -*** - -## Create Link - -To create a link between records of two data models, use the `create` method of Link. +The `text` method defines a string property. For example: -```ts -import { Modules } from "@medusajs/framework/utils" +```ts highlights={textHighlights} +import { model } from "@medusajs/framework/utils" -// ... - -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, +const Post = model.define("post", { + name: model.text(), + // ... }) + +export default Post ``` -The `create` method accepts as a parameter an object. The object’s keys are the names of the linked modules. +### number -The keys (names of linked modules) must be in the same [direction](https://docs.medusajs.com/learn/fundamentals/module-links/directions/index.html.md) of the link definition. - -The value of each module’s property is an object, whose keys are of the format `{data_model_snake_name}_id`, and values are the IDs of the linked record. - -So, in the example above, you link a record of the `MyCustom` data model in a `hello` module to a `Product` record in the Product Module. - -### Enforced Integrity Constraints on Link Creation - -Medusa enforces integrity constraints on links based on the link's relation type. So, an error is thrown in the following scenarios: - -- If the link is one-to-one and one of the linked records already has a link to another record of the same data model. For example: - -```ts -// no error -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, -}) - -// throws an error because `prod_123` already has a link to `mc_123` -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_456", - }, -}) -``` - -- If the link is one-to-many and the "one" side already has a link to another record of the same data model. For example, if a product can have many `MyCustom` records, but a `MyCustom` record can only have one product: - -```ts -// no error -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, -}) - -// also no error -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_456", - }, -}) - -// throws an error because `mc_123` already has a link to `prod_123` -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_456", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, -}) -``` - -There are no integrity constraints in a many-to-many link, so you can create multiple links between the same records. - -*** - -## Dismiss Link - -To remove a link between records of two data models, use the `dismiss` method of Link. +The `number` method defines a number property. For example: -```ts -import { Modules } from "@medusajs/framework/utils" +```ts highlights={numberHighlights} +import { model } from "@medusajs/framework/utils" -// ... - -await link.dismiss({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, +const Post = model.define("post", { + age: model.number(), + // ... }) + +export default Post ``` -The `dismiss` method accepts the same parameter type as the [create method](#create-link). +### float -The keys (names of linked modules) must be in the same [direction](https://docs.medusajs.com/learn/fundamentals/module-links/directions/index.html.md) of the link definition. +This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2). -*** +The `float` method defines a number property that allows for values with decimal places. -## Cascade Delete Linked Records - -If a record is deleted, use the `delete` method of Link to delete all linked records. +Use this property type when it's less important to have high precision for numbers with large decimal places. Alternatively, for higher percision, use the [bigNumber property](#bignumber). For example: -```ts -import { Modules } from "@medusajs/framework/utils" +```ts highlights={floatHighlights} +import { model } from "@medusajs/framework/utils" -// ... - -await productModuleService.deleteVariants([variant.id]) - -await link.delete({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, +const Post = model.define("post", { + rating: model.float(), + // ... }) + +export default Post ``` -This deletes all records linked to the deleted product. +### bigNumber -*** +The `bigNumber` method defines a number property that expects large numbers, such as prices. -## Restore Linked Records - -If a record that was previously soft-deleted is now restored, use the `restore` method of Link to restore all linked records. +Use this property type when it's important to have high precision for numbers with large decimal places. Alternatively, for less percision, use the [float property](#float). For example: +```ts highlights={bigNumberHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + price: model.bigNumber(), + // ... +}) + +export default Post +``` + +### boolean + +The `boolean` method defines a boolean property. + +For example: + +```ts highlights={booleanHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + hasAccount: model.boolean(), + // ... +}) + +export default Post +``` + +### enum + +The `enum` method defines a property whose value can only be one of the specified values. + +For example: + +```ts highlights={enumHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + color: model.enum(["black", "white"]), + // ... +}) + +export default Post +``` + +The `enum` method accepts an array of possible string values. + +### dateTime + +The `dateTime` method defines a timestamp property. + +For example: + +```ts highlights={dateTimeHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + date_of_birth: model.dateTime(), + // ... +}) + +export default Post +``` + +### json + +The `json` method defines a property whose value is a stringified JSON object. + +For example: + +```ts highlights={jsonHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + metadata: model.json(), + // ... +}) + +export default Post +``` + +### array + +The `array` method defines an array of strings property. + +For example: + +```ts highlights={arrHightlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + names: model.array(), + // ... +}) + +export default Post +``` + +### Properties Reference + +Refer to the [Data Model Language (DML) reference](https://docs.medusajs.com/resources/references/data-model/index.html.md) for a full reference of the properties. + +*** + +## Set Primary Key Property + +To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method. + +For example: + +```ts highlights={highlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + id: model.id().primaryKey(), + // ... +}) + +export default Post +``` + +In the example above, the `id` property is defined as the data model's primary key. + +*** + +## Property Default Value + +Use the `default` method on a property's definition to specify the default value of a property. + +For example: + +```ts highlights={defaultHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + color: model + .enum(["black", "white"]) + .default("black"), + age: model + .number() + .default(0), + // ... +}) + +export default Post +``` + +In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`. + +*** + +## Make Property Optional + +Use the `nullable` method to indicate that a property’s value can be `null`. This is useful when you want a property to be optional. + +For example: + +```ts highlights={nullableHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + price: model.bigNumber().nullable(), + // ... +}) + +export default Post +``` + +In the example above, the `price` property is configured to allow `null` values, making it optional. + +*** + +## Unique Property + +The `unique` method indicates that a property’s value must be unique in the database through a unique index. + +For example: + +```ts highlights={uniqueHighlights} +import { model } from "@medusajs/framework/utils" + +const User = model.define("user", { + email: model.text().unique(), + // ... +}) + +export default User +``` + +In this example, multiple users can’t have the same email. + +*** + +## Define Database Index on Property + +Use the `index` method on a property's definition to define a database index. + +For example: + +```ts highlights={dbIndexHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + id: model.id().primaryKey(), + name: model.text().index( + "IDX_MY_CUSTOM_NAME" + ), +}) + +export default Post +``` + +The `index` method optionally accepts the name of the index as a parameter. + +In this example, you define an index on the `name` property. + +*** + +## Define a Searchable Property + +Methods generated by the [service factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters. + +When the `q` filter is passed, the data model's searchable properties are queried to find matching records. + +Use the `searchable` method on a `text` property to indicate that it's searchable. + +For example: + +```ts highlights={searchableHighlights} +import { model } from "@medusajs/framework/utils" + +const Post = model.define("post", { + title: model.text().searchable(), + // ... +}) + +export default Post +``` + +In this example, the `title` property is searchable. + +### Search Example + +If you pass a `q` filter to the `listPosts` method: + ```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await productModuleService.restoreProducts(["prod_123"]) - -await link.restore({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, +const posts = await blogModuleService.listPosts({ + q: "New Products", }) ``` +This retrieves records that include `New Products` in their `title` property. + # Read-Only Module Link @@ -10854,352 +12920,6 @@ If multiple posts have their `product_id` set to a product's ID, an array of pos [Sanity Integration Tutorial](https://docs.medusajs.com/resources/integrations/guides/sanity/index.html.md). -# Seed Data with Custom CLI Script - -In this chapter, you'll learn how to seed data using a custom CLI script. - -## How to Seed Data - -To seed dummy data for development or demo purposes, use a custom CLI script. - -In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md), to seed data. - -### Example: Seed Dummy Products - -In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products. - -First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script: - -```bash npm2yarn -npm install --save-dev @faker-js/faker -``` - -Then, create the file `src/scripts/demo-products.ts` with the following content: - -```ts title="src/scripts/demo-products.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" -import { ExecArgs } from "@medusajs/framework/types" -import { faker } from "@faker-js/faker" -import { - ContainerRegistrationKeys, - Modules, - ProductStatus, -} from "@medusajs/framework/utils" -import { - createInventoryLevelsWorkflow, - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -export default async function seedDummyProducts({ - container, -}: ExecArgs) { - const salesChannelModuleService = container.resolve( - Modules.SALES_CHANNEL - ) - const logger = container.resolve( - ContainerRegistrationKeys.LOGGER - ) - const query = container.resolve( - ContainerRegistrationKeys.QUERY - ) - - const defaultSalesChannel = await salesChannelModuleService - .listSalesChannels({ - name: "Default Sales Channel", - }) - - const sizeOptions = ["S", "M", "L", "XL"] - const colorOptions = ["Black", "White"] - const currency_code = "eur" - const productsNum = 50 - - // TODO seed products -} -``` - -So far, in the script, you: - -- Resolve the Sales Channel Module's main service to retrieve the application's default sales channel. This is the sales channel the dummy products will be available in. -- Resolve the Logger to log messages in the terminal, and Query to later retrieve data useful for the seeded products. -- Initialize some default data to use when seeding the products next. - -Next, replace the `TODO` with the following: - -```ts title="src/scripts/demo-products.ts" -const productsData = new Array(productsNum).fill(0).map((_, index) => { - const title = faker.commerce.product() + "_" + index - return { - title, - is_giftcard: true, - description: faker.commerce.productDescription(), - status: ProductStatus.PUBLISHED, - options: [ - { - title: "Size", - values: sizeOptions, - }, - { - title: "Color", - values: colorOptions, - }, - ], - images: [ - { - url: faker.image.urlPlaceholder({ - text: title, - }), - }, - { - url: faker.image.urlPlaceholder({ - text: title, - }), - }, - ], - variants: new Array(10).fill(0).map((_, variantIndex) => ({ - title: `${title} ${variantIndex}`, - sku: `variant-${variantIndex}${index}`, - prices: new Array(10).fill(0).map((_, priceIndex) => ({ - currency_code, - amount: 10 * priceIndex, - })), - options: { - Size: sizeOptions[Math.floor(Math.random() * 3)], - }, - })), - shipping_profile_id: "sp_123", - sales_channels: [ - { - id: defaultSalesChannel[0].id, - }, - ], - } -}) - -// TODO seed products -``` - -You generate fifty products using the sales channel and variables you initialized, and using Faker for random data, such as the product's title or images. - -Then, replace the new `TODO` with the following: - -```ts title="src/scripts/demo-products.ts" -const { result: products } = await createProductsWorkflow(container).run({ - input: { - products: productsData, - }, -}) - -logger.info(`Seeded ${products.length} products.`) - -// TODO add inventory levels -``` - -You create the generated products using the `createProductsWorkflow` imported previously from `@medusajs/medusa/core-flows`. It accepts the product data as input, and returns the created products. - -Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following: - -```ts title="src/scripts/demo-products.ts" -logger.info("Seeding inventory levels.") - -const { data: stockLocations } = await query.graph({ - entity: "stock_location", - fields: ["id"], -}) - -const { data: inventoryItems } = await query.graph({ - entity: "inventory_item", - fields: ["id"], -}) - -const inventoryLevels = inventoryItems.map((inventoryItem) => ({ - location_id: stockLocations[0].id, - stocked_quantity: 1000000, - inventory_item_id: inventoryItem.id, -})) - -await createInventoryLevelsWorkflow(container).run({ - input: { - inventory_levels: inventoryLevels, - }, -}) - -logger.info("Finished seeding inventory levels data.") -``` - -You use Query to retrieve the stock location, to use the first location in the application, and the inventory items. - -Then, you generate inventory levels for each inventory item, associating it with the first stock location. - -Finally, you use the `createInventoryLevelsWorkflow` from Medusa's core workflows to create the inventory levels. - -### Test Script - -To test out the script, run the following command in your project's directory: - -```bash -npx medusa exec ./src/scripts/demo-products.ts -``` - -This seeds the products to your database. If you run your Medusa application and view the products in the dashboard, you'll find fifty new products. - - -# Add Columns to a Link Table - -In this chapter, you'll learn how to add custom columns to a link definition's table and manage them. - -## Link Table's Default Columns - -When you define a link between two data models, Medusa creates a link table in the database to store the IDs of the linked records. You can learn more about the created table in the [Module Links chapter](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). - -In various cases, you might need to store additional data in the link table. For example, if you define a link between a `product` and a `post`, you might want to store the publish date of the product's post in the link table. - -In those cases, you can add a custom column to a link's table in the link definition. You can later set that column whenever you create or update a link between the linked records. - -*** - -## How to Add Custom Columns to a Link's Table? - -The `defineLink` function used to define a link accepts a third parameter, which is an object of options. - -To add custom columns to a link's table, pass in the third parameter of `defineLink` a `database` property: - -```ts highlights={linkHighlights} -import BlogModule from "../modules/blog" -import ProductModule from "@medusajs/medusa/product" -import { defineLink } from "@medusajs/framework/utils" - -export default defineLink( - ProductModule.linkable.product, - BlogModule.linkable.blog, - { - database: { - extraColumns: { - metadata: { - type: "json", - }, - }, - }, - } -) -``` - -This adds to the table created for the link between `product` and `blog` a `metadata` column of type `json`. - -### Database Options - -The `database` property defines configuration for the table created in the database. - -Its `extraColumns` property defines custom columns to create in the link's table. - -`extraColumns`'s value is an object whose keys are the names of the columns, and values are the column's configurations as an object. - -### Column Configurations - -The column's configurations object accepts the following properties: - -- `type`: The column's type. Possible values are: - - `string` - - `text` - - `integer` - - `boolean` - - `date` - - `time` - - `datetime` - - `enum` - - `json` - - `array` - - `enumArray` - - `float` - - `double` - - `decimal` - - `bigint` - - `mediumint` - - `smallint` - - `tinyint` - - `blob` - - `uuid` - - `uint8array` -- `defaultValue`: The column's default value. -- `nullable`: Whether the column can have `null` values. - -*** - -## Set Custom Column when Creating Link - -The object you pass to Link's `create` method accepts a `data` property. Its value is an object whose keys are custom column names, and values are the value of the custom column for this link. - -For example: - -Learn more about Link, how to resolve it, and its methods in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md). - -```ts -await link.create({ - [Modules.PRODUCT]: { - product_id: "123", - }, - [BLOG_MODULE]: { - post_id: "321", - }, - data: { - metadata: { - test: true, - }, - }, -}) -``` - -*** - -## Retrieve Custom Column with Link - -To retrieve linked records with their custom columns, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). A module link's definition, exported by a file under `src/links`, has a special `entryPoint` property. Use this property when specifying the `entity` property in Query's `graph` method. - -For example: - -```ts highlights={retrieveHighlights} -import productPostLink from "../links/product-post" - -// ... - -const { data } = await query.graph({ - entity: productPostLink.entryPoint, - fields: ["metadata", "product.*", "post.*"], - filters: { - product_id: "prod_123", - }, -}) -``` - -This retrieves the product of id `prod_123` and its linked `post` records. - -In the `fields` array you pass `metadata`, which is the custom column to retrieve of the link. - -*** - -## Update Custom Column's Value - -Link's `create` method updates a link's data if the link between the specified records already exists. - -So, to update the value of a custom column in a created link, use the `create` method again passing it a new value for the custom column. - -For example: - -```ts -await link.create({ - [Modules.PRODUCT]: { - product_id: "123", - }, - [BLOG_MODULE]: { - post_id: "321", - }, - data: { - metadata: { - test: false, - }, - }, -}) -``` - - # Commerce Modules In this chapter, you'll learn about Medusa's Commerce Modules. @@ -11244,439 +12964,45 @@ export const countProductsStep = createStep( Your workflow can use services of both custom and Commerce Modules, supporting you in building custom flows without having to re-build core commerce features. -# Create a Plugin +# Infer Type of Data Model -In this chapter, you'll learn how to create a Medusa plugin and publish it. +In this chapter, you'll learn how to infer the type of a data model. -A [plugin](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md) is a package of reusable Medusa customizations that you can install in any Medusa application. By creating and publishing a plugin, you can reuse your Medusa customizations across multiple projects or share them with the community. +## How to Infer Type of Data Model? -Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). +Consider you have a `Post` data model. You can't reference this data model in a type, such as a workflow input or service method output types, since it's a variable. -## 1. Create a Plugin Project - -Plugins are created in a separate Medusa project. This makes the development and publishing of the plugin easier. Later, you'll install that plugin in your Medusa application to test it out and use it. - -Medusa's `create-medusa-app` CLI tool provides the option to create a plugin project. Run the following command to create a new plugin project: - -```bash -npx create-medusa-app my-plugin --plugin -``` - -This will create a new Medusa plugin project in the `my-plugin` directory. - -### Plugin Directory Structure - -After the installation is done, the plugin structure will look like this: - -![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg) - -- `src/`: Contains the Medusa customizations. -- `src/admin`: Contains [admin extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md). -- `src/api`: Contains [API routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) and [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). You can add store, admin, or any custom API routes. -- `src/jobs`: Contains [scheduled jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md). -- `src/links`: Contains [module links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). -- `src/modules`: Contains [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). -- `src/provider`: Contains [module providers](#create-module-providers). -- `src/subscribers`: Contains [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). -- `src/workflows`: Contains [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). You can also add [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md) under `src/workflows/hooks`. -- `package.json`: Contains the plugin's package information, including general information and dependencies. -- `tsconfig.json`: Contains the TypeScript configuration for the plugin. - -*** - -## 2. Prepare Plugin - -### Package Name - -Before developing, testing, and publishing your plugin, make sure its name in `package.json` is correct. This is the name you'll use to install the plugin in your Medusa application. +Instead, Medusa provides `InferTypeOf` that transforms your data model to a type. For example: -```json title="package.json" -{ - "name": "@myorg/plugin-name", - // ... -} -``` - -### Package Keywords - -Medusa scrapes NPM for a list of plugins that integrate third-party services, to later showcase them on the Medusa website. If you want your plugin to appear in that listing, make sure to add the `medusa-v2` and `medusa-plugin-integration` keywords to the `keywords` field in `package.json`. - -Only plugins that integrate third-party services are listed in the Medusa integrations page. If your plugin doesn't integrate a third-party service, you can skip this step. - -```json title="package.json" -{ - "keywords": [ - "medusa-plugin-integration", - "medusa-v2" - ], - // ... -} -``` - -In addition, make sure to use one of the following keywords based on your integration type: - -|Keyword|Description|Example| -|---|---|---| -|\`medusa-plugin-analytics\`|Analytics service integration|Google Analytics| -|\`medusa-plugin-auth\`|Authentication service integration|Auth0| -|\`medusa-plugin-cms\`|CMS service integration|Contentful| -|\`medusa-plugin-notification\`|Notification service integration|Twilio SMS| -|\`medusa-plugin-payment\`|Payment service integration|PayPal| -|\`medusa-plugin-search\`|Search service integration|MeiliSearch| -|\`medusa-plugin-shipping\`|Shipping service integration|DHL| -|\`medusa-plugin-other\`|Other third-party integrations|Sentry| - -### Package Dependencies - -Your plugin project will already have the dependencies mentioned in this section. Unless you made changes to the dependencies, you can skip this section. - -In the `package.json` file you must have the Medusa dependencies as `devDependencies` and `peerDependencies`. In addition, you must have `@swc/core` as a `devDependency`, as it's used by the plugin CLI tools. - -For example, assuming `2.5.0` is the latest Medusa version: - -```json title="package.json" -{ - "devDependencies": { - "@medusajs/admin-sdk": "2.5.0", - "@medusajs/cli": "2.5.0", - "@medusajs/framework": "2.5.0", - "@medusajs/medusa": "2.5.0", - "@medusajs/test-utils": "2.5.0", - "@medusajs/ui": "4.0.4", - "@medusajs/icons": "2.5.0", - "@swc/core": "1.5.7", - }, - "peerDependencies": { - "@medusajs/admin-sdk": "2.5.0", - "@medusajs/cli": "2.5.0", - "@medusajs/framework": "2.5.0", - "@medusajs/test-utils": "2.5.0", - "@medusajs/medusa": "2.5.0", - "@medusajs/ui": "4.0.3", - "@medusajs/icons": "2.5.0", - } -} -``` - -### Package Exports - -Your plugin project will already have the exports mentioned in this section. Unless you made changes to the exports or you created your plugin before [Medusa v2.7.0](https://github.com/medusajs/medusa/releases/tag/v2.7.0), you can skip this section. - -In the `package.json` file, make sure your plugin has the following exports: - -```json title="package.json" -{ - "exports": { - "./package.json": "./package.json", - "./workflows": "./.medusa/server/src/workflows/index.js", - "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js", - "./providers/*": "./.medusa/server/src/providers/*/index.js", - "./admin": { - "import": "./.medusa/server/src/admin/index.mjs", - "require": "./.medusa/server/src/admin/index.js", - "default": "./.medusa/server/src/admin/index.js" - }, - "./*": "./.medusa/server/src/*.js" - } -} -``` - -Aside from the `./package.json`, `./providers`, and `./admin`, these exports are only a recommendation. You can cherry-pick the files and directories you want to export. - -The plugin exports the following files and directories: - -- `./package.json`: The `package.json` file. Medusa needs to access the `package.json` when registering the plugin. -- `./workflows`: The workflows exported in `./src/workflows/index.ts`. -- `./.medusa/server/src/modules/*`: The definition file of modules. This is useful if you create links to the plugin's modules in the Medusa application. -- `./providers/*`: The definition file of module providers. This is useful if your plugin includes a module provider, allowing you to register the plugin's providers in Medusa applications. Learn more in the [Create Module Providers](#create-module-providers) section. -- `./admin`: The admin extensions exported in `./src/admin/index.ts`. -- `./*`: Any other files in the plugin's `src` directory. - -*** - -## 3. Publish Plugin Locally for Development and Testing - -Medusa's CLI tool provides commands to simplify developing and testing your plugin in a local Medusa application. You start by publishing your plugin in the local package registry, then install it in your Medusa application. You can then watch for changes in the plugin as you develop it. - -### Publish and Install Local Package - -### Prerequisites - -- [Medusa application installed.](https://docs.medusajs.com/learn/installation/index.html.md) - -The first time you create your plugin, you need to publish the package into a local package registry, then install it in your Medusa application. This is a one-time only process. - -To publish the plugin to the local registry, run the following command in your plugin project: - -```bash title="Plugin project" -npx medusa plugin:publish -``` - -This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`. - -Next, navigate to your Medusa application: - -```bash title="Medusa application" -cd ~/path/to/medusa-app -``` - -Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application. - -Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency: - -```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green" -npm install --save-dev yalc -``` - -After that, run the following Medusa CLI command to install the plugin: - -```bash title="Medusa application" -npx medusa plugin:add @myorg/plugin-name -``` - -Make sure to replace `@myorg/plugin-name` with the name of your plugin as specified in `package.json`. Your plugin will be installed from the local package registry into your Medusa application. - -### Register Plugin in Medusa Application - -After installing the plugin, you need to register it in your Medusa application in the configurations defined in `medusa-config.ts`. - -Add the plugin to the `plugins` array in the `medusa-config.ts` file: - -```ts title="medusa-config.ts" highlights={pluginHighlights} -module.exports = defineConfig({ - // ... - plugins: [ - { - resolve: "@myorg/plugin-name", - options: {}, - }, - ], -}) -``` - -The `plugins` configuration is an array of objects where each object has a `resolve` key whose value is the name of the plugin package. - -#### Pass Module Options through Plugin - -Each plugin configuration also accepts an `options` property, whose value is an object of options to pass to the plugin's modules. - -For example: - -```ts title="medusa-config.ts" highlights={pluginOptionsHighlight} -module.exports = defineConfig({ - // ... - plugins: [ - { - resolve: "@myorg/plugin-name", - options: { - apiKey: true, - }, - }, - ], -}) -``` - -The `options` property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/options/index.html.md). - -### Watch Plugin Changes During Development - -While developing your plugin, you can watch for changes in the plugin and automatically update the plugin in the Medusa application using it. This is the only command you'll continuously need during your plugin development. - -To do that, run the following command in your plugin project: - -```bash title="Plugin project" -npx medusa plugin:develop -``` - -This command will: - -- Watch for changes in the plugin. Whenever a file is changed, the plugin is automatically built. -- Publish the plugin changes to the local package registry. This will automatically update the plugin in the Medusa application using it. You can also benefit from real-time HMR updates of admin extensions. - -### Start Medusa Application - -You can start your Medusa application's development server to test out your plugin: - -```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green" -npm run dev -``` - -While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application. - -*** - -## 4. Create Customizations in the Plugin - -You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin. - -- [Create a module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) -- [Create a module link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) -- [Create a workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) -- [Add a workflow hook](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md) -- [Create an API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) -- [Add a subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) -- [Add a scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md) -- [Add an admin widget](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md) -- [Add an admin UI route](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md) - -While building those customizations, you can test them in your Medusa application by [watching the plugin changes](#watch-plugin-changes-during-development) and [starting the Medusa application](#start-medusa-application). - -### Generating Migrations for Modules - -During your development, you may need to generate migrations for modules in your plugin. To do that, first, add the following environment variables in your plugin project: - -```plain title="Plugin project" -DB_USERNAME=postgres -DB_PASSWORD=123... -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=db_name -``` - -You can add these environment variables in a `.env` file in your plugin project. The variables are: - -- `DB_USERNAME`: The username of the PostgreSQL user to connect to the database. -- `DB_PASSWORD`: The password of the PostgreSQL user to connect to the database. -- `DB_HOST`: The host of the PostgreSQL database. Typically, it's `localhost` if you're running the database locally. -- `DB_PORT`: The port of the PostgreSQL database. Typically, it's `5432` if you're running the database locally. -- `DB_NAME`: The name of the PostgreSQL database to connect to. - -Then, run the following command in your plugin project to generate migrations for the modules in your plugin: - -```bash title="Plugin project" -npx medusa plugin:db:generate -``` - -This command generates migrations for all modules in the plugin. - -Finally, run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command: - -```bash title="Medusa application" -npx medusa db:migrate -``` - -The migrations in your application, including your plugin, will run and update the database. - -### Importing Module Resources - -In the [Prepare Plugin](#2-prepare-plugin) section, you learned about [exported resources](#package-exports) in your plugin. - -These exports allow you to import your plugin resources in your Medusa application, including workflows, links and modules. - -For example, to import the plugin's workflow in your Medusa application: - -`@myorg/plugin-name` is the plugin package's name. - ```ts -import { Workflow1, Workflow2 } from "@myorg/plugin-name/workflows" -import BlogModule from "@myorg/plugin-name/modules/blog" -// import other files created in plugin like ./src/types/blog.ts -import BlogType from "@myorg/plugin-name/types/blog" +import { InferTypeOf } from "@medusajs/framework/types" +import { Post } from "../modules/blog/models/post" // relative path to the model + +export type Post = InferTypeOf ``` -### Create Module Providers +`InferTypeOf` accepts as a type argument the type of the data model. -The [exported resources](#package-exports) also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or Infrastructure Module. +Since the `Post` data model is a variable, use the `typeof` operator to pass the data model as a type argument to `InferTypeOf`. -For example, assuming your plugin has a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification/index.html.md) called `my-notification`, you can register it in your Medusa application's configuration like this: +You can now use the `Post` type to reference a data model in other types, such as in workflow inputs or service method outputs: -`@myorg/plugin-name` is the plugin package's name. +```ts title="Example Service" +// other imports... +import { InferTypeOf } from "@medusajs/framework/types" +import { Post } from "../models/post" -```ts highlights={[["9"]]} title="medusa-config.ts" -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "@medusajs/medusa/notification", - options: { - providers: [ - { - resolve: "@myorg/plugin-name/providers/my-notification", - id: "my-notification", - options: { - channels: ["email"], - // provider options... - }, - }, - ], - }, - }, - ], -}) +type Post = InferTypeOf + +class BlogModuleService extends MedusaService({ Post }) { + async doSomething(): Promise { + // ... + } +} ``` -You pass to `resolve` the path to the provider relative to the plugin package. So, in this example, the `my-notification` provider is located in `./src/providers/my-notification/index.ts` of the plugin. - -To learn how to create module providers, refer to the following guides: - -- [File Module Provider](https://docs.medusajs.com/resources/references/file-provider-module/index.html.md) -- [Notification Module Provider](https://docs.medusajs.com/resources/references/notification-provider-module/index.html.md) -- [Auth Module Provider](https://docs.medusajs.com/resources/references/auth/provider/index.html.md) -- [Payment Module Provider](https://docs.medusajs.com/resources/references/payment/provider/index.html.md) -- [Fulfillment Module Provider](https://docs.medusajs.com/resources/references/fulfillment/provider/index.html.md) -- [Tax Module Provider](https://docs.medusajs.com/resources/references/tax/provider/index.html.md) - -*** - -## 5. Publish Plugin to NPM - -Make sure to add the keywords mentioned in the [Package Keywords](#package-keywords) section in your plugin's `package.json` file. - -Medusa's CLI tool provides a command that bundles your plugin to be published to npm. Once you're ready to publish your plugin publicly, run the following command in your plugin project: - -```bash -npx medusa plugin:build -``` - -The command will compile an output in the `.medusa/server` directory. - -You can now publish the plugin to npm using the [NPM CLI tool](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Run the following command to publish the plugin to npm: - -```bash -npm publish -``` - -If you haven't logged in before with your NPM account, you'll be asked to log in first. Then, your package is published publicly to be used in any Medusa application. - -### Install Public Plugin in Medusa Application - -You install a plugin that's published publicly using your package manager. For example: - -```bash npm2yarn -npm install @myorg/plugin-name -``` - -Where `@myorg/plugin-name` is the name of your plugin as published on NPM. - -Then, register the plugin in your Medusa application's configurations as explained in [this section](#register-plugin-in-medusa-application). - -*** - -## Update a Published Plugin - -To update the Medusa dependencies in a plugin, refer to [this documentation](https://docs.medusajs.com/learn/update#update-plugin-project/index.html.md). - -If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again. - -First, run the following command to change the version of the plugin: - -```bash -npm version -``` - -Where `` indicates the type of version update you’re publishing. For example, it can be `major` or `minor`. Refer to the [npm version documentation](https://docs.npmjs.com/cli/v10/commands/npm-version) for more information. - -Then, re-run the same commands for publishing a plugin: - -```bash -npx medusa plugin:build -npm publish -``` - -This will publish an updated version of your plugin under a new version. - # Module Container @@ -12383,387 +13709,6 @@ There are different Infrastructure Module types including: Refer to the [Infrastructure Modules reference](https://docs.medusajs.com/resources/infrastructure-modules/index.html.md) for a list of Medusa’s Infrastructure Modules, available modules to install, and how to create an Infrastructure Module. -# Module Isolation - -In this chapter, you'll learn how modules are isolated, and what that means for your custom development. - -- Modules can't access resources, such as services or data models, from other modules. -- Use Medusa's linking concepts, as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), to extend a module's data models and retrieve data across modules. - -## How are Modules Isolated? - -A module is unaware of any resources other than its own, such as services or data models. This means it can't access these resources if they're implemented in another module. - -For example, your custom module can't resolve the Product Module's main service or have direct relationships from its data model to the Product Module's data models. - -*** - -## Why are Modules Isolated - -Some of the module isolation's benefits include: - -- Integrate your module into any Medusa application without side-effects to your setup. -- Replace existing modules with your custom implementation, if your use case is drastically different. -- Use modules in other environments, such as Edge functions and Next.js apps. - -*** - -## How to Extend Data Model of Another Module? - -To extend the data model of another module, such as the `product` data model of the Product Module, use Medusa's linking concepts as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). - -*** - -## How to Use Services of Other Modules? - -If you're building a feature that uses functionalities from different modules, use a workflow whose steps resolve the modules' services to perform these functionalities. - -Workflows ensure data consistency through their roll-back mechanism and tracking of each execution's status, steps, input, and output. - -### Example - -For example, consider you have two modules: - -1. A module that stores and manages brands in your application. -2. A module that integrates a third-party Content Management System (CMS). - -To sync brands from your application to the third-party system, create the following steps: - -```ts title="Example Steps" highlights={stepsHighlights} -const retrieveBrandsStep = createStep( - "retrieve-brands", - async (_, { container }) => { - const brandModuleService = container.resolve( - "brandModuleService" - ) - - const brands = await brandModuleService.listBrands() - - return new StepResponse(brands) - } -) - -const createBrandsInCmsStep = createStep( - "create-brands-in-cms", - async ({ brands }, { container }) => { - const cmsModuleService = container.resolve( - "cmsModuleService" - ) - - const cmsBrands = await cmsModuleService.createBrands(brands) - - return new StepResponse(cmsBrands, cmsBrands) - }, - async (brands, { container }) => { - const cmsModuleService = container.resolve( - "cmsModuleService" - ) - - await cmsModuleService.deleteBrands( - brands.map((brand) => brand.id) - ) - } -) -``` - -The `retrieveBrandsStep` retrieves the brands from a brand module, and the `createBrandsInCmsStep` creates the brands in a third-party system using a CMS module. - -Then, create the following workflow that uses these steps: - -```ts title="Example Workflow" -export const syncBrandsWorkflow = createWorkflow( - "sync-brands", - () => { - const brands = retrieveBrandsStep() - - createBrandsInCmsStep({ brands }) - } -) -``` - -You can then use this workflow in an API route, scheduled job, or other resources that use this functionality. - - -# Loaders - -In this chapter, you’ll learn about loaders and how to use them. - -## What is a Loader? - -When building a commerce application, you'll often need to execute an action the first time the application starts. For example, if your application needs to connect to databases other than Medusa's PostgreSQL database, you might need to establish a connection on application startup. - -In Medusa, you can execute an action when the application starts using a loader. A loader is a function exported by a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), which is a package of business logic for a single domain. When the Medusa application starts, it executes all loaders exported by configured modules. - -Loaders are useful to register custom resources, such as database connections, in the [module's container](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md), which is similar to the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) but includes only [resources available to the module](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). Modules are isolated, so they can't access resources outside of them, such as a service in another module. - -Medusa isolates modules to ensure that they're re-usable across applications, aren't tightly coupled to other resources, and don't have implications when integrated into the Medusa application. Learn more about why modules are isolated in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md), and check out [this reference for the list of resources in the module's container](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). - -*** - -## How to Create a Loader? - -### 1. Implement Loader Function - -You create a loader function in a TypeScript or JavaScript file under a module's `loaders` directory. - -For example, consider you have a `hello` module, you can create a loader at `src/modules/hello/loaders/hello-world.ts` with the following content: - -![Example of loader file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732865671/Medusa%20Book/loader-dir-overview_eg6vtu.jpg) - -Learn how to create a module in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). - -```ts title="src/modules/hello/loaders/hello-world.ts" -import { - LoaderOptions, -} from "@medusajs/framework/types" - -export default async function helloWorldLoader({ - container, -}: LoaderOptions) { - const logger = container.resolve("logger") - - logger.info("[HELLO MODULE] Just started the Medusa application!") -} -``` - -The loader file exports an async function, which is the function executed when the application loads. - -The function receives an object parameter that has a `container` property, which is the module's container that you can use to resolve resources from. In this example, you resolve the Logger utility to log a message in the terminal. - -Find the list of resources in the module's container in [this reference](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). - -### 2. Export Loader in Module Definition - -After implementing the loader, you must export it in the module's definition in the `index.ts` file at the root of the module's directory. Otherwise, the Medusa application will not run it. - -So, to export the loader you implemented above in the `hello` module, add the following to `src/modules/hello/index.ts`: - -```ts title="src/modules/hello/index.ts" -// other imports... -import helloWorldLoader from "./loaders/hello-world" - -export default Module("hello", { - // ... - loaders: [helloWorldLoader], -}) -``` - -The second parameter of the `Module` function accepts a `loaders` property whose value is an array of loader functions. The Medusa application will execute these functions when it starts. - -### Test the Loader - -Assuming your module is [added to Medusa's configuration](https://docs.medusajs.com/learn/fundamentals/modules#4-add-module-to-medusas-configurations/index.html.md), you can test the loader by starting the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, you'll find the following message logged in the terminal: - -```plain -info: [HELLO MODULE] Just started the Medusa application! -``` - -This indicates that the loader in the `hello` module ran and logged this message. - -*** - -## When are Loaders Executed? - -When you start the Medusa application, it executes the loaders of all modules in their registration order. - -A loader is executed before the module's main service is instantiated. So, you can use loaders to register in the module's container resources that you want to use in the module's service. For example, you can register a database connection. - -Loaders are also useful to only load a module if a certain condition is met. For example, if you try to connect to a database in a loader but the connection fails, you can throw an error in the loader to prevent the module from being loaded. This is useful if your module depends on an external service to work. - -*** - -## Example: Register Custom MongoDB Connection - -As mentioned in this chapter's introduction, loaders are most useful when you need to register a custom resource in the module's container to re-use it in other customizations in the module. - -Consider your have a MongoDB module that allows you to perform operations on a MongoDB database. - -### Prerequisites - -- [MongoDB database that you can connect to from a local machine.](https://www.mongodb.com) -- [Install the MongoDB SDK in your Medusa application.](https://www.mongodb.com/docs/drivers/node/current/quick-start/download-and-install/#install-the-node.js-driver) - -To connect to the database, you create the following loader in your module: - -```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights} -import { LoaderOptions } from "@medusajs/framework/types" -import { asValue } from "awilix" -import { MongoClient } from "mongodb" - -type ModuleOptions = { - connection_url?: string - db_name?: string -} - -export default async function mongoConnectionLoader({ - container, - options, -}: LoaderOptions) { - if (!options.connection_url) { - throw new Error(`[MONGO MDOULE]: connection_url option is required.`) - } - if (!options.db_name) { - throw new Error(`[MONGO MDOULE]: db_name option is required.`) - } - const logger = container.resolve("logger") - - try { - const clientDb = ( - await (new MongoClient(options.connection_url)).connect() - ).db(options.db_name) - - logger.info("Connected to MongoDB") - - container.register( - "mongoClient", - asValue(clientDb) - ) - } catch (e) { - logger.error( - `[MONGO MDOULE]: An error occurred while connecting to MongoDB: ${e}` - ) - } -} -``` - -The loader function accepts in its object parameter an `options` property, which is the options passed to the module in Medusa's configurations. For example: - -```ts title="medusa-config.ts" highlights={optionHighlights} -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "./src/modules/mongo", - options: { - connection_url: process.env.MONGO_CONNECTION_URL, - db_name: process.env.MONGO_DB_NAME, - }, - }, - ], -}) -``` - -Passing options is useful when your module needs informations like connection URLs or API keys, as it ensures your module can be re-usable across applications. For the MongoDB Module, you expect two options: - -- `connection_url`: the URL to connect to the MongoDB database. -- `db_name`: The name of the database to connect to. - -In the loader, you check first that these options are set before proceeding. Then, you create an instance of the MongoDB client and connect to the database specified in the options. - -After creating the client, you register it in the module's container using the container's `register` method. The method accepts two parameters: - -1. The key to register the resource under, which in this case is `mongoClient`. You'll use this name later to resolve the client. -2. The resource to register in the container, which is the MongoDB client you created. However, you don't pass the resource as-is. Instead, you need to use an `asValue` function imported from the [awilix package](https://github.com/jeffijoe/awilix), which is the package used to implement the container functionality in Medusa. - -### Use Custom Registered Resource in Module's Service - -After registering the custom MongoDB client in the module's container, you can now resolve and use it in the module's service. - -For example: - -```ts title="src/modules/mongo/service.ts" -import type { Db } from "mongodb" - -type InjectedDependencies = { - mongoClient: Db -} - -export default class MongoModuleService { - private mongoClient_: Db - - constructor({ mongoClient }: InjectedDependencies) { - this.mongoClient_ = mongoClient - } - - async createMovie({ title }: { - title: string - }) { - const moviesCol = this.mongoClient_.collection("movie") - - const insertedMovie = await moviesCol.insertOne({ - title, - }) - - const movie = await moviesCol.findOne({ - _id: insertedMovie.insertedId, - }) - - return movie - } - - async deleteMovie(id: string) { - const moviesCol = this.mongoClient_.collection("movie") - - await moviesCol.deleteOne({ - _id: { - equals: id, - }, - }) - } -} -``` - -The service `MongoModuleService` resolves the `mongoClient` resource you registered in the loader and sets it as a class property. You then use it in the `createMovie` and `deleteMovie` methods, which create and delete a document in a `movie` collection in the MongoDB database, respectively. - -Make sure to export the loader in the module's definition in the `index.ts` file at the root directory of the module: - -```ts title="src/modules/mongo/index.ts" highlights={[["9"]]} -import { Module } from "@medusajs/framework/utils" -import MongoModuleService from "./service" -import mongoConnectionLoader from "./loaders/connection" - -export const MONGO_MODULE = "mongo" - -export default Module(MONGO_MODULE, { - service: MongoModuleService, - loaders: [mongoConnectionLoader], -}) -``` - -### Test it Out - -You can test the connection out by starting the Medusa application. If it's successful, you'll see the following message logged in the terminal: - -```bash -info: Connected to MongoDB -``` - -You can now resolve the MongoDB Module's main service in your customizations to perform operations on the MongoDB database. - - -# Modules Directory Structure - -In this document, you'll learn about the expected files and directories in your module. - -![Module Directory Structure Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1714379976/Medusa%20Book/modules-dir-overview_nqq7ne.jpg) - -## index.ts - -The `index.ts` file in the root of your module's directory is the only required file. It must export the module's definition as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). - -*** - -## service.ts - -A module must have a main service. It's created in the `service.ts` file at the root of your module directory as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). - -*** - -## Other Directories - -The following directories are optional and their content are explained more in the following chapters: - -- `models`: Holds the data models representing tables in the database. -- `migrations`: Holds the migration files used to reflect changes on the database. -- `loaders`: Holds the scripts to run on the Medusa application's start-up. - - # Multiple Services in a Module In this chapter, you'll learn how to use multiple services in a module. @@ -12892,6 +13837,33 @@ The `configModule` has a `modules` property that includes all registered modules If its value is not a `boolean`, set the service's options to the module configuration's `options` property. +# Modules Directory Structure + +In this document, you'll learn about the expected files and directories in your module. + +![Module Directory Structure Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1714379976/Medusa%20Book/modules-dir-overview_nqq7ne.jpg) + +## index.ts + +The `index.ts` file in the root of your module's directory is the only required file. It must export the module's definition as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). + +*** + +## service.ts + +A module must have a main service. It's created in the `service.ts` file at the root of your module directory as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). + +*** + +## Other Directories + +The following directories are optional and their content are explained more in the following chapters: + +- `models`: Holds the data models representing tables in the database. +- `migrations`: Holds the migration files used to reflect changes on the database. +- `loaders`: Holds the scripts to run on the Medusa application's start-up. + + # Module Options In this chapter, you’ll learn about passing options to your module from the Medusa application’s configurations and using them in the module’s resources. @@ -13270,1764 +14242,569 @@ export default BlogModuleService ``` -# Pass Additional Data to Medusa's API Route +# Scheduled Jobs Number of Executions -In this chapter, you'll learn how to pass additional data in requests to Medusa's API Route. +In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed. -## Why Pass Additional Data? +## numberOfExecutions Option -Some of Medusa's API Routes accept an `additional_data` parameter whose type is an object. The API Route passes the `additional_data` to the workflow, which in turn passes it to its hooks. +The export configuration object of the scheduled job accepts an optional property `numberOfExecutions`. Its value is a number indicating how many times the scheduled job can be executed during the Medusa application's runtime. -This is useful when you have a link from your custom module to a Commerce Module, and you want to perform an additional action when a request is sent to an existing API route. +For example: -For example, the [Create Product API Route](https://docs.medusajs.com/api/admin#products_postproducts) accepts an `additional_data` parameter. If you have a data model linked to it, you consume the `productsCreated` hook to create a record of the data model using the custom data and link it to the product. +```ts highlights={highlights} +export default async function myCustomJob() { + console.log("I'll be executed three times only.") +} -### API Routes Accepting Additional Data +export const config = { + name: "hello-world", + // execute every minute + schedule: "* * * * *", + numberOfExecutions: 3, +} +``` -### API Routes List +The above scheduled job has the `numberOfExecutions` configuration set to `3`. -- Campaigns - - [Create Campaign](https://docs.medusajs.com/api/admin#campaigns_postcampaigns) - - [Update Campaign](https://docs.medusajs.com/api/admin#campaigns_postcampaignsid) -- Cart - - [Create Cart](https://docs.medusajs.com/api/store#carts_postcarts) - - [Update Cart](https://docs.medusajs.com/api/store#carts_postcartsid) -- Collections - - [Create Collection](https://docs.medusajs.com/api/admin#collections_postcollections) - - [Update Collection](https://docs.medusajs.com/api/admin#collections_postcollectionsid) -- Customers - - [Create Customer](https://docs.medusajs.com/api/admin#customers_postcustomers) - - [Update Customer](https://docs.medusajs.com/api/admin#customers_postcustomersid) - - [Create Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddresses) - - [Update Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddressesaddress_id) -- Draft Orders - - [Create Draft Order](https://docs.medusajs.com/api/admin#draft-orders_postdraftorders) -- Orders - - [Complete Orders](https://docs.medusajs.com/api/admin#orders_postordersidcomplete) - - [Cancel Order's Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idcancel) - - [Create Shipment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idshipments) - - [Create Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillments) -- Products - - [Create Product](https://docs.medusajs.com/api/admin#products_postproducts) - - [Update Product](https://docs.medusajs.com/api/admin#products_postproductsid) - - [Create Product Variant](https://docs.medusajs.com/api/admin#products_postproductsidvariants) - - [Update Product Variant](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_id) - - [Create Product Option](https://docs.medusajs.com/api/admin#products_postproductsidoptions) - - [Update Product Option](https://docs.medusajs.com/api/admin#products_postproductsidoptionsoption_id) -- Product Tags - - [Create Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttags) - - [Update Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttagsid) -- Product Types - - [Create Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypes) - - [Update Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypesid) -- Promotions - - [Create Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotions) - - [Update Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotionsid) +So, it'll only execute 3 times, each every minute, then it won't be executed anymore. + +If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified. + + +# Create a Plugin + +In this chapter, you'll learn how to create a Medusa plugin and publish it. + +A [plugin](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md) is a package of reusable Medusa customizations that you can install in any Medusa application. By creating and publishing a plugin, you can reuse your Medusa customizations across multiple projects or share them with the community. + +Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). + +## 1. Create a Plugin Project + +Plugins are created in a separate Medusa project. This makes the development and publishing of the plugin easier. Later, you'll install that plugin in your Medusa application to test it out and use it. + +Medusa's `create-medusa-app` CLI tool provides the option to create a plugin project. Run the following command to create a new plugin project: + +```bash +npx create-medusa-app my-plugin --plugin +``` + +This will create a new Medusa plugin project in the `my-plugin` directory. + +### Plugin Directory Structure + +After the installation is done, the plugin structure will look like this: + +![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg) + +- `src/`: Contains the Medusa customizations. +- `src/admin`: Contains [admin extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md). +- `src/api`: Contains [API routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) and [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). You can add store, admin, or any custom API routes. +- `src/jobs`: Contains [scheduled jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md). +- `src/links`: Contains [module links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). +- `src/modules`: Contains [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). +- `src/provider`: Contains [module providers](#create-module-providers). +- `src/subscribers`: Contains [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). +- `src/workflows`: Contains [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). You can also add [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md) under `src/workflows/hooks`. +- `package.json`: Contains the plugin's package information, including general information and dependencies. +- `tsconfig.json`: Contains the TypeScript configuration for the plugin. *** -## How to Pass Additional Data +## 2. Prepare Plugin -### 1. Specify Validation of Additional Data +### Package Name -Before passing custom data in the `additional_data` object parameter, you must specify validation rules for the allowed properties in the object. +Before developing, testing, and publishing your plugin, make sure its name in `package.json` is correct. This is the name you'll use to install the plugin in your Medusa application. -To do that, use the middleware route object defined in `src/api/middlewares.ts`. +For example: -For example, create the file `src/api/middlewares.ts` with the following content: +```json title="package.json" +{ + "name": "@myorg/plugin-name", + // ... +} +``` -```ts title="src/api/middlewares.ts" -import { defineMiddlewares } from "@medusajs/framework/http" -import { z } from "zod" +### Package Keywords -export default defineMiddlewares({ - routes: [ +Medusa scrapes NPM for a list of plugins that integrate third-party services, to later showcase them on the Medusa website. If you want your plugin to appear in that listing, make sure to add the `medusa-v2` and `medusa-plugin-integration` keywords to the `keywords` field in `package.json`. + +Only plugins that integrate third-party services are listed in the Medusa integrations page. If your plugin doesn't integrate a third-party service, you can skip this step. + +```json title="package.json" +{ + "keywords": [ + "medusa-plugin-integration", + "medusa-v2" + ], + // ... +} +``` + +In addition, make sure to use one of the following keywords based on your integration type: + +|Keyword|Description|Example| +|---|---|---| +|\`medusa-plugin-analytics\`|Analytics service integration|Google Analytics| +|\`medusa-plugin-auth\`|Authentication service integration|Auth0| +|\`medusa-plugin-cms\`|CMS service integration|Contentful| +|\`medusa-plugin-notification\`|Notification service integration|Twilio SMS| +|\`medusa-plugin-payment\`|Payment service integration|PayPal| +|\`medusa-plugin-search\`|Search service integration|MeiliSearch| +|\`medusa-plugin-shipping\`|Shipping service integration|DHL| +|\`medusa-plugin-other\`|Other third-party integrations|Sentry| + +### Package Dependencies + +Your plugin project will already have the dependencies mentioned in this section. Unless you made changes to the dependencies, you can skip this section. + +In the `package.json` file you must have the Medusa dependencies as `devDependencies` and `peerDependencies`. In addition, you must have `@swc/core` as a `devDependency`, as it's used by the plugin CLI tools. + +For example, assuming `2.5.0` is the latest Medusa version: + +```json title="package.json" +{ + "devDependencies": { + "@medusajs/admin-sdk": "2.5.0", + "@medusajs/cli": "2.5.0", + "@medusajs/framework": "2.5.0", + "@medusajs/medusa": "2.5.0", + "@medusajs/test-utils": "2.5.0", + "@medusajs/ui": "4.0.4", + "@medusajs/icons": "2.5.0", + "@swc/core": "1.5.7", + }, + "peerDependencies": { + "@medusajs/admin-sdk": "2.5.0", + "@medusajs/cli": "2.5.0", + "@medusajs/framework": "2.5.0", + "@medusajs/test-utils": "2.5.0", + "@medusajs/medusa": "2.5.0", + "@medusajs/ui": "4.0.3", + "@medusajs/icons": "2.5.0", + } +} +``` + +### Package Exports + +Your plugin project will already have the exports mentioned in this section. Unless you made changes to the exports or you created your plugin before [Medusa v2.7.0](https://github.com/medusajs/medusa/releases/tag/v2.7.0), you can skip this section. + +In the `package.json` file, make sure your plugin has the following exports: + +```json title="package.json" +{ + "exports": { + "./package.json": "./package.json", + "./workflows": "./.medusa/server/src/workflows/index.js", + "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js", + "./providers/*": "./.medusa/server/src/providers/*/index.js", + "./admin": { + "import": "./.medusa/server/src/admin/index.mjs", + "require": "./.medusa/server/src/admin/index.js", + "default": "./.medusa/server/src/admin/index.js" + }, + "./*": "./.medusa/server/src/*.js" + } +} +``` + +Aside from the `./package.json`, `./providers`, and `./admin`, these exports are only a recommendation. You can cherry-pick the files and directories you want to export. + +The plugin exports the following files and directories: + +- `./package.json`: The `package.json` file. Medusa needs to access the `package.json` when registering the plugin. +- `./workflows`: The workflows exported in `./src/workflows/index.ts`. +- `./.medusa/server/src/modules/*`: The definition file of modules. This is useful if you create links to the plugin's modules in the Medusa application. +- `./providers/*`: The definition file of module providers. This is useful if your plugin includes a module provider, allowing you to register the plugin's providers in Medusa applications. Learn more in the [Create Module Providers](#create-module-providers) section. +- `./admin`: The admin extensions exported in `./src/admin/index.ts`. +- `./*`: Any other files in the plugin's `src` directory. + +*** + +## 3. Publish Plugin Locally for Development and Testing + +Medusa's CLI tool provides commands to simplify developing and testing your plugin in a local Medusa application. You start by publishing your plugin in the local package registry, then install it in your Medusa application. You can then watch for changes in the plugin as you develop it. + +### Publish and Install Local Package + +### Prerequisites + +- [Medusa application installed.](https://docs.medusajs.com/learn/installation/index.html.md) + +The first time you create your plugin, you need to publish the package into a local package registry, then install it in your Medusa application. This is a one-time only process. + +To publish the plugin to the local registry, run the following command in your plugin project: + +```bash title="Plugin project" +npx medusa plugin:publish +``` + +This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`. + +Next, navigate to your Medusa application: + +```bash title="Medusa application" +cd ~/path/to/medusa-app +``` + +Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application. + +Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency: + +```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green" +npm install --save-dev yalc +``` + +After that, run the following Medusa CLI command to install the plugin: + +```bash title="Medusa application" +npx medusa plugin:add @myorg/plugin-name +``` + +Make sure to replace `@myorg/plugin-name` with the name of your plugin as specified in `package.json`. Your plugin will be installed from the local package registry into your Medusa application. + +### Register Plugin in Medusa Application + +After installing the plugin, you need to register it in your Medusa application in the configurations defined in `medusa-config.ts`. + +Add the plugin to the `plugins` array in the `medusa-config.ts` file: + +```ts title="medusa-config.ts" highlights={pluginHighlights} +module.exports = defineConfig({ + // ... + plugins: [ { - method: "POST", - matcher: "/admin/products", - additionalDataValidator: { - brand: z.string().optional(), + resolve: "@myorg/plugin-name", + options: {}, + }, + ], +}) +``` + +The `plugins` configuration is an array of objects where each object has a `resolve` key whose value is the name of the plugin package. + +#### Pass Module Options through Plugin + +Each plugin configuration also accepts an `options` property, whose value is an object of options to pass to the plugin's modules. + +For example: + +```ts title="medusa-config.ts" highlights={pluginOptionsHighlight} +module.exports = defineConfig({ + // ... + plugins: [ + { + resolve: "@myorg/plugin-name", + options: { + apiKey: true, }, }, ], }) ``` -The middleware route object accepts an optional parameter `additionalDataValidator` whose value is an object of key-value pairs. The keys indicate the name of accepted properties in the `additional_data` parameter, and the value is [Zod](https://zod.dev/) validation rules of the property. +The `options` property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/options/index.html.md). -In this example, you indicate that the `additional_data` parameter accepts a `brand` property whose value is an optional string. +### Watch Plugin Changes During Development -Refer to [Zod's documentation](https://zod.dev) for all available validation rules. +While developing your plugin, you can watch for changes in the plugin and automatically update the plugin in the Medusa application using it. This is the only command you'll continuously need during your plugin development. -### 2. Pass the Additional Data in a Request +To do that, run the following command in your plugin project: -You can now pass a `brand` property in the `additional_data` parameter of a request to the Create Product API Route. - -For example: - -```bash -curl -X POST 'http://localhost:9000/admin/products' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {token}' \ ---data '{ - "title": "Product 1", - "options": [ - { - "title": "Default option", - "values": ["Default option value"] - } - ], - "shipping_profile_id": "{shipping_profile_id}", - "additional_data": { - "brand": "Acme" - } -}' +```bash title="Plugin project" +npx medusa plugin:develop ``` -Make sure to replace the `{token}` in the authorization header with an admin user's authentication token, and `{shipping_profile_id}` with an existing shipping profile's ID. +This command will: -In this request, you pass in the `additional_data` parameter a `brand` property and set its value to `Acme`. +- Watch for changes in the plugin. Whenever a file is changed, the plugin is automatically built. +- Publish the plugin changes to the local package registry. This will automatically update the plugin in the Medusa application using it. You can also benefit from real-time HMR updates of admin extensions. -The `additional_data` is then passed to hooks in the `createProductsWorkflow` used by the API route. +### Start Medusa Application + +You can start your Medusa application's development server to test out your plugin: + +```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green" +npm run dev +``` + +While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application. *** -## Use Additional Data in a Hook +## 4. Create Customizations in the Plugin -Learn about workflow hooks in [this guide](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md). +You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin. -Step functions consuming the workflow hook can access the `additional_data` in the first parameter. +- [Create a module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) +- [Create a module link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) +- [Create a workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) +- [Add a workflow hook](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md) +- [Create an API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) +- [Add a subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) +- [Add a scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md) +- [Add an admin widget](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md) +- [Add an admin UI route](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md) -For example, consider you want to store the data passed in `additional_data` in the product's `metadata` property. +While building those customizations, you can test them in your Medusa application by [watching the plugin changes](#watch-plugin-changes-during-development) and [starting the Medusa application](#start-medusa-application). -To do that, create the file `src/workflows/hooks/product-created.ts` with the following content: +### Generating Migrations for Modules -```ts title="src/workflows/hooks/product-created.ts" -import { StepResponse } from "@medusajs/framework/workflows-sdk" -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" -import { Modules } from "@medusajs/framework/utils" +During your development, you may need to generate migrations for modules in your plugin. To do that, first, add the following environment variables in your plugin project: -createProductsWorkflow.hooks.productsCreated( - async ({ products, additional_data }, { container }) => { - if (!additional_data?.brand) { - return - } - - const productModuleService = container.resolve( - Modules.PRODUCT - ) - - await productModuleService.upsertProducts( - products.map((product) => ({ - ...product, - metadata: { - ...product.metadata, - brand: additional_data.brand, - }, - })) - ) - - return new StepResponse(products, { - products, - additional_data, - }) - } -) +```plain title="Plugin project" +DB_USERNAME=postgres +DB_PASSWORD=123... +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=db_name ``` -This consumes the `productsCreated` hook, which runs after the products are created. +You can add these environment variables in a `.env` file in your plugin project. The variables are: -If `brand` is passed in `additional_data`, you resolve the Product Module's main service and use its `upsertProducts` method to update the products, adding the brand to the `metadata` property. +- `DB_USERNAME`: The username of the PostgreSQL user to connect to the database. +- `DB_PASSWORD`: The password of the PostgreSQL user to connect to the database. +- `DB_HOST`: The host of the PostgreSQL database. Typically, it's `localhost` if you're running the database locally. +- `DB_PORT`: The port of the PostgreSQL database. Typically, it's `5432` if you're running the database locally. +- `DB_NAME`: The name of the PostgreSQL database to connect to. -### Compensation Function +Then, run the following command in your plugin project to generate migrations for the modules in your plugin: -Hooks also accept a compensation function as a second parameter to undo the actions made by the step function. - -For example, pass the following second parameter to the `productsCreated` hook: - -```ts title="src/workflows/hooks/product-created.ts" -createProductsWorkflow.hooks.productsCreated( - async ({ products, additional_data }, { container }) => { - // ... - }, - async ({ products, additional_data }, { container }) => { - if (!additional_data.brand) { - return - } - - const productModuleService = container.resolve( - Modules.PRODUCT - ) - - await productModuleService.upsertProducts( - products - ) - } -) +```bash title="Plugin project" +npx medusa plugin:db:generate ``` -This updates the products to their original state before adding the brand to their `metadata` property. +This command generates migrations for all modules in the plugin. +Finally, run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command: -# Handling CORS in API Routes - -In this chapter, you’ll learn about the CORS middleware and how to configure it for custom API routes. - -## CORS Overview - -Cross-Origin Resource Sharing (CORS) allows only configured origins to access your API Routes. - -For example, if you allow only origins starting with `http://localhost:7001` to access your Admin API Routes, other origins accessing those routes get a CORS error. - -### CORS Configurations - -The `storeCors` and `adminCors` properties of Medusa's `http` configuration set the allowed origins for routes starting with `/store` and `/admin` respectively. - -These configurations accept a URL pattern to identify allowed origins. - -For example: - -```js title="medusa-config.ts" -module.exports = defineConfig({ - projectConfig: { - http: { - storeCors: "http://localhost:8000", - adminCors: "http://localhost:7001", - // ... - }, - }, -}) +```bash title="Medusa application" +npx medusa db:migrate ``` -This allows the `http://localhost:7001` origin to access the Admin API Routes, and the `http://localhost:8000` origin to access Store API Routes. +The migrations in your application, including your plugin, will run and update the database. -Learn more about the CORS configurations in [this resource guide](https://docs.medusajs.com/learn/configurations/medusa-config#http/index.html.md). +### Importing Module Resources -*** +In the [Prepare Plugin](#2-prepare-plugin) section, you learned about [exported resources](#package-exports) in your plugin. -## CORS in Store and Admin Routes +These exports allow you to import your plugin resources in your Medusa application, including workflows, links and modules. -To disable the CORS middleware for a route, export a `CORS` variable in the route file with its value set to `false`. +For example, to import the plugin's workflow in your Medusa application: -For example: - -```ts title="src/api/store/custom/route.ts" highlights={[["15"]]} -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export const GET = ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: "[GET] Hello world!", - }) -} - -export const CORS = false -``` - -This disables the CORS middleware on API Routes at the path `/store/custom`. - -*** - -## CORS in Custom Routes - -If you create a route that doesn’t start with `/store` or `/admin`, you must apply the CORS middleware manually. Otherwise, all requests to your API route lead to a CORS error. - -You can do that in the exported middlewares configurations in `src/api/middlewares.ts`. - -For example: - -```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-10" expandButtonLabel="Show Imports" -import { defineMiddlewares } from "@medusajs/framework/http" -import type { - MedusaNextFunction, - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { ConfigModule } from "@medusajs/framework/types" -import { parseCorsOrigins } from "@medusajs/framework/utils" -import cors from "cors" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom*", - middlewares: [ - ( - req: MedusaRequest, - res: MedusaResponse, - next: MedusaNextFunction - ) => { - const configModule: ConfigModule = - req.scope.resolve("configModule") - - return cors({ - origin: parseCorsOrigins( - configModule.projectConfig.http.storeCors - ), - credentials: true, - })(req, res, next) - }, - ], - }, - ], -}) -``` - -This retrieves the configurations exported from `medusa-config.ts` and applies the `storeCors` to routes starting with `/custom`. - - -# Throwing and Handling Errors - -In this guide, you'll learn how to throw errors in your Medusa application, how it affects an API route's response, and how to change the default error handler of your Medusa application. - -## Throw MedusaError - -When throwing an error in your API routes, middlewares, workflows, or any customization, throw a `MedusaError` from the Medusa Framework. - -The Medusa application's API route error handler then wraps your thrown error in a uniform object and returns it in the response. - -For example: +`@myorg/plugin-name` is the plugin package's name. ```ts -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { MedusaError } from "@medusajs/framework/utils" +import { Workflow1, Workflow2 } from "@myorg/plugin-name/workflows" +import BlogModule from "@myorg/plugin-name/modules/blog" +// import other files created in plugin like ./src/types/blog.ts +import BlogType from "@myorg/plugin-name/types/blog" +``` -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - if (!req.query.q) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "The `q` query parameter is required." - ) - } +### Create Module Providers +The [exported resources](#package-exports) also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or Infrastructure Module. + +For example, assuming your plugin has a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification/index.html.md) called `my-notification`, you can register it in your Medusa application's configuration like this: + +`@myorg/plugin-name` is the plugin package's name. + +```ts highlights={[["9"]]} title="medusa-config.ts" +module.exports = defineConfig({ // ... -} -``` - -The `MedusaError` class accepts in its constructor two parameters: - -1. The first is the error's type. `MedusaError` has a static property `Types` that you can use. `Types` is an enum whose possible values are explained in the next section. -2. The second is the message to show in the error response. - -### Error Object in Response - -The error object returned in the response has two properties: - -- `type`: The error's type. -- `message`: The error message, if available. -- `code`: A common snake-case code. Its values can be: - - `invalid_request_error` for the `DUPLICATE_ERROR` type. - - `api_error`: for the `DB_ERROR` type. - - `invalid_state_error` for `CONFLICT` error type. - - `unknown_error` for any unidentified error type. - - For other error types, this property won't be available unless you provide a code as a third parameter to the `MedusaError` constructor. - -### MedusaError Types - -|Type|Description|Status Code| -|---|---|---|---|---| -|\`DB\_ERROR\`|Indicates a database error.|\`500\`| -|\`DUPLICATE\_ERROR\`|Indicates a duplicate of a record already exists. For example, when trying to create a customer whose email is registered by another customer.|\`422\`| -|\`INVALID\_ARGUMENT\`|Indicates an error that occurred due to incorrect arguments or other unexpected state.|\`500\`| -|\`INVALID\_DATA\`|Indicates a validation error.|\`400\`| -|\`UNAUTHORIZED\`|Indicates that a user is not authorized to perform an action or access a route.|\`401\`| -|\`NOT\_FOUND\`|Indicates that the requested resource, such as a route or a record, isn't found.|\`404\`| -|\`NOT\_ALLOWED\`|Indicates that an operation isn't allowed.|\`400\`| -|\`CONFLICT\`|Indicates that a request conflicts with another previous or ongoing request. The error message in this case is ignored for a default message.|\`409\`| -|\`PAYMENT\_AUTHORIZATION\_ERROR\`|Indicates an error has occurred while authorizing a payment.|\`422\`| -|Other error types|Any other error type results in an |\`500\`| - -*** - -## Override Error Handler - -The `defineMiddlewares` function used to apply middlewares on routes accepts an `errorHandler` in its object parameter. Use it to override the default error handler for API routes. - -This error handler will also be used for errors thrown in Medusa's API routes and resources. - -For example, create `src/api/middlewares.ts` with the following: - -```ts title="src/api/middlewares.ts" collapsibleLines="1-8" expandMoreLabel="Show Imports" -import { - defineMiddlewares, - MedusaNextFunction, - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { MedusaError } from "@medusajs/framework/utils" - -export default defineMiddlewares({ - errorHandler: ( - error: MedusaError | any, - req: MedusaRequest, - res: MedusaResponse, - next: MedusaNextFunction - ) => { - res.status(400).json({ - error: "Something happened.", - }) - }, -}) -``` - -The `errorHandler` property's value is a function that accepts four parameters: - -1. The error thrown. Its type can be `MedusaError` or any other thrown error type. -2. A request object of type `MedusaRequest`. -3. A response object of type `MedusaResponse`. -4. A function of type MedusaNextFunction that executes the next middleware in the stack. - -This example overrides Medusa's default error handler with a handler that always returns a `400` status code with the same message. - - -# HTTP Methods - -In this chapter, you'll learn about how to add new API routes for each HTTP method. - -## HTTP Method Handler - -An API route is created for every HTTP method you export a handler function for in a route file. - -Allowed HTTP methods are: `GET`, `POST`, `DELETE`, `PUT`, `PATCH`, `OPTIONS`, and `HEAD`. - -For example, create the file `src/api/hello-world/route.ts` with the following content: - -```ts title="src/api/hello-world/route.ts" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: "[GET] Hello world!", - }) -} - -export const POST = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: "[POST] Hello world!", - }) -} -``` - -This adds two API Routes: - -- A `GET` route at `http://localhost:9000/hello-world`. -- A `POST` route at `http://localhost:9000/hello-world`. - - -# Middlewares - -In this chapter, you’ll learn about middlewares and how to create them. - -## What is a Middleware? - -A middleware is a function executed when a request is sent to an API Route. It's executed before the route handler function. - -Middlewares are used to guard API routes, parse request content types other than `application/json`, manipulate request data, and more. - -As Medusa's server is based on Express, you can use any [Express middleware](https://expressjs.com/en/resources/middleware.html). - -### Middleware Types - -There are two types of middlewares: - -1. Global Middleware: A middleware that applies to all routes matching a specified pattern. -2. Route Middleware: A middleware that applies to routes matching a specified pattern and HTTP method(s). - -These middlewares generally have the same definition and usage, but they differ in the routes they apply to. You'll learn how to create both types in the following sections. - -*** - -## How to Create a Global Middleware? - -Middlewares of all types are defined in the special file `src/api/middlewares.ts`. Use the `defineMiddlewares` function from the Medusa Framework to define the middlewares, and export its value. - -For example: - -```ts title="src/api/middlewares.ts" -import { - defineMiddlewares, - MedusaNextFunction, - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ + modules: [ { - matcher: "/custom*", - middlewares: [ - ( - req: MedusaRequest, - res: MedusaResponse, - next: MedusaNextFunction - ) => { - console.log("Received a request!") - - next() - }, - ], - }, - ], -}) -``` - -The `defineMiddlewares` function accepts a middleware configurations object that has the property `routes`. `routes`'s value is an array of middleware route objects, each having the following properties: - -- `matcher`: a string or regular expression indicating the API route path to apply the middleware on. The regular expression must be compatible with [path-to-regexp](https://github.com/pillarjs/path-to-regexp). -- `middlewares`: An array of global and route middleware functions. - -In the example above, you define a global middleware that logs the message `Received a request!` whenever a request is sent to an API route path starting with `/custom`. - -### Test the Global Middleware - -To test the middleware: - -1. Start the application: - -```bash npm2yarn -npm run dev -``` - -2. Send a request to any API route starting with `/custom`. -3. See the following message in the terminal: - -```bash -Received a request! -``` - -*** - -## How to Create a Route Middleware? - -In the previous section, you learned how to create a global middleware. You define the route middleware in the same way in `src/api/middlewares.ts`, but you specify an additional property `method` in the middleware route object. Its value is one or more HTTP methods to apply the middleware to. - -For example: - -```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports" -import { - MedusaNextFunction, - MedusaRequest, - MedusaResponse, - defineMiddlewares, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom*", - method: ["POST", "PUT"], - middlewares: [ - ( - req: MedusaRequest, - res: MedusaResponse, - next: MedusaNextFunction - ) => { - console.log("Received a request!") - - next() - }, - ], - }, - ], -}) -``` - -This example applies the middleware only when a `POST` or `PUT` request is sent to an API route path starting with `/custom`, changing the middleware from a global middleware to a route middleware. - -### Test the Route Middleware - -To test the middleware: - -1. Start the application: - -```bash npm2yarn -npm run dev -``` - -2. Send a `POST` request to any API route starting with `/custom`. -3. See the following message in the terminal: - -```bash -Received a request! -``` - -*** - -## When to Use Middlewares - -- You want to protect API routes by a custom condition. -- You're modifying the request body. - -*** - -## Middleware Function Parameters - -The middleware function accepts three parameters: - -1. A request object of type `MedusaRequest`. -2. A response object of type `MedusaResponse`. -3. A function of type `MedusaNextFunction` that executes the next middleware in the stack. - -You must call the `next` function in the middleware. Otherwise, other middlewares and the API route handler won’t execute. - -*** - -## Middleware for Routes with Path Parameters - -To indicate a path parameter in a middleware's `matcher` pattern, use the format `:{param-name}`. - -For example: - -```ts title="src/api/middlewares.ts" collapsibleLines="1-7" expandMoreLabel="Show Imports" highlights={pathParamHighlights} -import { - MedusaNextFunction, - MedusaRequest, - MedusaResponse, - defineMiddlewares, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom/:id", - middlewares: [ - // ... - ], - }, - ], -}) -``` - -This applies a middleware to the routes defined in the file `src/api/custom/[id]/route.ts`. - -*** - -## Request URLs with Trailing Backslashes - -A middleware whose `matcher` pattern doesn't end with a backslash won't be applied for requests to URLs with a trailing backslash. - -For example, consider you have the following middleware: - -```ts title="src/api/middlewares.ts" collapsibleLines="1-7" expandMoreLabel="Show Imports" -import { - MedusaNextFunction, - MedusaRequest, - MedusaResponse, - defineMiddlewares, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom", - middlewares: [ - ( - req: MedusaRequest, - res: MedusaResponse, - next: MedusaNextFunction - ) => { - console.log("Received a request!") - - next() - }, - ], - }, - ], -}) -``` - -If you send a request to `http://localhost:9000/custom`, the middleware will run. - -However, if you send a request to `http://localhost:9000/custom/`, the middleware won't run. - -In general, avoid adding trailing backslashes when sending requests to API routes. - -*** - -## Middlewares and Route Ordering - -The ordering explained in this section was added in [Medusa v2.6](https://github.com/medusajs/medusa/releases/tag/v2.6) - -The Medusa application registers middlewares and API route handlers in the following order: - -1. Global middlewares in the following order: - 1. Global middleware defined in the Medusa's core. - 2. Global middleware defined in the plugins (in the order the plugins are registered in). - 3. Global middleware you define in the application. -2. Route middlewares in the following order: - 1. Route middleware defined in the Medusa's core. - 2. Route middleware defined in the plugins (in the order the plugins are registered in). - 3. Route middleware you define in the application. -3. API routes in the following order: - 1. API routes defined in the Medusa's core. - 2. API routes defined in the plugins (in the order the plugins are registered in). - 3. API routes you define in the application. - -### Middlewares Sorting - -On top of the previous ordering, Medusa sorts global and route middlewares based on their matcher pattern in the following order: - -1. Wildcard matchers. For example, `/custom*`. -2. Regex matchers. For example, `/custom/(products|collections)`. -3. Static matchers without parameters. For example, `/custom`. -4. Static matchers with parameters. For example, `/custom/:id`. - -For example, if you have the following middlewares: - -```ts title="src/api/middlewares.ts" -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom/:id", - middlewares: [/* ... */], - }, - { - matcher: "/custom", - middlewares: [/* ... */], - }, - { - matcher: "/custom*", - method: ["GET"], - middlewares: [/* ... */], - }, - { - matcher: "/custom/:id", - method: ["GET"], - middlewares: [/* ... */], - }, - ], -}) -``` - -The global middlewares are sorted into the following order before they're registered: - -1. Global middleware `/custom`. -2. Global middleware `/custom/:id`. - -And the route middlewares are sorted into the following order before they're registered: - -1. Route middleware `/custom*`. -2. Route middleware `/custom/:id`. - -Then, the middlwares are registered in the order mentioned earlier, with global middlewares first, then the route middlewares. - -### Middlewares and Route Execution Order - -When a request is sent to an API route, the global middlewares are executed first, then the route middlewares, and finally the route handler. - -For example, consider you have the following middlewares: - -```ts title="src/api/middlewares.ts" -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom", - middlewares: [ - (req, res, next) => { - console.log("Global middleware") - next() - }, - ], - }, - { - matcher: "/custom", - method: ["GET"], - middlewares: [ - (req, res, next) => { - console.log("Route middleware") - next() - }, - ], - }, - ], -}) -``` - -When you send a request to `/custom` route, the following messages are logged in the terminal: - -```bash -Global middleware -Route middleware -Hello from custom! # message logged from API route handler -``` - -The global middleware runs first, then the route middleware, and finally the route handler, assuming that it logs the message `Hello from custom!`. - -*** - -## Overriding Middlewares - -A middleware can not override an existing middleware. Instead, middlewares are added to the end of the middleware stack. - -For example, if you define a custom validation middleware, such as `validateAndTransformBody`, on an existing route, then both the original and the custom validation middleware will run. - - -# API Route Parameters - -In this chapter, you’ll learn about path, query, and request body parameters. - -## Path Parameters - -To create an API route that accepts a path parameter, create a directory within the route file's path whose name is of the format `[param]`. - -For example, to create an API Route at the path `/hello-world/:id`, where `:id` is a path parameter, create the file `src/api/hello-world/[id]/route.ts` with the following content: - -```ts title="src/api/hello-world/[id]/route.ts" highlights={singlePathHighlights} -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: `[GET] Hello ${req.params.id}!`, - }) -} -``` - -The `MedusaRequest` object has a `params` property. `params` holds the path parameters in key-value pairs. - -### Multiple Path Parameters - -To create an API route that accepts multiple path parameters, create within the file's path multiple directories whose names are of the format `[param]`. - -For example, to create an API route at `/hello-world/:id/name/:name`, create the file `src/api/hello-world/[id]/name/[name]/route.ts` with the following content: - -```ts title="src/api/hello-world/[id]/name/[name]/route.ts" highlights={multiplePathHighlights} -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: `[GET] Hello ${ - req.params.id - } - ${req.params.name}!`, - }) -} -``` - -You access the `id` and `name` path parameters using the `req.params` property. - -*** - -## Query Parameters - -You can access all query parameters in the `query` property of the `MedusaRequest` object. `query` is an object of key-value pairs, where the key is a query parameter's name, and the value is its value. - -For example: - -```ts title="src/api/hello-world/route.ts" highlights={queryHighlights} -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: `Hello ${req.query.name}`, - }) -} -``` - -The value of `req.query.name` is the value passed in `?name=John`, for example. - -### Validate Query Parameters - -You can apply validation rules on received query parameters to ensure they match specified rules and types. - -Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-query-paramters/index.html.md). - -*** - -## Request Body Parameters - -The Medusa application parses the body of any request having a JSON, URL-encoded, or text request content types. The request body parameters are set in the `MedusaRequest`'s `body` property. - -Learn more about configuring body parsing in [this guide](https://docs.medusajs.com/learn/fundamentals/api-routes/parse-body/index.html.md). - -For example: - -```ts title="src/api/hello-world/route.ts" highlights={bodyHighlights} -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -type HelloWorldReq = { - name: string -} - -export const POST = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: `[POST] Hello ${req.body.name}!`, - }) -} -``` - -In this example, you use the `name` request body parameter to create the message in the returned response. - -The `MedusaRequest` type accepts a type argument that indicates the type of the request body. This is useful for auto-completion and to avoid typing errors. - -To test it out, send the following request to your Medusa application: - -```bash -curl -X POST 'http://localhost:9000/hello-world' \ --H 'Content-Type: application/json' \ ---data-raw '{ - "name": "John" -}' -``` - -This returns the following JSON object: - -```json -{ - "message": "[POST] Hello John!" -} -``` - -### Validate Body Parameters - -You can apply validation rules on received body parameters to ensure they match specified rules and types. - -Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-body/index.html.md). - - -# Configure Request Body Parser - -In this chapter, you'll learn how to configure the request body parser for your API routes. - -## Default Body Parser Configuration - -The Medusa application configures the body parser by default to parse JSON, URL-encoded, and text request content types. You can parse other data types by adding the relevant [Express middleware](https://expressjs.com/en/guide/using-middleware.html) or preserve the raw body data by configuring the body parser, which is useful for webhook requests. - -This chapter shares some examples of configuring the body parser for different data types or use cases. - -*** - -## Preserve Raw Body Data for Webhooks - -If your API route receives webhook requests, you might want to preserve the raw body data. To do this, you can configure the body parser to parse the raw body data and store it in the `req.rawBody` property. - -To do that, create the file `src/api/middlewares.ts` with the following content: - -```ts title="src/api/middlewares.ts" highlights={preserveHighlights} -import { defineMiddlewares } from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - method: ["POST"], - bodyParser: { preserveRawBody: true }, - matcher: "/custom", - }, - ], -}) -``` - -The middleware route object passed to `routes` accepts a `bodyParser` property whose value is an object of configuration for the default body parser. By enabling the `preserveRawBody` property, the raw body data is preserved and stored in the `req.rawBody` property. - -Learn more about [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). - -You can then access the raw body data in your API route handler: - -```ts title="src/api/custom/route.ts" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - console.log(req.rawBody) - - // TODO use raw body -} -``` - -*** - -## Configure Request Body Size Limit - -By default, the body parser limits the request body size to `100kb`. If a request body exceeds that size, the Medusa application throws an error. - -You can configure the body parser to accept larger request bodies by setting the `sizeLimit` property of the `bodyParser` object in a middleware route object. For example: - -```ts title="src/api/middlewares.ts" highlights={sizeLimitHighlights} -import { defineMiddlewares } from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - method: ["POST"], - bodyParser: { sizeLimit: "2mb" }, - matcher: "/custom", - }, - ], -}) -``` - -The `sizeLimit` property accepts one of the following types of values: - -- A string representing the size limit in bytes (For example, `100kb`, `2mb`, `5gb`). It is passed to the [bytes](https://www.npmjs.com/package/bytes) library to parse the size. -- A number representing the size limit in bytes. For example, `1024` for 1kb. - -*** - -## Configure File Uploads - -To accept file uploads in your API routes, you can configure the [Express Multer middleware](https://expressjs.com/en/resources/middleware/multer.html) on your route. - -The `multer` package is available through the `@medusajs/medusa` package, so you don't need to install it. However, for better typing support, install the `@types/multer` package as a development dependency: - -```bash npm2yarn -npm install --save-dev @types/multer -``` - -Then, to configure file upload for your route, create the file `src/api/middlewares.ts` with the following content: - -```ts title="src/api/middlewares.ts" highlights={uploadHighlights} -import { defineMiddlewares } from "@medusajs/framework/http" -import multer from "multer" - -const upload = multer({ storage: multer.memoryStorage() }) - -export default defineMiddlewares({ - routes: [ - { - method: ["POST"], - matcher: "/custom", - middlewares: [ - // @ts-ignore - upload.array("files"), - ], - }, - ], -}) -``` - -In the example above, you configure the `multer` middleware to store the uploaded files in memory. Then, you apply the `upload.array("files")` middleware to the route to accept file uploads. By using the `array` method, you accept multiple file uploads with the same `files` field name. - -You can then access the uploaded files in your API route handler: - -```ts title="src/api/custom/route.ts" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - const files = req.files as Express.Multer.File[] - - // TODO handle files -} -``` - -The uploaded files are stored in the `req.files` property as an array of Multer file objects that have properties like `filename` and `mimetype`. - -### Uploading Files using File Module Provider - -The recommended way to upload the files to storage using the configured [File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file/index.html.md) is to use the [uploadFilesWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md): - -```ts title="src/api/custom/route.ts" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { MedusaError } from "@medusajs/framework/utils" -import { uploadFilesWorkflow } from "@medusajs/medusa/core-flows" - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - const files = req.files as Express.Multer.File[] - - if (!files?.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "No files were uploaded" - ) - } - - const { result } = await uploadFilesWorkflow(req.scope).run({ - input: { - files: files?.map((f) => ({ - filename: f.originalname, - mimeType: f.mimetype, - content: f.buffer.toString("binary"), - access: "public", - })), - }, - }) - - res.status(200).json({ files: result }) -} -``` - -Check out the [uploadFilesWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md) for details on the expected input and output of the workflow. - - -# Protected Routes - -In this chapter, you’ll learn how to create protected routes. - -## What is a Protected Route? - -A protected route is a route that requires requests to be user-authenticated before performing the route's functionality. Otherwise, the request fails, and the user is prevented access. - -*** - -## Default Protected Routes - -Medusa applies an authentication guard on routes starting with `/admin`, including custom API routes. - -Requests to `/admin` must be user-authenticated to access the route. - -Refer to the API Reference for [Admin](https://docs.medusajs.com/api/admin#authentication) and [Store](https://docs.medusajs.com/api/store#authentication) authentication methods. - -*** - -## Protect Custom API Routes - -To protect custom API Routes to only allow authenticated customer or admin users, use the `authenticate` middleware from the Medusa Framework. - -For example: - -```ts title="src/api/middlewares.ts" highlights={highlights} -import { - defineMiddlewares, - authenticate, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom/admin*", - middlewares: [authenticate("user", ["session", "bearer", "api-key"])], - }, - { - matcher: "/custom/customer*", - middlewares: [authenticate("customer", ["session", "bearer"])], - }, - ], -}) -``` - -The `authenticate` middleware function accepts three parameters: - -1. The type of user authenticating. Use `user` for authenticating admin users, and `customer` for authenticating customers. You can also pass `*` to allow all types of users, or pass an array of actor types. -2. An array of types of authentication methods allowed. Both `user` and `customer` scopes support `session` and `bearer`. The `admin` scope also supports the `api-key` authentication method. -3. An optional object of configurations accepting the following properties: - - `allowUnauthenticated`: (default: `false`) A boolean indicating whether authentication is required. For example, you may have an API route where you want to access the logged-in customer if available, but guest customers can still access it too. - - `allowUnregistered` (default: `false`): A boolean indicating if unregistered users should be allowed access. This is useful when you want to allow users who aren’t registered to access certain routes. - -### Example: Custom Actor Type - -For example, to require authentication of a custom actor type `manager` to an API route: - -```ts title="src/api/middlewares.ts" -import { - defineMiddlewares, - authenticate, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/manager*", - middlewares: [authenticate("manager", ["session", "bearer"])], - }, - ], -}) -``` - -Refer to the [Custom Actor-Type Guide](https://docs.medusajs.com/resources/commerce-modules/auth/create-actor-type/index.html.md) for detailed explanation on how to create a custom actor type and apply authentication middlewares. - -### Example: Allow Multiple Actor Types - -To allow multiple actor types to access an API route, pass an array of actor types to the `authenticate` middleware: - -```ts title="src/api/middlewares.ts" -import { - defineMiddlewares, - authenticate, -} from "@medusajs/framework/http" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom*", - middlewares: [authenticate(["user", "customer"], ["session", "bearer"])], - }, - ], -}) -``` - -*** - -## Authentication Opt-Out - -To disable the authentication guard on custom routes under the `/admin` path prefix, export an `AUTHENTICATE` variable in the route file with its value set to `false`. - -For example: - -```ts title="src/api/admin/custom/route.ts" highlights={[["15"]]} -import type { - AuthenticatedMedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" - -export const GET = async ( - req: AuthenticatedMedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: "Hello", - }) -} - -export const AUTHENTICATE = false -``` - -Now, any request sent to the `/admin/custom` API route is allowed, regardless if the admin user is authenticated. - -*** - -## Authenticated Request Type - -To access the authentication details in an API route, such as the logged-in user's ID, set the type of the first request parameter to `AuthenticatedMedusaRequest`. It extends `MedusaRequest`. - -The `auth_context.actor_id` property of `AuthenticatedMedusaRequest` holds the ID of the authenticated user or customer. If there isn't any authenticated user or customer, `auth_context` is `undefined`. - -If you opt-out of authentication in a route as mentioned in the [previous section](#authentication-opt-out), you can't access the authenticated user or customer anymore. Use the [authenticate middleware](#protect-custom-api-routes) instead. - -### Retrieve Logged-In Customer's Details - -You can access the logged-in customer’s ID in all API routes starting with `/store` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object. - -For example: - -```ts title="src/api/store/custom/route.ts" highlights={[["19", "req.auth_context.actor_id", "Access the logged-in customer's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" -import type { - AuthenticatedMedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { Modules } from "@medusajs/framework/utils" -import { ICustomerModuleService } from "@medusajs/framework/types" - -export const GET = async ( - req: AuthenticatedMedusaRequest, - res: MedusaResponse -) => { - if (req.auth_context?.actor_id) { - // retrieve customer - const customerModuleService: ICustomerModuleService = req.scope.resolve( - Modules.CUSTOMER - ) - - const customer = await customerModuleService.retrieveCustomer( - req.auth_context.actor_id - ) - } - - // ... -} -``` - -In this example, you resolve the Customer Module's main service, then use it to retrieve the logged-in customer, if available. - -### Retrieve Logged-In Admin User's Details - -You can access the logged-in admin user’s ID in all API Routes starting with `/admin` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object. - -For example: - -```ts title="src/api/admin/custom/route.ts" highlights={[["17", "req.auth_context.actor_id", "Access the logged-in admin user's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" -import type { - AuthenticatedMedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { Modules } from "@medusajs/framework/utils" -import { IUserModuleService } from "@medusajs/framework/types" - -export const GET = async ( - req: AuthenticatedMedusaRequest, - res: MedusaResponse -) => { - const userModuleService: IUserModuleService = req.scope.resolve( - Modules.USER - ) - - const user = await userModuleService.retrieveUser( - req.auth_context.actor_id - ) - - // ... -} -``` - -In the route handler, you resolve the User Module's main service, then use it to retrieve the logged-in admin user. - - -# API Route Response - -In this chapter, you'll learn how to send a response in your API route. - -## Send a JSON Response - -To send a JSON response, use the `json` method of the `MedusaResponse` object passed as the second parameter of your API route handler. - -For example: - -```ts title="src/api/custom/route.ts" highlights={jsonHighlights} -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - message: "Hello, World!", - }) -} -``` - -This API route returns the following JSON object: - -```json -{ - "message": "Hello, World!" -} -``` - -*** - -## Set Response Status Code - -By default, setting the JSON data using the `json` method returns a response with a `200` status code. - -To change the status code, use the `status` method of the `MedusaResponse` object. - -For example: - -```ts title="src/api/custom/route.ts" highlights={statusHighlight} -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.status(201).json({ - message: "Hello, World!", - }) -} -``` - -The response of this API route has the status code `201`. - -*** - -## Change Response Content Type - -To return response data other than a JSON object, use the `writeHead` method of the `MedusaResponse` object. It allows you to set the response headers, including the content type. - -For example, to create an API route that returns an event stream: - -```ts highlights={streamHighlights} -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.writeHead(200, { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - }) - - const interval = setInterval(() => { - res.write("Streaming data...\n") - }, 3000) - - req.on("end", () => { - clearInterval(interval) - res.end() - }) -} -``` - -The `writeHead` method accepts two parameters: - -1. The first one is the response's status code. -2. The second is an object of key-value pairs to set the headers of the response. - -This API route opens a stream by setting the `Content-Type` in the header to `text/event-stream`. It then simulates a stream by creating an interval that writes the stream data every three seconds. - -*** - -## Do More with Responses - -The `MedusaResponse` type is based on [Express's Response](https://expressjs.com/en/api.html#res). Refer to their API reference for other uses of responses. - - -# Retrieve Custom Links from Medusa's API Route - -In this chapter, you'll learn how to retrieve custom data models linked to existing Medusa data models from Medusa's API routes. - -## Why Retrieve Custom Linked Data Models? - -Often, you'll link custom data models to existing Medusa data models to implement custom features or expand on existing ones. - -For example, to add brands for products, you can create a `Brand` data model in a Brand Module, then [define a link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) to the [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md)'s `Product` data model. - -When you implement this customization, you might need to retrieve the brand of a product using the existing [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid). You can do this by passing the linked data model's name in the `fields` query parameter of the API route. - -*** - -## How to Retrieve Custom Linked Data Models Using `fields`? - -Most of Medusa's API routes accept a `fields` query parameter that allows you to specify the fields and relations to retrieve in the resource, such as a product. - -For example, to retrieve the brand of a product, you can pass the `brand` field in the `fields` query parameter of the [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid): - -```bash -curl 'http://localhost:9000/admin/products/{id}?fields=*brand' \ --H 'Authorization: Bearer {access_token}' -``` - -The `fields` query parameter accepts a comma-separated list of fields and relations to retrieve. To learn more about using the `fields` query parameter, refer to the [API Reference](https://docs.medusajs.com/api/store#select-fields-and-relations). - -By prefixing `brand` with an asterisk (`*`), you retrieve all the default fields of the product, including the `brand` field. If you don't include the `*` prefix, the response will only include the product's brand. - -*** - -## API Routes that Restrict Retrievable Fields - -Some of Medusa's API routes restrict the fields and relations you can retrieve, which means you can't pass your custom linked data models in the `fields` query parameter. Medusa makes this restriction to ensure the API routes are performant and secure. - -The API routes that restrict the fields and relations you can retrieve are: - -- [Customer Store API Routes](https://docs.medusajs.com/api/store#customers) -- [Customer Admin API Routes](https://docs.medusajs.com/api/admin#customers) -- [Product Category Admin API Routes](https://docs.medusajs.com/api/admin#product-categories) - -### How to Override Allowed Fields and Relations - -For these routes, you need to override the allowed fields and relations to be retrieved. You can do this by adding a [middleware](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md) to those routes. - -For example, to allow retrieving the `b2b_company` of a customer using the [Get Customer Admin API Route](https://docs.medusajs.com/api/admin#customers_getcustomersid), create the file `src/api/middlewares.ts` with the following content: - -Learn how to create a middleware in the [Middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md) chapter. - -```ts title="src/api/middlewares.ts" highlights={highlights} -import { defineMiddlewares } from "@medusajs/medusa" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/store/customers/me", - method: "GET", - middlewares: [ - (req, res, next) => { - req.allowed?.push("b2b_company") - next() - }, - ], - }, - ], -}) -``` - -In this example, you apply a middleware to the [Get Customer Admin API Route](https://docs.medusajs.com/api/admin#customers_getcustomersid). - -The request object passed to middlewares has an `allowed` property that contains the fields and relations that can be retrieved. So, you modify the `allowed` array to include the `b2b_company` field. - -You can now retrieve the `b2b_company` field using the `fields` query parameter of the [Get Customer Admin API Route](https://docs.medusajs.com/api/admin#customers_getcustomersid): - -```bash -curl 'http://localhost:9000/admin/customers/{id}?fields=*b2b_company' \ --H 'Authorization: Bearer {access_token}' -``` - -In this example, you retrieve the `b2b_company` relation of the customer using the `fields` query parameter. - - -# Request Body and Query Parameter Validation - -In this chapter, you'll learn how to validate request body and query parameters in your custom API route. - -## Request Validation - -Consider you're creating a `POST` API route at `/custom`. It accepts two parameters `a` and `b` that are required numbers, and returns their sum. - -Medusa provides two middlewares to validate the request body and query paramters of incoming requests to your custom API routes: - -- `validateAndTransformBody` to validate the request's body parameters against a schema. -- `validateAndTransformQuery` to validate the request's query parameters against a schema. - -Both middlewares accept a [Zod](https://zod.dev/) schema as a parameter, which gives you flexibility in how you define your validation schema with complex rules. - -The next steps explain how to add request body and query parameter validation to the API route mentioned earlier. - -*** - -## How to Validate Request Body - -### Step 1: Create Validation Schema - -Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters. - -To create a validation schema with Zod, create a `validators.ts` file in any `src/api` subfolder. This file holds Zod schemas for each of your API routes. - -For example, create the file `src/api/custom/validators.ts` with the following content: - -```ts title="src/api/custom/validators.ts" -import { z } from "zod" - -export const PostStoreCustomSchema = z.object({ - a: z.number(), - b: z.number(), -}) -``` - -The `PostStoreCustomSchema` variable is a Zod schema that indicates the request body is valid if: - -1. It's an object. -2. It has a property `a` that is a required number. -3. It has a property `b` that is a required number. - -### Step 2: Add Request Body Validation Middleware - -To use this schema for validating the body parameters of requests to `/custom`, use the `validateAndTransformBody` middleware provided by `@medusajs/framework/http`. It accepts the Zod schema as a parameter. - -For example, create the file `src/api/middlewares.ts` with the following content: - -```ts title="src/api/middlewares.ts" -import { - defineMiddlewares, - validateAndTransformBody, -} from "@medusajs/framework/http" -import { PostStoreCustomSchema } from "./custom/validators" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom", - method: "POST", - middlewares: [ - validateAndTransformBody(PostStoreCustomSchema), - ], - }, - ], -}) -``` - -This applies the `validateAndTransformBody` middleware on `POST` requests to `/custom`. It uses the `PostStoreCustomSchema` as the validation schema. - -#### How the Validation Works - -If a request's body parameters don't pass the validation, the `validateAndTransformBody` middleware throws an error indicating the validation errors. - -If a request's body parameters are validated successfully, the middleware sets the validated body parameters in the `validatedBody` property of `MedusaRequest`. - -### Step 3: Use Validated Body in API Route - -In your API route, consume the validated body using the `validatedBody` property of `MedusaRequest`. - -For example, create the file `src/api/custom/route.ts` with the following content: - -```ts title="src/api/custom/route.ts" highlights={routeHighlights} -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { z } from "zod" -import { PostStoreCustomSchema } from "./validators" - -type PostStoreCustomSchemaType = z.infer< - typeof PostStoreCustomSchema -> - -export const POST = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - res.json({ - sum: req.validatedBody.a + req.validatedBody.b, - }) -} -``` - -In the API route, you use the `validatedBody` property of `MedusaRequest` to access the values of the `a` and `b` properties. - -To pass the request body's type as a type parameter to `MedusaRequest`, use Zod's `infer` type that accepts the type of a schema as a parameter. - -### Test it Out - -To test out the validation, send a `POST` request to `/custom` passing `a` and `b` body parameters. You can try sending incorrect request body parameters to test out the validation. - -For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data: - -```json -{ - "type": "invalid_data", - "message": "Invalid request: Field 'a' is required" -} -``` - -*** - -## How to Validate Request Query Parameters - -The steps to validate the request query parameters are the similar to that of [validating the body](#how-to-validate-request-body). - -### Step 1: Create Validation Schema - -The first step is to create a schema with Zod with the rules of the accepted query parameters. - -Consider that the API route accepts two query parameters `a` and `b` that are numbers, similar to the previous section. - -Create the file `src/api/custom/validators.ts` with the following content: - -```ts title="src/api/custom/validators.ts" -import { z } from "zod" - -export const PostStoreCustomSchema = z.object({ - a: z.preprocess( - (val) => { - if (val && typeof val === "string") { - return parseInt(val) - } - return val + resolve: "@medusajs/medusa/notification", + options: { + providers: [ + { + resolve: "@myorg/plugin-name/providers/my-notification", + id: "my-notification", + options: { + channels: ["email"], + // provider options... + }, + }, + ], }, - z - .number() - ), - b: z.preprocess( - (val) => { - if (val && typeof val === "string") { - return parseInt(val) - } - return val - }, - z - .number() - ), -}) -``` - -Since a query parameter's type is originally a string or array of strings, you have to use Zod's `preprocess` method to validate other query types, such as numbers. - -For both `a` and `b`, you transform the query parameter's value to an integer first if it's a string, then, you check that the resulting value is a number. - -### Step 2: Add Request Query Validation Middleware - -Next, you'll use the schema to validate incoming requests' query parameters to the `/custom` API route. - -Add the `validateAndTransformQuery` middleware to the API route in the file `src/api/middlewares.ts`: - -```ts title="src/api/middlewares.ts" -import { - validateAndTransformQuery, - defineMiddlewares, -} from "@medusajs/framework/http" -import { PostStoreCustomSchema } from "./custom/validators" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/custom", - method: "POST", - middlewares: [ - validateAndTransformQuery( - PostStoreCustomSchema, - {} - ), - ], }, ], }) ``` -The `validateAndTransformQuery` accepts two parameters: +You pass to `resolve` the path to the provider relative to the plugin package. So, in this example, the `my-notification` provider is located in `./src/providers/my-notification/index.ts` of the plugin. -- The first one is the Zod schema to validate the query parameters against. -- The second one is an object of options for retrieving data using Query, which you can learn more about in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). +To learn how to create module providers, refer to the following guides: -#### How the Validation Works - -If a request's query parameters don't pass the validation, the `validateAndTransformQuery` middleware throws an error indicating the validation errors. - -If a request's query parameters are validated successfully, the middleware sets the validated query parameters in the `validatedQuery` property of `MedusaRequest`. - -### Step 3: Use Validated Query in API Route - -Finally, use the validated query in the API route. The `MedusaRequest` parameter has a `validatedQuery` parameter that you can use to access the validated parameters. - -For example, create the file `src/api/custom/route.ts` with the following content: - -```ts title="src/api/custom/route.ts" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" - -export const GET = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - const a = req.validatedQuery.a as number - const b = req.validatedQuery.b as number - - res.json({ - sum: a + b, - }) -} -``` - -In the API route, you use the `validatedQuery` property of `MedusaRequest` to access the values of the `a` and `b` properties as numbers, then return in the response their sum. - -### Test it Out - -To test out the validation, send a `POST` request to `/custom` with `a` and `b` query parameters. You can try sending incorrect query parameters to see how the validation works. - -For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data: - -```json -{ - "type": "invalid_data", - "message": "Invalid request: Field 'a' is required" -} -``` +- [File Module Provider](https://docs.medusajs.com/resources/references/file-provider-module/index.html.md) +- [Notification Module Provider](https://docs.medusajs.com/resources/references/notification-provider-module/index.html.md) +- [Auth Module Provider](https://docs.medusajs.com/resources/references/auth/provider/index.html.md) +- [Payment Module Provider](https://docs.medusajs.com/resources/references/payment/provider/index.html.md) +- [Fulfillment Module Provider](https://docs.medusajs.com/resources/references/fulfillment/provider/index.html.md) +- [Tax Module Provider](https://docs.medusajs.com/resources/references/tax/provider/index.html.md) *** -## Learn More About Validation Schemas +## 5. Publish Plugin to NPM -To see different examples and learn more about creating a validation schema, refer to [Zod's documentation](https://zod.dev). +Make sure to add the keywords mentioned in the [Package Keywords](#package-keywords) section in your plugin's `package.json` file. + +Medusa's CLI tool provides a command that bundles your plugin to be published to npm. Once you're ready to publish your plugin publicly, run the following command in your plugin project: + +```bash +npx medusa plugin:build +``` + +The command will compile an output in the `.medusa/server` directory. + +You can now publish the plugin to npm using the [NPM CLI tool](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Run the following command to publish the plugin to npm: + +```bash +npm publish +``` + +If you haven't logged in before with your NPM account, you'll be asked to log in first. Then, your package is published publicly to be used in any Medusa application. + +### Install Public Plugin in Medusa Application + +You install a plugin that's published publicly using your package manager. For example: + +```bash npm2yarn +npm install @myorg/plugin-name +``` + +Where `@myorg/plugin-name` is the name of your plugin as published on NPM. + +Then, register the plugin in your Medusa application's configurations as explained in [this section](#register-plugin-in-medusa-application). + +*** + +## Update a Published Plugin + +To update the Medusa dependencies in a plugin, refer to [this documentation](https://docs.medusajs.com/learn/update#update-plugin-project/index.html.md). + +If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again. + +First, run the following command to change the version of the plugin: + +```bash +npm version +``` + +Where `` indicates the type of version update you’re publishing. For example, it can be `major` or `minor`. Refer to the [npm version documentation](https://docs.npmjs.com/cli/v10/commands/npm-version) for more information. + +Then, re-run the same commands for publishing a plugin: + +```bash +npx medusa plugin:build +npm publish +``` + +This will publish an updated version of your plugin under a new version. + + +# Module Isolation + +In this chapter, you'll learn how modules are isolated, and what that means for your custom development. + +- Modules can't access resources, such as services or data models, from other modules. +- Use Medusa's linking concepts, as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), to extend a module's data models and retrieve data across modules. + +## How are Modules Isolated? + +A module is unaware of any resources other than its own, such as services or data models. This means it can't access these resources if they're implemented in another module. + +For example, your custom module can't resolve the Product Module's main service or have direct relationships from its data model to the Product Module's data models. + +*** + +## Why are Modules Isolated + +Some of the module isolation's benefits include: + +- Integrate your module into any Medusa application without side-effects to your setup. +- Replace existing modules with your custom implementation, if your use case is drastically different. +- Use modules in other environments, such as Edge functions and Next.js apps. + +*** + +## How to Extend Data Model of Another Module? + +To extend the data model of another module, such as the `product` data model of the Product Module, use Medusa's linking concepts as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). + +*** + +## How to Use Services of Other Modules? + +If you're building a feature that uses functionalities from different modules, use a workflow whose steps resolve the modules' services to perform these functionalities. + +Workflows ensure data consistency through their roll-back mechanism and tracking of each execution's status, steps, input, and output. + +### Example + +For example, consider you have two modules: + +1. A module that stores and manages brands in your application. +2. A module that integrates a third-party Content Management System (CMS). + +To sync brands from your application to the third-party system, create the following steps: + +```ts title="Example Steps" highlights={stepsHighlights} +const retrieveBrandsStep = createStep( + "retrieve-brands", + async (_, { container }) => { + const brandModuleService = container.resolve( + "brandModuleService" + ) + + const brands = await brandModuleService.listBrands() + + return new StepResponse(brands) + } +) + +const createBrandsInCmsStep = createStep( + "create-brands-in-cms", + async ({ brands }, { container }) => { + const cmsModuleService = container.resolve( + "cmsModuleService" + ) + + const cmsBrands = await cmsModuleService.createBrands(brands) + + return new StepResponse(cmsBrands, cmsBrands) + }, + async (brands, { container }) => { + const cmsModuleService = container.resolve( + "cmsModuleService" + ) + + await cmsModuleService.deleteBrands( + brands.map((brand) => brand.id) + ) + } +) +``` + +The `retrieveBrandsStep` retrieves the brands from a brand module, and the `createBrandsInCmsStep` creates the brands in a third-party system using a CMS module. + +Then, create the following workflow that uses these steps: + +```ts title="Example Workflow" +export const syncBrandsWorkflow = createWorkflow( + "sync-brands", + () => { + const brands = retrieveBrandsStep() + + createBrandsInCmsStep({ brands }) + } +) +``` + +You can then use this workflow in an API route, scheduled job, or other resources that use this functionality. # Expose a Workflow Hook @@ -15355,6 +15132,1179 @@ So, if an error occurs during the loop, the compensation function will still rec For more details on error handling in workflows and steps, check the [Handling Errors chapter](https://docs.medusajs.com/learn/fundamentals/workflows/errors/index.html.md). +# Error Handling in Workflows + +In this chapter, you’ll learn about what happens when an error occurs in a workflow, how to disable error throwing in a workflow, and try-catch alternatives in workflow definitions. + +## Default Behavior of Errors in Workflows + +When an error occurs in a workflow, such as when a step throws an error, the workflow execution stops. Then, [the compensation function](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md) of every step in the workflow is called to undo the actions performed by their respective steps. + +The workflow's caller, such as an API route, subscriber, or scheduled job, will also fail and stop execution. Medusa then logs the error in the console. For API routes, an appropriate error is returned to the client based on the thrown error. + +Learn more about error handling in API routes in the [Errors chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/errors/index.html.md). + +This is the default behavior of errors in workflows. However, you can configure workflows to not throw errors, or you can configure a step's internal error handling mechanism to change the default behavior. + +*** + +## Disable Error Throwing in Workflow + +When an error is thrown in the workflow, that means the caller of the workflow, such as an API route, will fail and stop execution as well. + +While this is the common behavior, there are certain cases where you want to handle the error differently. For example, you may want to check the errors thrown by the workflow and return a custom error response to the client. + +The object parameter of a workflow's `run` method accepts a `throwOnError` property. When this property is set to `false`, the workflow will stop execution if an error occurs, but the Medusa's workflow engine will catch that error and return it to the caller instead of throwing it. + +For example: + +```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import myWorkflow from "../../../workflows/hello-world" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result, errors } = await myWorkflow(req.scope) + .run({ + // ... + throwOnError: false, + }) + + if (errors.length) { + return res.send({ + message: "Something unexpected happened. Please try again.", + }) + } + + res.send(result) +} +``` + +You disable throwing errors in the workflow by setting the `throwOnError` property to `false` in the `run` method of the workflow. + +The object returned by the `run` method contains an `errors` property. This property is an array of errors that occured during the workflow's execution. You can check this array to see if any errors occurred and handle them accordingly. + +An error object has the following properties: + +- action: (\`string\`) The ID of the step that threw the error. +- handlerType: (\`invoke\` \\| \`compensate\`) Where the error occurred. If the value is \`invoke\`, it means the error occurred in a step. Otherwise, the error occurred in the compensation function of a step. +- error: (\[Error]\(https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error)) The error object that was thrown. + +*** + +## Try-Catch Alternatives in Workflow Definition + +If you want to use try-catch mechanism in a workflow to undo step actions, use a [compensation function](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md) instead. + +### Why You Can't Use Try-Catch in Workflow Definitions + +Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. + +At that point, variables in the workflow don't have any values. They only do when you execute the workflow. + +So, try-catch blocks in the workflow definition function won't have an effect, as at that time the workflow is not executed and errors are not thrown. + +You can still use try-catch blocks in a workflow's step functions. For cases that require granular control over error handling in a workflow's definition, you can configure the internal error handling mechanism of a step. + +### Skip Workflow on Step Failure + +A step has a `skipOnPermanentFailure` configuration that allows you to configure what happens when an error occurs in the step. Its value can be a boolean or a string. + +By default, `skipOnPermanentFailure` is disabled. When it's enabled, the workflow's status is set to `skipped` instead of `failed`. This means: + +- Compensation functions of the workflow's steps are not called. +- The workflow's caller continues executing. You can still [access the error](#disable-error-throwing-in-workflow) that occurred during the workflow's execution as mentioned in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. + +This is useful when you want to perform actions if no error occurs, but you don't care about compensating the workflow's steps or you don't want to stop the caller's execution. + +You can think of setting the `skipOnPermanentFailure` to `true` as the equivalent of the following `try-catch` block: + +```ts title="Outside a Workflow" +try { + actionThatThrowsError() + + moreActions() +} catch (e) { + // don't do anything +} +``` + +You can do this in a workflow using the step's `skipOnPermanentFailure` configuration: + +```ts title="Workflow Equivalent" highlights={skipOnPermanentFailureEnabledHighlights} +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { + actionThatThrowsError, + moreActions, +} from "./steps" + +export const myWorkflow = createWorkflow( + "hello-world", + function (input) { + actionThatThrowsError().config({ + skipOnPermanentFailure: true, + }) + + // This action will not be executed if the previous step throws an error + moreActions() + } +) +``` + +You set the configuration of a step by chaining the `config` method to the step's function call. The `config` method accepts an object similar to the one that can be passed to `createStep`. + +In this example, if the `actionThatThrowsError` step throws an error, the rest of the workflow will be skipped, and the `moreActions` step will not be executed. + +You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. + +### Continue Workflow Execution from a Specific Step + +In some cases, if an error occurs in a step, you may want to continue the workflow's execution from a specific step instead of stopping the workflow's execution or skipping the rest of the steps. + +The `skipOnPermanentFailure` configuration can accept a step's ID as a value. Then, the workflow will continue execution from that step if an error occurs in the step that has the `skipOnPermanentFailure` configuration. + +The compensation function of the step that has the `skipOnPermanentFailure` configuration will not be called when an error occurs. + +You can think of setting the `skipOnPermanentFailure` to a step's ID as the equivalent of the following `try-catch` block: + +```ts title="Outside a Workflow" +try { + actionThatThrowsError() + + moreActions() +} catch (e) { + // do nothing +} + +continueExecutionFromStep() +``` + +You can do this in a workflow using the step's `skipOnPermanentFailure` configuration: + +```ts title="Workflow Equivalent" highlights={skipOnPermanentFailureStepHighlights} +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { + actionThatThrowsError, + moreActions, + continueExecutionFromStep, +} from "./steps" + +export const myWorkflow = createWorkflow( + "hello-world", + function (input) { + actionThatThrowsError().config({ + // The `continue-execution-from-step` is the ID passed as a first + // parameter to `createStep` of `continueExecutionFromStep`. + skipOnPermanentFailure: "continue-execution-from-step", + }) + + // This action will not be executed if the previous step throws an error + moreActions() + + // This action will be executed either way + continueExecutionFromStep() + } +) +``` + +In this example, you configure the `actionThatThrowsError` step to continue the workflow's execution from the `continueExecutionFromStep` step if an error occurs in the `actionThatThrowsError` step. + +Notice that you pass the ID of the `continueExecutionFromStep` step as it's set in the `createStep` function. + +So, the `moreActions` step will not be executed if the `actionThatThrowsError` step throws an error, and the `continueExecutionFromStep` will be executed anyway. + +You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. + +If the specified step ID doesn't exist in the workflow, it will be equivalent to setting the `skipOnPermanentFailure` configuration to `true`. So, the workflow will be skipped, and the rest of the steps will not be executed. + +### Set Step as Failed, but Continue Workflow Execution + +In some cases, you may want to fail a step, but continue the rest of the workflow's execution. + +This is useful when you don't want a step's failure to stop the workflow's execution, but you want to mark that step as failed. + +The `continueOnPermanentFailure` configuration allows you to do that. When enabled, the workflow's execution will continue, but the step will be marked as failed if an error occurs in that step. + +The compensation function of the step that has the `continueOnPermanentFailure` configuration will not be called when an error occurs. + +You can think of setting the `continueOnPermanentFailure` to `true` as the equivalent of the following `try-catch` block: + +```ts title="Outside a Workflow" +try { + actionThatThrowsError() +} catch (e) { + // do nothing +} + +moreActions() +``` + +You can do this in a workflow using the step's `continueOnPermanentFailure` configuration: + +```ts title="Workflow Equivalent" highlights={continueOnPermanentFailureHighlights} +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { + actionThatThrowsError, + moreActions, +} from "./steps" + +export const myWorkflow = createWorkflow( + "hello-world", + function (input) { + actionThatThrowsError().config({ + continueOnPermanentFailure: true, + }) + + // This action will be executed even if the previous step throws an error + moreActions() + } +) +``` + +In this example, if the `actionThatThrowsError` step throws an error, the `moreActions` step will still be executed. + +You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. + + +# Execute Another Workflow + +In this chapter, you'll learn how to execute a workflow in another. + +## Execute in a Workflow + +To execute a workflow in another, use the `runAsStep` method that every workflow has. + +For example: + +```ts highlights={workflowsHighlights} collapsibleLines="1-7" expandMoreButton="Show Imports" +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" + +const workflow = createWorkflow( + "hello-world", + async (input) => { + const products = createProductsWorkflow.runAsStep({ + input: { + products: [ + // ... + ], + }, + }) + + // ... + } +) +``` + +Instead of invoking the workflow and passing it the container, you use its `runAsStep` method and pass it an object as a parameter. + +The object has an `input` property to pass input to the workflow. + +*** + +## Preparing Input Data + +If you need to perform some data manipulation to prepare the other workflow's input data, use `transform` from the Workflows SDK. + +Learn about transform in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md). + +For example: + +```ts highlights={transformHighlights} collapsibleLines="1-12" +import { + createWorkflow, + transform, +} from "@medusajs/framework/workflows-sdk" +import { + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" + +type WorkflowInput = { + title: string +} + +const workflow = createWorkflow( + "hello-product", + async (input: WorkflowInput) => { + const createProductsData = transform({ + input, + }, (data) => [ + { + title: `Hello ${data.input.title}`, + }, + ]) + + const products = createProductsWorkflow.runAsStep({ + input: { + products: createProductsData, + }, + }) + + // ... + } +) +``` + +In this example, you use the `transform` function to prepend `Hello` to the title of the product. Then, you pass the result as an input to the `createProductsWorkflow`. + +*** + +## Run Workflow Conditionally + +To run a workflow in another based on a condition, use when-then from the Workflows SDK. + +Learn about when-then in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md). + +For example: + +```ts highlights={whenHighlights} collapsibleLines="1-16" +import { + createWorkflow, + when, +} from "@medusajs/framework/workflows-sdk" +import { + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" +import { + CreateProductWorkflowInputDTO, +} from "@medusajs/framework/types" + +type WorkflowInput = { + product?: CreateProductWorkflowInputDTO + should_create?: boolean +} + +const workflow = createWorkflow( + "hello-product", + async (input: WorkflowInput) => { + const product = when(input, ({ should_create }) => should_create) + .then(() => { + return createProductsWorkflow.runAsStep({ + input: { + products: [input.product], + }, + }) + }) + } +) +``` + +In this example, you use when-then to run the `createProductsWorkflow` only if `should_create` (passed in the `input`) is enabled. + + +# Long-Running Workflows + +In this chapter, you’ll learn what a long-running workflow is and how to configure it. + +## What is a Long-Running Workflow? + +When you execute a workflow, you wait until the workflow finishes execution to receive the output. + +A long-running workflow is a workflow that continues its execution in the background. You don’t receive its output immediately. Instead, you subscribe to the workflow execution to listen to status changes and receive its result once the execution is finished. + +### Why use Long-Running Workflows? + +Long-running workflows are useful if: + +- A task takes too long. For example, you're importing data from a CSV file. +- The workflow's steps wait for an external action to finish before resuming execution. For example, before you import the data from the CSV file, you wait until the import is confirmed by the user. +- You want to retry workflow steps after a long period of time. For example, you want to retry a step that processes a payment every day until the payment is successful. + - Refer to the [Retry Failed Steps chapter](https://docs.medusajs.com/learn/fundamentals/workflows/retry-failed-steps/index.html.md) for more information. + +*** + +## Configure Long-Running Workflows + +A workflow is considered long-running if at least one step has its `async` configuration set to `true` and doesn't return a step response. + +For example, consider the following workflow and steps: + +```ts title="src/workflows/hello-world.ts" highlights={[["15"]]} collapsibleLines="1-11" expandButtonLabel="Show More" +import { + createStep, + createWorkflow, + WorkflowResponse, + StepResponse, +} from "@medusajs/framework/workflows-sdk" + +const step1 = createStep("step-1", async () => { + return new StepResponse({}) +}) + +const step2 = createStep( + { + name: "step-2", + async: true, + }, + async () => { + console.log("Waiting to be successful...") + } +) + +const step3 = createStep("step-3", async () => { + return new StepResponse("Finished three steps") +}) + +const myWorkflow = createWorkflow( + "hello-world", + function () { + step1() + step2() + const message = step3() + + return new WorkflowResponse({ + message, + }) +}) + +export default myWorkflow +``` + +The second step has in its configuration object `async` set to `true` and it doesn't return a step response. This indicates that this step is an asynchronous step. + +So, when you execute the `hello-world` workflow, it continues its execution in the background once it reaches the second step. + +### When is a Workflow Considered Long-Running? + +A workflow is also considered long-running if: + +- One of its steps has its `async` configuration set to `true` and doesn't return a step response. +- One of its steps has its `retryInterval` option set as explained in the [Retry Failed Steps chapter](https://docs.medusajs.com/learn/fundamentals/workflows/retry-failed-steps/index.html.md). + +*** + +## Change Step Status + +Once the workflow's execution reaches an async step, it'll wait in the background for the step to succeed or fail before it moves to the next step. + +To fail or succeed a step, use the Workflow Engine Module's main service that is registered in the Medusa Container under the `Modules.WORKFLOW_ENGINE` (or `workflowsModuleService`) key. + +### Retrieve Transaction ID + +Before changing the status of a workflow execution's async step, you must have the execution's transaction ID. + +When you execute the workflow, the object returned has a `transaction` property, which is an object that holds the details of the workflow execution's transaction. Use its `transactionId` to later change async steps' statuses: + +```ts +const { transaction } = await myWorkflow(req.scope) + .run() + +// use transaction.transactionId later +``` + +### Change Step Status to Successful + +The Workflow Engine Module's main service has a `setStepSuccess` method to set a step's status to successful. If you use it on a workflow execution's async step, the workflow continues execution to the next step. + +For example, consider the following step: + +```ts highlights={successStatusHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports" +import { + Modules, + TransactionHandlerType, +} from "@medusajs/framework/utils" +import { + StepResponse, + createStep, +} from "@medusajs/framework/workflows-sdk" + +type SetStepSuccessStepInput = { + transactionId: string +}; + +export const setStepSuccessStep = createStep( + "set-step-success-step", + async function ( + { transactionId }: SetStepSuccessStepInput, + { container } + ) { + const workflowEngineService = container.resolve( + Modules.WORKFLOW_ENGINE + ) + + await workflowEngineService.setStepSuccess({ + idempotencyKey: { + action: TransactionHandlerType.INVOKE, + transactionId, + stepId: "step-2", + workflowId: "hello-world", + }, + stepResponse: new StepResponse("Done!"), + options: { + container, + }, + }) + } +) +``` + +In this step (which you use in a workflow other than the long-running workflow), you resolve the Workflow Engine Module's main service and set `step-2` of the previous workflow as successful. + +The `setStepSuccess` method of the workflow engine's main service accepts as a parameter an object having the following properties: + +- idempotencyKey: (\`object\`) The details of the workflow execution. + + - action: (\`invoke\` | \`compensate\`) If the step's compensation function is running, use \`compensate\`. Otherwise, use \`invoke\`. + + - transactionId: (\`string\`) The ID of the workflow execution's transaction. + + - stepId: (\`string\`) The ID of the step to change its status. This is the first parameter passed to \`createStep\` when creating the step. + + - workflowId: (\`string\`) The ID of the workflow. This is the first parameter passed to \`createWorkflow\` when creating the workflow. +- stepResponse: (\`StepResponse\`) Set the response of the step. This is similar to the response you return in a step's definition, but since the \`async\` step doesn't have a response, you set its response when changing its status. +- options: (\`Record\\`) Options to pass to the step. + + - container: (\`MedusaContainer\`) An instance of the Medusa Container + +### Change Step Status to Failed + +The Workflow Engine Module's main service also has a `setStepFailure` method that changes a step's status to failed. It accepts the same parameter as `setStepSuccess`. + +After changing the async step's status to failed, the workflow execution fails and the compensation functions of previous steps are executed. + +For example: + +```ts highlights={failureStatusHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports" +import { + Modules, + TransactionHandlerType, +} from "@medusajs/framework/utils" +import { + StepResponse, + createStep, +} from "@medusajs/framework/workflows-sdk" + +type SetStepFailureStepInput = { + transactionId: string +}; + +export const setStepFailureStep = createStep( + "set-step-failure-step", + async function ( + { transactionId }: SetStepFailureStepInput, + { container } + ) { + const workflowEngineService = container.resolve( + Modules.WORKFLOW_ENGINE + ) + + await workflowEngineService.setStepFailure({ + idempotencyKey: { + action: TransactionHandlerType.INVOKE, + transactionId, + stepId: "step-2", + workflowId: "hello-world", + }, + stepResponse: new StepResponse("Failed!"), + options: { + container, + }, + }) + } +) +``` + +You use this step in another workflow that changes the status of an async step in a long-running workflow's execution to failed. + +*** + +## Access Long-Running Workflow Status and Result + +To access the status and result of a long-running workflow execution, use the `subscribe` and `unsubscribe` methods of the Workflow Engine Module's main service. + +To retrieve the workflow execution's details at a later point, you must enable [storing the workflow's executions](https://docs.medusajs.com/learn/fundamentals/workflows/store-executions/index.html.md). + +For example: + +```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-11" expandButtonLabel="Show Imports" +import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import myWorkflow from "../../../workflows/hello-world" +import { + IWorkflowEngineService, +} from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" + +export async function GET(req: MedusaRequest, res: MedusaResponse) { + const { transaction, result } = await myWorkflow(req.scope).run() + + const workflowEngineService = req.scope.resolve< + IWorkflowEngineService + >( + Modules.WORKFLOW_ENGINE + ) + + const subscriptionOptions = { + workflowId: "hello-world", + transactionId: transaction.transactionId, + subscriberId: "hello-world-subscriber", + } + + await workflowEngineService.subscribe({ + ...subscriptionOptions, + subscriber: async (data) => { + if (data.eventType === "onFinish") { + console.log("Finished execution", data.result) + // unsubscribe + await workflowEngineService.unsubscribe({ + ...subscriptionOptions, + subscriberOrId: subscriptionOptions.subscriberId, + }) + } else if (data.eventType === "onStepFailure") { + console.log("Workflow failed", data.step) + } + }, + }) + + res.send(result) +} +``` + +In the above example, you execute the long-running workflow `hello-world` and resolve the Workflow Engine Module's main service from the Medusa container. + +### subscribe Method + +The main service's `subscribe` method allows you to listen to changes in the workflow execution’s status. It accepts an object having three properties: + +- workflowId: (\`string\`) The name of the workflow. +- transactionId: (\`string\`) The ID of the workflow exection's transaction. The transaction's details are returned in the response of the workflow execution. +- subscriberId: (\`string\`) The ID of the subscriber. +- subscriber: (\`(data: \{ eventType: string, result?: any }) => Promise\\`) The function executed when the workflow execution's status changes. The function receives a data object. It has an \`eventType\` property, which you use to check the status of the workflow execution. + +If the value of `eventType` in the `subscriber` function's first parameter is `onFinish`, the workflow finished executing. The first parameter then also has a `result` property holding the workflow's output. + +### unsubscribe Method + +You can unsubscribe from the workflow using the workflow engine's `unsubscribe` method, which requires the same object parameter as the `subscribe` method. + +However, instead of the `subscriber` property, it requires a `subscriberOrId` property whose value is the same `subscriberId` passed to the `subscribe` method. + +*** + +## Example: Restaurant-Delivery Recipe + +To find a full example of a long-running workflow, refer to the [restaurant-delivery recipe](https://docs.medusajs.com/resources/recipes/marketplace/examples/restaurant-delivery/index.html.md). + +In the recipe, you use a long-running workflow that moves an order from placed to completed. The workflow waits for the restaurant to accept the order, the driver to pick up the order, and other external actions. + + +# Multiple Step Usage in Workflow + +In this chapter, you'll learn how to use a step multiple times in a workflow. + +## Problem Reusing a Step in a Workflow + +In some cases, you may need to use a step multiple times in the same workflow. + +The most common example is using the `useQueryGraphStep` multiple times in a workflow to retrieve multiple unrelated data, such as customers and products. + +Each workflow step must have a unique ID, which is the ID passed as a first parameter when creating the step: + +```ts +const useQueryGraphStep = createStep( + "use-query-graph" + // ... +) +``` + +This causes an error when you use the same step multiple times in a workflow, as it's registered in the workflow as two steps having the same ID: + +```ts +const helloWorkflow = createWorkflow( + "hello", + () => { + const { data: products } = useQueryGraphStep({ + entity: "product", + fields: ["id"], + }) + + // ERROR OCCURS HERE: A STEP HAS THE SAME ID AS ANOTHER IN THE WORKFLOW + const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: ["id"], + }) + } +) +``` + +The next section explains how to fix this issue to use the same step multiple times in a workflow. + +*** + +## How to Use a Step Multiple Times in a Workflow? + +When you execute a step in a workflow, you can chain a `config` method to it to change the step's config. + +Use the `config` method to change a step's ID for a single execution. + +So, this is the correct way to write the example above: + +```ts highlights={highlights} +const helloWorkflow = createWorkflow( + "hello", + () => { + const { data: products } = useQueryGraphStep({ + entity: "product", + fields: ["id"], + }) + + // ✓ No error occurs, the step has a different ID. + const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: ["id"], + }).config({ name: "fetch-customers" }) + } +) +``` + +The `config` method accepts an object with a `name` property. Its value is a new ID of the step to use for this execution only. + +The first `useQueryGraphStep` usage has the ID `use-query-graph`, and the second `useQueryGraphStep` usage has the ID `fetch-customers`. + + +# Loaders + +In this chapter, you’ll learn about loaders and how to use them. + +## What is a Loader? + +When building a commerce application, you'll often need to execute an action the first time the application starts. For example, if your application needs to connect to databases other than Medusa's PostgreSQL database, you might need to establish a connection on application startup. + +In Medusa, you can execute an action when the application starts using a loader. A loader is a function exported by a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), which is a package of business logic for a single domain. When the Medusa application starts, it executes all loaders exported by configured modules. + +Loaders are useful to register custom resources, such as database connections, in the [module's container](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md), which is similar to the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) but includes only [resources available to the module](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). Modules are isolated, so they can't access resources outside of them, such as a service in another module. + +Medusa isolates modules to ensure that they're re-usable across applications, aren't tightly coupled to other resources, and don't have implications when integrated into the Medusa application. Learn more about why modules are isolated in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md), and check out [this reference for the list of resources in the module's container](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). + +*** + +## How to Create a Loader? + +### 1. Implement Loader Function + +You create a loader function in a TypeScript or JavaScript file under a module's `loaders` directory. + +For example, consider you have a `hello` module, you can create a loader at `src/modules/hello/loaders/hello-world.ts` with the following content: + +![Example of loader file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732865671/Medusa%20Book/loader-dir-overview_eg6vtu.jpg) + +Learn how to create a module in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). + +```ts title="src/modules/hello/loaders/hello-world.ts" +import { + LoaderOptions, +} from "@medusajs/framework/types" + +export default async function helloWorldLoader({ + container, +}: LoaderOptions) { + const logger = container.resolve("logger") + + logger.info("[HELLO MODULE] Just started the Medusa application!") +} +``` + +The loader file exports an async function, which is the function executed when the application loads. + +The function receives an object parameter that has a `container` property, which is the module's container that you can use to resolve resources from. In this example, you resolve the Logger utility to log a message in the terminal. + +Find the list of resources in the module's container in [this reference](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). + +### 2. Export Loader in Module Definition + +After implementing the loader, you must export it in the module's definition in the `index.ts` file at the root of the module's directory. Otherwise, the Medusa application will not run it. + +So, to export the loader you implemented above in the `hello` module, add the following to `src/modules/hello/index.ts`: + +```ts title="src/modules/hello/index.ts" +// other imports... +import helloWorldLoader from "./loaders/hello-world" + +export default Module("hello", { + // ... + loaders: [helloWorldLoader], +}) +``` + +The second parameter of the `Module` function accepts a `loaders` property whose value is an array of loader functions. The Medusa application will execute these functions when it starts. + +### Test the Loader + +Assuming your module is [added to Medusa's configuration](https://docs.medusajs.com/learn/fundamentals/modules#4-add-module-to-medusas-configurations/index.html.md), you can test the loader by starting the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, you'll find the following message logged in the terminal: + +```plain +info: [HELLO MODULE] Just started the Medusa application! +``` + +This indicates that the loader in the `hello` module ran and logged this message. + +*** + +## When are Loaders Executed? + +When you start the Medusa application, it executes the loaders of all modules in their registration order. + +A loader is executed before the module's main service is instantiated. So, you can use loaders to register in the module's container resources that you want to use in the module's service. For example, you can register a database connection. + +Loaders are also useful to only load a module if a certain condition is met. For example, if you try to connect to a database in a loader but the connection fails, you can throw an error in the loader to prevent the module from being loaded. This is useful if your module depends on an external service to work. + +*** + +## Example: Register Custom MongoDB Connection + +As mentioned in this chapter's introduction, loaders are most useful when you need to register a custom resource in the module's container to re-use it in other customizations in the module. + +Consider your have a MongoDB module that allows you to perform operations on a MongoDB database. + +### Prerequisites + +- [MongoDB database that you can connect to from a local machine.](https://www.mongodb.com) +- [Install the MongoDB SDK in your Medusa application.](https://www.mongodb.com/docs/drivers/node/current/quick-start/download-and-install/#install-the-node.js-driver) + +To connect to the database, you create the following loader in your module: + +```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights} +import { LoaderOptions } from "@medusajs/framework/types" +import { asValue } from "awilix" +import { MongoClient } from "mongodb" + +type ModuleOptions = { + connection_url?: string + db_name?: string +} + +export default async function mongoConnectionLoader({ + container, + options, +}: LoaderOptions) { + if (!options.connection_url) { + throw new Error(`[MONGO MDOULE]: connection_url option is required.`) + } + if (!options.db_name) { + throw new Error(`[MONGO MDOULE]: db_name option is required.`) + } + const logger = container.resolve("logger") + + try { + const clientDb = ( + await (new MongoClient(options.connection_url)).connect() + ).db(options.db_name) + + logger.info("Connected to MongoDB") + + container.register( + "mongoClient", + asValue(clientDb) + ) + } catch (e) { + logger.error( + `[MONGO MDOULE]: An error occurred while connecting to MongoDB: ${e}` + ) + } +} +``` + +The loader function accepts in its object parameter an `options` property, which is the options passed to the module in Medusa's configurations. For example: + +```ts title="medusa-config.ts" highlights={optionHighlights} +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "./src/modules/mongo", + options: { + connection_url: process.env.MONGO_CONNECTION_URL, + db_name: process.env.MONGO_DB_NAME, + }, + }, + ], +}) +``` + +Passing options is useful when your module needs informations like connection URLs or API keys, as it ensures your module can be re-usable across applications. For the MongoDB Module, you expect two options: + +- `connection_url`: the URL to connect to the MongoDB database. +- `db_name`: The name of the database to connect to. + +In the loader, you check first that these options are set before proceeding. Then, you create an instance of the MongoDB client and connect to the database specified in the options. + +After creating the client, you register it in the module's container using the container's `register` method. The method accepts two parameters: + +1. The key to register the resource under, which in this case is `mongoClient`. You'll use this name later to resolve the client. +2. The resource to register in the container, which is the MongoDB client you created. However, you don't pass the resource as-is. Instead, you need to use an `asValue` function imported from the [awilix package](https://github.com/jeffijoe/awilix), which is the package used to implement the container functionality in Medusa. + +### Use Custom Registered Resource in Module's Service + +After registering the custom MongoDB client in the module's container, you can now resolve and use it in the module's service. + +For example: + +```ts title="src/modules/mongo/service.ts" +import type { Db } from "mongodb" + +type InjectedDependencies = { + mongoClient: Db +} + +export default class MongoModuleService { + private mongoClient_: Db + + constructor({ mongoClient }: InjectedDependencies) { + this.mongoClient_ = mongoClient + } + + async createMovie({ title }: { + title: string + }) { + const moviesCol = this.mongoClient_.collection("movie") + + const insertedMovie = await moviesCol.insertOne({ + title, + }) + + const movie = await moviesCol.findOne({ + _id: insertedMovie.insertedId, + }) + + return movie + } + + async deleteMovie(id: string) { + const moviesCol = this.mongoClient_.collection("movie") + + await moviesCol.deleteOne({ + _id: { + equals: id, + }, + }) + } +} +``` + +The service `MongoModuleService` resolves the `mongoClient` resource you registered in the loader and sets it as a class property. You then use it in the `createMovie` and `deleteMovie` methods, which create and delete a document in a `movie` collection in the MongoDB database, respectively. + +Make sure to export the loader in the module's definition in the `index.ts` file at the root directory of the module: + +```ts title="src/modules/mongo/index.ts" highlights={[["9"]]} +import { Module } from "@medusajs/framework/utils" +import MongoModuleService from "./service" +import mongoConnectionLoader from "./loaders/connection" + +export const MONGO_MODULE = "mongo" + +export default Module(MONGO_MODULE, { + service: MongoModuleService, + loaders: [mongoConnectionLoader], +}) +``` + +### Test it Out + +You can test the connection out by starting the Medusa application. If it's successful, you'll see the following message logged in the terminal: + +```bash +info: Connected to MongoDB +``` + +You can now resolve the MongoDB Module's main service in your customizations to perform operations on the MongoDB database. + + +# Run Workflow Steps in Parallel + +In this chapter, you’ll learn how to run workflow steps in parallel. + +## parallelize Utility Function + +If your workflow has steps that don’t rely on one another’s results, run them in parallel using `parallelize` from the Workflows SDK. + +The workflow waits until all steps passed to the `parallelize` function finish executing before continuing to the next step. + +For example: + +```ts highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" +import { + createWorkflow, + WorkflowResponse, + parallelize, +} from "@medusajs/framework/workflows-sdk" +import { + createProductStep, + getProductStep, + createPricesStep, + attachProductToSalesChannelStep, +} from "./steps" + +interface WorkflowInput { + title: string +} + +const myWorkflow = createWorkflow( + "my-workflow", + (input: WorkflowInput) => { + const product = createProductStep(input) + + const [prices, productSalesChannel] = parallelize( + createPricesStep(product), + attachProductToSalesChannelStep(product) + ) + + const refetchedProduct = getProductStep(product.id) + + return new WorkflowResponse(refetchedProduct) + } +) +``` + +The `parallelize` function accepts the steps to run in parallel as a parameter. + +It returns an array of the steps' results in the same order they're passed to the `parallelize` function. + +So, `prices` is the result of `createPricesStep`, and `productSalesChannel` is the result of `attachProductToSalesChannelStep`. + + +# Retry Failed Steps + +In this chapter, you’ll learn how to configure steps to allow retrial on failure. + +## What is a Step Retrial? + +A step retrial is a mechanism that allows a step to be retried automatically when it fails. This is useful for handling transient errors, such as network issues or temporary unavailability of a service. + +When a step fails, the workflow engine can automatically retry the step a specified number of times before marking the workflow as failed. This can help improve the reliability and resilience of your workflows. + +You can also configure the interval between retries, allowing you to wait for a certain period before attempting the step again. This is useful when the failure is due to a temporary issue that may resolve itself after some time. + +For example, if a step captures a payment, you may want to retry it the next day until the payment is successful or the maximum number of retries is reached. + +*** + +## Configure a Step’s Retrial + +By default, when an error occurs in a step, the step and the workflow fail, and the execution stops. + +You can configure the step to retry on failure. The `createStep` function can accept a configuration object instead of the step’s name as a first parameter. + +For example: + +```ts title="src/workflows/hello-world.ts" highlights={[["10"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + createStep, + createWorkflow, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" + +const step1 = createStep( + { + name: "step-1", + maxRetries: 2, + }, + async () => { + console.log("Executing step 1") + + throw new Error("Oops! Something happened.") + } +) + +const myWorkflow = createWorkflow( + "hello-world", + function () { + const str1 = step1() + + return new WorkflowResponse({ + message: str1, + }) +}) + +export default myWorkflow +``` + +The step’s configuration object accepts a `maxRetries` property, which is a number indicating the number of times a step can be retried when it fails. + +When you execute the above workflow, you’ll see the following result in the terminal: + +```bash +Executing step 1 +Executing step 1 +Executing step 1 +error: Oops! Something happened. +Error: Oops! Something happened. +``` + +The first line indicates the first time the step was executed, and the next two lines indicate the times the step was retried. After that, the step and workflow fail. + +*** + +## Step Retry Intervals + +By default, a step is retried immediately after it fails. To specify a wait time before a step is retried, pass a `retryInterval` property to the step's configuration object. Its value is a number of seconds to wait before retrying the step. + +For example: + +```ts title="src/workflows/hello-world.ts" highlights={[["5"]]} +const step1 = createStep( + { + name: "step-1", + maxRetries: 2, + retryInterval: 2, // 2 seconds + }, + async () => { + // ... + } +) +``` + +In this example, if the step fails, it will be retried after two seconds. + +### Maximum Retry Interval + +The `retryInterval` property's maximum value is [Number.MAX\_SAFE\_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER). So, you can set a very long wait time before the step is retried, allowing you to retry steps after a long period. + +For example, to retry a step after a day: + +```ts title="src/workflows/hello-world.ts" highlights={[["5"]]} +const step1 = createStep( + { + name: "step-1", + maxRetries: 2, + retryInterval: 86400, // 1 day + }, + async () => { + // ... + } +) +``` + +In this example, if the step fails, it will be retried after `86400` seconds (one day). + +### Interval Changes Workflow to Long-Running + +By setting `retryInterval` on a step, a workflow that uses that step becomes a [long-running workflow](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow/index.html.md) that runs asynchronously in the background. This is useful when creating workflows that may fail and should run for a long time until they succeed, such as waiting for a payment to be captured or a shipment to be delivered. + +However, since the long-running workflow runs in the background, you won't receive its result or errors immediately when you execute the workflow. + +Instead, you must subscribe to the workflow's execution using the Workflow Engine Module Service. Learn more about it in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow#access-long-running-workflow-status-and-result/index.html.md). + + # Conditions in Workflows with When-Then In this chapter, you'll learn how to execute an action based on a condition in a workflow using when-then from the Workflows SDK. @@ -15514,6 +16464,211 @@ Since `then` returns a value different than the step's result, you pass to the ` The second and third parameters are the same as the parameters you previously passed to `when`. +# Data Manipulation in Workflows with transform + +In this chapter, you'll learn how to use `transform` from the Workflows SDK to manipulate data and variables in a workflow. + +## Why Variable Manipulation isn't Allowed in Workflows + +Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. + +At that point, variables in the workflow don't have any values. They only do when you execute the workflow. + +So, you can only pass variables as parameters to steps. But, in a workflow, you can't change a variable's value or, if the variable is an array, loop over its items. + +Instead, use `transform` from the Workflows SDK. + +Restrictions for variable manipulation is only applicable in a workflow's definition. You can still manipulate variables in your step's code. + +*** + +## What is the transform Utility? + +`transform` creates a new variable as the result of manipulating other variables. + +For example, consider you have two strings as the output of two steps: + +```ts +const str1 = step1() +const str2 = step2() +``` + +To concatenate the strings, you create a new variable `str3` using the `transform` function: + +```ts highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + transform, +} from "@medusajs/framework/workflows-sdk" +// step imports... + +const myWorkflow = createWorkflow( + "hello-world", + function (input) { + const str1 = step1(input) + const str2 = step2(input) + + const str3 = transform( + { str1, str2 }, + (data) => `${data.str1}${data.str2}` + ) + + return new WorkflowResponse(str3) + } +) +``` + +`transform` accepts two parameters: + +1. The first parameter is an object of variables to manipulate. The object is passed as a parameter to `transform`'s second parameter function. +2. The second parameter is the function performing the variable manipulation. + +The value returned by the second parameter function is returned by `transform`. So, the `str3` variable holds the concatenated string. + +You can use the returned value in the rest of the workflow, either to pass it as an input to other steps or to return it in the workflow's response. + +*** + +## Example: Looping Over Array + +Use `transform` to loop over arrays to create another variable from the array's items. + +For example: + +```ts collapsibleLines="1-7" expandButtonLabel="Show Imports" +import { + createWorkflow, + WorkflowResponse, + transform, +} from "@medusajs/framework/workflows-sdk" +// step imports... + +type WorkflowInput = { + items: { + id: string + name: string + }[] +} + +const myWorkflow = createWorkflow( + "hello-world", + function ({ items }: WorkflowInput) { + const ids = transform( + { items }, + (data) => data.items.map((item) => item.id) + ) + + doSomethingStep(ids) + + // ... + } +) +``` + +This workflow receives an `items` array in its input. + +You use `transform` to create an `ids` variable, which is an array of strings holding the `id` of each item in the `items` array. + +You then pass the `ids` variable as a parameter to the `doSomethingStep`. + +*** + +## Example: Creating a Date + +If you create a date with `new Date()` in a workflow's constructor function, Medusa evaluates the date's value when it creates the internal representation of the workflow, not when the workflow is executed. + +So, use `transform` instead to create a date variable with `new Date()`. + +For example: + +```ts +const myWorkflow = createWorkflow( + "hello-world", + () => { + const today = transform({}, () => new Date()) + + doSomethingStep(today) + } +) +``` + +In this workflow, `today` is only evaluated when the workflow is executed. + +*** + +## Caveats + +### Transform Evaluation + +`transform`'s value is only evaluated if you pass its output to a step or in the workflow response. + +For example, if you have the following workflow: + +```ts +const myWorkflow = createWorkflow( + "hello-world", + function (input) { + const str = transform( + { input }, + (data) => `${data.input.str1}${data.input.str2}` + ) + + return new WorkflowResponse("done") + } +) +``` + +Since `str`'s value isn't used as a step's input or passed to `WorkflowResponse`, its value is never evaluated. + +### Data Validation + +`transform` should only be used to perform variable or data manipulation. + +If you want to perform some validation on the data, use a step or [when-then](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md) instead. + +For example: + +```ts +// DON'T +const myWorkflow = createWorkflow( + "hello-world", + function (input) { + const str = transform( + { input }, + (data) => { + if (!input.str1) { + throw new Error("Not allowed!") + } + } + ) + } +) + +// DO +const validateHasStr1Step = createStep( + "validate-has-str1", + ({ input }) => { + if (!input.str1) { + throw new Error("Not allowed!") + } + } +) + +const myWorkflow = createWorkflow( + "hello-world", + function (input) { + validateHasStr1({ + input, + }) + + // workflow continues its execution only if + // the step doesn't throw the error. + } +) +``` + + # Workflow Constraints This chapter lists constraints of defining a workflow or its steps. @@ -15868,1274 +17023,128 @@ const step1 = createStep( ``` -# Error Handling in Workflows +# Workflow Hooks -In this chapter, you’ll learn about what happens when an error occurs in a workflow, how to disable error throwing in a workflow, and try-catch alternatives in workflow definitions. +In this chapter, you'll learn what a workflow hook is and how to consume them. -## Default Behavior of Errors in Workflows +## What is a Workflow Hook? -When an error occurs in a workflow, such as when a step throws an error, the workflow execution stops. Then, [the compensation function](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md) of every step in the workflow is called to undo the actions performed by their respective steps. +A workflow hook is a point in a workflow where you can inject custom functionality as a step function, called a hook handler. -The workflow's caller, such as an API route, subscriber, or scheduled job, will also fail and stop execution. Medusa then logs the error in the console. For API routes, an appropriate error is returned to the client based on the thrown error. +Medusa exposes hooks in many of its workflows that are used in its API routes. You can consume those hooks to add your custom logic. -Learn more about error handling in API routes in the [Errors chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/errors/index.html.md). +Refer to the [Workflows Reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md) to view all workflows and their hooks. -This is the default behavior of errors in workflows. However, you can configure workflows to not throw errors, or you can configure a step's internal error handling mechanism to change the default behavior. +You want to perform a custom action during a workflow's execution, such as when a product is created. *** -## Disable Error Throwing in Workflow +## How to Consume a Hook? -When an error is thrown in the workflow, that means the caller of the workflow, such as an API route, will fail and stop execution as well. +A workflow has a special `hooks` property which is an object that holds its hooks. -While this is the common behavior, there are certain cases where you want to handle the error differently. For example, you may want to check the errors thrown by the workflow and return a custom error response to the client. +So, in a TypeScript or JavaScript file created under the `src/workflows/hooks` directory: -The object parameter of a workflow's `run` method accepts a `throwOnError` property. When this property is set to `false`, the workflow will stop execution if an error occurs, but the Medusa's workflow engine will catch that error and return it to the caller instead of throwing it. +- Import the workflow. +- Access its hook using the `hooks` property. +- Pass the hook a step function as a parameter to consume it. + +For example, to consume the `productsCreated` hook of Medusa's `createProductsWorkflow`, create the file `src/workflows/hooks/product-created.ts` with the following content: + +```ts title="src/workflows/hooks/product-created.ts" highlights={handlerHighlights} +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" + +createProductsWorkflow.hooks.productsCreated( + async ({ products }, { container }) => { + // TODO perform an action + } +) +``` + +The `productsCreated` hook is available on the workflow's `hooks` property by its name. + +You invoke the hook, passing a step function (the hook handler) as a parameter. + +Now, when a product is created using the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), your hook handler is executed after the product is created. + +A hook can have only one handler. + +Refer to the [createProductsWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) to see at which point the hook handler is executed. + +### Hook Handler Parameter + +Since a hook handler is essentially a step function, it receives the hook's input as a first parameter, and an object holding a `container` property as a second parameter. + +Each hook has different input. For example, the `productsCreated` hook receives an object having a `products` property holding the created product. + +### Hook Handler Compensation + +Since the hook handler is a step function, you can set its compensation function as a second parameter of the hook. For example: -```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import myWorkflow from "../../../workflows/hello-world" +```ts title="src/workflows/hooks/product-created.ts" +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result, errors } = await myWorkflow(req.scope) - .run({ - // ... - throwOnError: false, - }) +createProductsWorkflow.hooks.productsCreated( + async ({ products }, { container }) => { + // TODO perform an action - if (errors.length) { - return res.send({ - message: "Something unexpected happened. Please try again.", - }) - } - - res.send(result) -} -``` - -You disable throwing errors in the workflow by setting the `throwOnError` property to `false` in the `run` method of the workflow. - -The object returned by the `run` method contains an `errors` property. This property is an array of errors that occured during the workflow's execution. You can check this array to see if any errors occurred and handle them accordingly. - -An error object has the following properties: - -- action: (\`string\`) The ID of the step that threw the error. -- handlerType: (\`invoke\` \\| \`compensate\`) Where the error occurred. If the value is \`invoke\`, it means the error occurred in a step. Otherwise, the error occurred in the compensation function of a step. -- error: (\[Error]\(https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error)) The error object that was thrown. - -*** - -## Try-Catch Alternatives in Workflow Definition - -If you want to use try-catch mechanism in a workflow to undo step actions, use a [compensation function](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md) instead. - -### Why You Can't Use Try-Catch in Workflow Definitions - -Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. - -At that point, variables in the workflow don't have any values. They only do when you execute the workflow. - -So, try-catch blocks in the workflow definition function won't have an effect, as at that time the workflow is not executed and errors are not thrown. - -You can still use try-catch blocks in a workflow's step functions. For cases that require granular control over error handling in a workflow's definition, you can configure the internal error handling mechanism of a step. - -### Skip Workflow on Step Failure - -A step has a `skipOnPermanentFailure` configuration that allows you to configure what happens when an error occurs in the step. Its value can be a boolean or a string. - -By default, `skipOnPermanentFailure` is disabled. When it's enabled, the workflow's status is set to `skipped` instead of `failed`. This means: - -- Compensation functions of the workflow's steps are not called. -- The workflow's caller continues executing. You can still [access the error](#disable-error-throwing-in-workflow) that occurred during the workflow's execution as mentioned in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. - -This is useful when you want to perform actions if no error occurs, but you don't care about compensating the workflow's steps or you don't want to stop the caller's execution. - -You can think of setting the `skipOnPermanentFailure` to `true` as the equivalent of the following `try-catch` block: - -```ts title="Outside a Workflow" -try { - actionThatThrowsError() - - moreActions() -} catch (e) { - // don't do anything -} -``` - -You can do this in a workflow using the step's `skipOnPermanentFailure` configuration: - -```ts title="Workflow Equivalent" highlights={skipOnPermanentFailureEnabledHighlights} -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - actionThatThrowsError, - moreActions, -} from "./steps" - -export const myWorkflow = createWorkflow( - "hello-world", - function (input) { - actionThatThrowsError().config({ - skipOnPermanentFailure: true, - }) - - // This action will not be executed if the previous step throws an error - moreActions() - } -) -``` - -You set the configuration of a step by chaining the `config` method to the step's function call. The `config` method accepts an object similar to the one that can be passed to `createStep`. - -In this example, if the `actionThatThrowsError` step throws an error, the rest of the workflow will be skipped, and the `moreActions` step will not be executed. - -You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. - -### Continue Workflow Execution from a Specific Step - -In some cases, if an error occurs in a step, you may want to continue the workflow's execution from a specific step instead of stopping the workflow's execution or skipping the rest of the steps. - -The `skipOnPermanentFailure` configuration can accept a step's ID as a value. Then, the workflow will continue execution from that step if an error occurs in the step that has the `skipOnPermanentFailure` configuration. - -The compensation function of the step that has the `skipOnPermanentFailure` configuration will not be called when an error occurs. - -You can think of setting the `skipOnPermanentFailure` to a step's ID as the equivalent of the following `try-catch` block: - -```ts title="Outside a Workflow" -try { - actionThatThrowsError() - - moreActions() -} catch (e) { - // do nothing -} - -continueExecutionFromStep() -``` - -You can do this in a workflow using the step's `skipOnPermanentFailure` configuration: - -```ts title="Workflow Equivalent" highlights={skipOnPermanentFailureStepHighlights} -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - actionThatThrowsError, - moreActions, - continueExecutionFromStep, -} from "./steps" - -export const myWorkflow = createWorkflow( - "hello-world", - function (input) { - actionThatThrowsError().config({ - // The `continue-execution-from-step` is the ID passed as a first - // parameter to `createStep` of `continueExecutionFromStep`. - skipOnPermanentFailure: "continue-execution-from-step", - }) - - // This action will not be executed if the previous step throws an error - moreActions() - - // This action will be executed either way - continueExecutionFromStep() - } -) -``` - -In this example, you configure the `actionThatThrowsError` step to continue the workflow's execution from the `continueExecutionFromStep` step if an error occurs in the `actionThatThrowsError` step. - -Notice that you pass the ID of the `continueExecutionFromStep` step as it's set in the `createStep` function. - -So, the `moreActions` step will not be executed if the `actionThatThrowsError` step throws an error, and the `continueExecutionFromStep` will be executed anyway. - -You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. - -If the specified step ID doesn't exist in the workflow, it will be equivalent to setting the `skipOnPermanentFailure` configuration to `true`. So, the workflow will be skipped, and the rest of the steps will not be executed. - -### Set Step as Failed, but Continue Workflow Execution - -In some cases, you may want to fail a step, but continue the rest of the workflow's execution. - -This is useful when you don't want a step's failure to stop the workflow's execution, but you want to mark that step as failed. - -The `continueOnPermanentFailure` configuration allows you to do that. When enabled, the workflow's execution will continue, but the step will be marked as failed if an error occurs in that step. - -The compensation function of the step that has the `continueOnPermanentFailure` configuration will not be called when an error occurs. - -You can think of setting the `continueOnPermanentFailure` to `true` as the equivalent of the following `try-catch` block: - -```ts title="Outside a Workflow" -try { - actionThatThrowsError() -} catch (e) { - // do nothing -} - -moreActions() -``` - -You can do this in a workflow using the step's `continueOnPermanentFailure` configuration: - -```ts title="Workflow Equivalent" highlights={continueOnPermanentFailureHighlights} -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - actionThatThrowsError, - moreActions, -} from "./steps" - -export const myWorkflow = createWorkflow( - "hello-world", - function (input) { - actionThatThrowsError().config({ - continueOnPermanentFailure: true, - }) - - // This action will be executed even if the previous step throws an error - moreActions() - } -) -``` - -In this example, if the `actionThatThrowsError` step throws an error, the `moreActions` step will still be executed. - -You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section. - - -# Multiple Step Usage in Workflow - -In this chapter, you'll learn how to use a step multiple times in a workflow. - -## Problem Reusing a Step in a Workflow - -In some cases, you may need to use a step multiple times in the same workflow. - -The most common example is using the `useQueryGraphStep` multiple times in a workflow to retrieve multiple unrelated data, such as customers and products. - -Each workflow step must have a unique ID, which is the ID passed as a first parameter when creating the step: - -```ts -const useQueryGraphStep = createStep( - "use-query-graph" - // ... -) -``` - -This causes an error when you use the same step multiple times in a workflow, as it's registered in the workflow as two steps having the same ID: - -```ts -const helloWorkflow = createWorkflow( - "hello", - () => { - const { data: products } = useQueryGraphStep({ - entity: "product", - fields: ["id"], - }) - - // ERROR OCCURS HERE: A STEP HAS THE SAME ID AS ANOTHER IN THE WORKFLOW - const { data: customers } = useQueryGraphStep({ - entity: "customer", - fields: ["id"], - }) - } -) -``` - -The next section explains how to fix this issue to use the same step multiple times in a workflow. - -*** - -## How to Use a Step Multiple Times in a Workflow? - -When you execute a step in a workflow, you can chain a `config` method to it to change the step's config. - -Use the `config` method to change a step's ID for a single execution. - -So, this is the correct way to write the example above: - -```ts highlights={highlights} -const helloWorkflow = createWorkflow( - "hello", - () => { - const { data: products } = useQueryGraphStep({ - entity: "product", - fields: ["id"], - }) - - // ✓ No error occurs, the step has a different ID. - const { data: customers } = useQueryGraphStep({ - entity: "customer", - fields: ["id"], - }).config({ name: "fetch-customers" }) - } -) -``` - -The `config` method accepts an object with a `name` property. Its value is a new ID of the step to use for this execution only. - -The first `useQueryGraphStep` usage has the ID `use-query-graph`, and the second `useQueryGraphStep` usage has the ID `fetch-customers`. - - -# Long-Running Workflows - -In this chapter, you’ll learn what a long-running workflow is and how to configure it. - -## What is a Long-Running Workflow? - -When you execute a workflow, you wait until the workflow finishes execution to receive the output. - -A long-running workflow is a workflow that continues its execution in the background. You don’t receive its output immediately. Instead, you subscribe to the workflow execution to listen to status changes and receive its result once the execution is finished. - -### Why use Long-Running Workflows? - -Long-running workflows are useful if: - -- A task takes too long. For example, you're importing data from a CSV file. -- The workflow's steps wait for an external action to finish before resuming execution. For example, before you import the data from the CSV file, you wait until the import is confirmed by the user. -- You want to retry workflow steps after a long period of time. For example, you want to retry a step that processes a payment every day until the payment is successful. - - Refer to the [Retry Failed Steps chapter](https://docs.medusajs.com/learn/fundamentals/workflows/retry-failed-steps/index.html.md) for more information. - -*** - -## Configure Long-Running Workflows - -A workflow is considered long-running if at least one step has its `async` configuration set to `true` and doesn't return a step response. - -For example, consider the following workflow and steps: - -```ts title="src/workflows/hello-world.ts" highlights={[["15"]]} collapsibleLines="1-11" expandButtonLabel="Show More" -import { - createStep, - createWorkflow, - WorkflowResponse, - StepResponse, -} from "@medusajs/framework/workflows-sdk" - -const step1 = createStep("step-1", async () => { - return new StepResponse({}) -}) - -const step2 = createStep( - { - name: "step-2", - async: true, + return new StepResponse(undefined, { ids }) }, - async () => { - console.log("Waiting to be successful...") - } -) - -const step3 = createStep("step-3", async () => { - return new StepResponse("Finished three steps") -}) - -const myWorkflow = createWorkflow( - "hello-world", - function () { - step1() - step2() - const message = step3() - - return new WorkflowResponse({ - message, - }) -}) - -export default myWorkflow -``` - -The second step has in its configuration object `async` set to `true` and it doesn't return a step response. This indicates that this step is an asynchronous step. - -So, when you execute the `hello-world` workflow, it continues its execution in the background once it reaches the second step. - -### When is a Workflow Considered Long-Running? - -A workflow is also considered long-running if: - -- One of its steps has its `async` configuration set to `true` and doesn't return a step response. -- One of its steps has its `retryInterval` option set as explained in the [Retry Failed Steps chapter](https://docs.medusajs.com/learn/fundamentals/workflows/retry-failed-steps/index.html.md). - -*** - -## Change Step Status - -Once the workflow's execution reaches an async step, it'll wait in the background for the step to succeed or fail before it moves to the next step. - -To fail or succeed a step, use the Workflow Engine Module's main service that is registered in the Medusa Container under the `Modules.WORKFLOW_ENGINE` (or `workflowsModuleService`) key. - -### Retrieve Transaction ID - -Before changing the status of a workflow execution's async step, you must have the execution's transaction ID. - -When you execute the workflow, the object returned has a `transaction` property, which is an object that holds the details of the workflow execution's transaction. Use its `transactionId` to later change async steps' statuses: - -```ts -const { transaction } = await myWorkflow(req.scope) - .run() - -// use transaction.transactionId later -``` - -### Change Step Status to Successful - -The Workflow Engine Module's main service has a `setStepSuccess` method to set a step's status to successful. If you use it on a workflow execution's async step, the workflow continues execution to the next step. - -For example, consider the following step: - -```ts highlights={successStatusHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports" -import { - Modules, - TransactionHandlerType, -} from "@medusajs/framework/utils" -import { - StepResponse, - createStep, -} from "@medusajs/framework/workflows-sdk" - -type SetStepSuccessStepInput = { - transactionId: string -}; - -export const setStepSuccessStep = createStep( - "set-step-success-step", - async function ( - { transactionId }: SetStepSuccessStepInput, - { container } - ) { - const workflowEngineService = container.resolve( - Modules.WORKFLOW_ENGINE - ) - - await workflowEngineService.setStepSuccess({ - idempotencyKey: { - action: TransactionHandlerType.INVOKE, - transactionId, - stepId: "step-2", - workflowId: "hello-world", - }, - stepResponse: new StepResponse("Done!"), - options: { - container, - }, - }) + async ({ ids }, { container }) => { + // undo the performed action } ) ``` -In this step (which you use in a workflow other than the long-running workflow), you resolve the Workflow Engine Module's main service and set `step-2` of the previous workflow as successful. +The compensation function is executed if an error occurs in the workflow to undo the actions performed by the hook handler. -The `setStepSuccess` method of the workflow engine's main service accepts as a parameter an object having the following properties: +The compensation function receives as an input the second parameter passed to the `StepResponse` returned by the step function. -- idempotencyKey: (\`object\`) The details of the workflow execution. +It also accepts as a second parameter an object holding a `container` property to resolve resources from the Medusa container. - - action: (\`invoke\` | \`compensate\`) If the step's compensation function is running, use \`compensate\`. Otherwise, use \`invoke\`. +### Additional Data Property - - transactionId: (\`string\`) The ID of the workflow execution's transaction. +Medusa's workflows pass in the hook's input an `additional_data` property: - - stepId: (\`string\`) The ID of the step to change its status. This is the first parameter passed to \`createStep\` when creating the step. +```ts title="src/workflows/hooks/product-created.ts" highlights={[["4", "additional_data"]]} +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" - - workflowId: (\`string\`) The ID of the workflow. This is the first parameter passed to \`createWorkflow\` when creating the workflow. -- stepResponse: (\`StepResponse\`) Set the response of the step. This is similar to the response you return in a step's definition, but since the \`async\` step doesn't have a response, you set its response when changing its status. -- options: (\`Record\\`) Options to pass to the step. - - - container: (\`MedusaContainer\`) An instance of the Medusa Container - -### Change Step Status to Failed - -The Workflow Engine Module's main service also has a `setStepFailure` method that changes a step's status to failed. It accepts the same parameter as `setStepSuccess`. - -After changing the async step's status to failed, the workflow execution fails and the compensation functions of previous steps are executed. - -For example: - -```ts highlights={failureStatusHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports" -import { - Modules, - TransactionHandlerType, -} from "@medusajs/framework/utils" -import { - StepResponse, - createStep, -} from "@medusajs/framework/workflows-sdk" - -type SetStepFailureStepInput = { - transactionId: string -}; - -export const setStepFailureStep = createStep( - "set-step-failure-step", - async function ( - { transactionId }: SetStepFailureStepInput, - { container } - ) { - const workflowEngineService = container.resolve( - Modules.WORKFLOW_ENGINE - ) - - await workflowEngineService.setStepFailure({ - idempotencyKey: { - action: TransactionHandlerType.INVOKE, - transactionId, - stepId: "step-2", - workflowId: "hello-world", - }, - stepResponse: new StepResponse("Failed!"), - options: { - container, - }, - }) +createProductsWorkflow.hooks.productsCreated( + async ({ products, additional_data }, { container }) => { + // TODO perform an action } ) ``` -You use this step in another workflow that changes the status of an async step in a long-running workflow's execution to failed. +This property is an object that holds additional data passed to the workflow through the request sent to the API route using the workflow. -*** +Learn how to pass `additional_data` in requests to API routes in [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md). -## Access Long-Running Workflow Status and Result +### Pass Additional Data to Workflow -To access the status and result of a long-running workflow execution, use the `subscribe` and `unsubscribe` methods of the Workflow Engine Module's main service. +You can also pass that additional data when executing the workflow. Pass it as a parameter to the `.run` method of the workflow: -To retrieve the workflow execution's details at a later point, you must enable [storing the workflow's executions](https://docs.medusajs.com/learn/fundamentals/workflows/store-executions/index.html.md). - -For example: - -```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-11" expandButtonLabel="Show Imports" +```ts title="src/workflows/hooks/product-created.ts" highlights={[["10", "additional_data"]]} import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import myWorkflow from "../../../workflows/hello-world" -import { - IWorkflowEngineService, -} from "@medusajs/framework/types" -import { Modules } from "@medusajs/framework/utils" +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" -export async function GET(req: MedusaRequest, res: MedusaResponse) { - const { transaction, result } = await myWorkflow(req.scope).run() - - const workflowEngineService = req.scope.resolve< - IWorkflowEngineService - >( - Modules.WORKFLOW_ENGINE - ) - - const subscriptionOptions = { - workflowId: "hello-world", - transactionId: transaction.transactionId, - subscriberId: "hello-world-subscriber", - } - - await workflowEngineService.subscribe({ - ...subscriptionOptions, - subscriber: async (data) => { - if (data.eventType === "onFinish") { - console.log("Finished execution", data.result) - // unsubscribe - await workflowEngineService.unsubscribe({ - ...subscriptionOptions, - subscriberOrId: subscriptionOptions.subscriberId, - }) - } else if (data.eventType === "onStepFailure") { - console.log("Workflow failed", data.step) - } +export async function POST(req: MedusaRequest, res: MedusaResponse) { + await createProductsWorkflow(req.scope).run({ + input: { + products: [ + // ... + ], + additional_data: { + custom_field: "test", + }, }, }) - - res.send(result) } ``` -In the above example, you execute the long-running workflow `hello-world` and resolve the Workflow Engine Module's main service from the Medusa container. - -### subscribe Method - -The main service's `subscribe` method allows you to listen to changes in the workflow execution’s status. It accepts an object having three properties: - -- workflowId: (\`string\`) The name of the workflow. -- transactionId: (\`string\`) The ID of the workflow exection's transaction. The transaction's details are returned in the response of the workflow execution. -- subscriberId: (\`string\`) The ID of the subscriber. -- subscriber: (\`(data: \{ eventType: string, result?: any }) => Promise\\`) The function executed when the workflow execution's status changes. The function receives a data object. It has an \`eventType\` property, which you use to check the status of the workflow execution. - -If the value of `eventType` in the `subscriber` function's first parameter is `onFinish`, the workflow finished executing. The first parameter then also has a `result` property holding the workflow's output. - -### unsubscribe Method - -You can unsubscribe from the workflow using the workflow engine's `unsubscribe` method, which requires the same object parameter as the `subscribe` method. - -However, instead of the `subscriber` property, it requires a `subscriberOrId` property whose value is the same `subscriberId` passed to the `subscribe` method. - -*** - -## Example: Restaurant-Delivery Recipe - -To find a full example of a long-running workflow, refer to the [restaurant-delivery recipe](https://docs.medusajs.com/resources/recipes/marketplace/examples/restaurant-delivery/index.html.md). - -In the recipe, you use a long-running workflow that moves an order from placed to completed. The workflow waits for the restaurant to accept the order, the driver to pick up the order, and other external actions. - - -# Execute Another Workflow - -In this chapter, you'll learn how to execute a workflow in another. - -## Execute in a Workflow - -To execute a workflow in another, use the `runAsStep` method that every workflow has. - -For example: - -```ts highlights={workflowsHighlights} collapsibleLines="1-7" expandMoreButton="Show Imports" -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -const workflow = createWorkflow( - "hello-world", - async (input) => { - const products = createProductsWorkflow.runAsStep({ - input: { - products: [ - // ... - ], - }, - }) - - // ... - } -) -``` - -Instead of invoking the workflow and passing it the container, you use its `runAsStep` method and pass it an object as a parameter. - -The object has an `input` property to pass input to the workflow. - -*** - -## Preparing Input Data - -If you need to perform some data manipulation to prepare the other workflow's input data, use `transform` from the Workflows SDK. - -Learn about transform in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md). - -For example: - -```ts highlights={transformHighlights} collapsibleLines="1-12" -import { - createWorkflow, - transform, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -type WorkflowInput = { - title: string -} - -const workflow = createWorkflow( - "hello-product", - async (input: WorkflowInput) => { - const createProductsData = transform({ - input, - }, (data) => [ - { - title: `Hello ${data.input.title}`, - }, - ]) - - const products = createProductsWorkflow.runAsStep({ - input: { - products: createProductsData, - }, - }) - - // ... - } -) -``` - -In this example, you use the `transform` function to prepend `Hello` to the title of the product. Then, you pass the result as an input to the `createProductsWorkflow`. - -*** - -## Run Workflow Conditionally - -To run a workflow in another based on a condition, use when-then from the Workflows SDK. - -Learn about when-then in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md). - -For example: - -```ts highlights={whenHighlights} collapsibleLines="1-16" -import { - createWorkflow, - when, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" -import { - CreateProductWorkflowInputDTO, -} from "@medusajs/framework/types" - -type WorkflowInput = { - product?: CreateProductWorkflowInputDTO - should_create?: boolean -} - -const workflow = createWorkflow( - "hello-product", - async (input: WorkflowInput) => { - const product = when(input, ({ should_create }) => should_create) - .then(() => { - return createProductsWorkflow.runAsStep({ - input: { - products: [input.product], - }, - }) - }) - } -) -``` - -In this example, you use when-then to run the `createProductsWorkflow` only if `should_create` (passed in the `input`) is enabled. - - -# Run Workflow Steps in Parallel - -In this chapter, you’ll learn how to run workflow steps in parallel. - -## parallelize Utility Function - -If your workflow has steps that don’t rely on one another’s results, run them in parallel using `parallelize` from the Workflows SDK. - -The workflow waits until all steps passed to the `parallelize` function finish executing before continuing to the next step. - -For example: - -```ts highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" -import { - createWorkflow, - WorkflowResponse, - parallelize, -} from "@medusajs/framework/workflows-sdk" -import { - createProductStep, - getProductStep, - createPricesStep, - attachProductToSalesChannelStep, -} from "./steps" - -interface WorkflowInput { - title: string -} - -const myWorkflow = createWorkflow( - "my-workflow", - (input: WorkflowInput) => { - const product = createProductStep(input) - - const [prices, productSalesChannel] = parallelize( - createPricesStep(product), - attachProductToSalesChannelStep(product) - ) - - const refetchedProduct = getProductStep(product.id) - - return new WorkflowResponse(refetchedProduct) - } -) -``` - -The `parallelize` function accepts the steps to run in parallel as a parameter. - -It returns an array of the steps' results in the same order they're passed to the `parallelize` function. - -So, `prices` is the result of `createPricesStep`, and `productSalesChannel` is the result of `attachProductToSalesChannelStep`. - - -# Retry Failed Steps - -In this chapter, you’ll learn how to configure steps to allow retrial on failure. - -## What is a Step Retrial? - -A step retrial is a mechanism that allows a step to be retried automatically when it fails. This is useful for handling transient errors, such as network issues or temporary unavailability of a service. - -When a step fails, the workflow engine can automatically retry the step a specified number of times before marking the workflow as failed. This can help improve the reliability and resilience of your workflows. - -You can also configure the interval between retries, allowing you to wait for a certain period before attempting the step again. This is useful when the failure is due to a temporary issue that may resolve itself after some time. - -For example, if a step captures a payment, you may want to retry it the next day until the payment is successful or the maximum number of retries is reached. - -*** - -## Configure a Step’s Retrial - -By default, when an error occurs in a step, the step and the workflow fail, and the execution stops. - -You can configure the step to retry on failure. The `createStep` function can accept a configuration object instead of the step’s name as a first parameter. - -For example: - -```ts title="src/workflows/hello-world.ts" highlights={[["10"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { - createStep, - createWorkflow, - WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" - -const step1 = createStep( - { - name: "step-1", - maxRetries: 2, - }, - async () => { - console.log("Executing step 1") - - throw new Error("Oops! Something happened.") - } -) - -const myWorkflow = createWorkflow( - "hello-world", - function () { - const str1 = step1() - - return new WorkflowResponse({ - message: str1, - }) -}) - -export default myWorkflow -``` - -The step’s configuration object accepts a `maxRetries` property, which is a number indicating the number of times a step can be retried when it fails. - -When you execute the above workflow, you’ll see the following result in the terminal: - -```bash -Executing step 1 -Executing step 1 -Executing step 1 -error: Oops! Something happened. -Error: Oops! Something happened. -``` - -The first line indicates the first time the step was executed, and the next two lines indicate the times the step was retried. After that, the step and workflow fail. - -*** - -## Step Retry Intervals - -By default, a step is retried immediately after it fails. To specify a wait time before a step is retried, pass a `retryInterval` property to the step's configuration object. Its value is a number of seconds to wait before retrying the step. - -For example: - -```ts title="src/workflows/hello-world.ts" highlights={[["5"]]} -const step1 = createStep( - { - name: "step-1", - maxRetries: 2, - retryInterval: 2, // 2 seconds - }, - async () => { - // ... - } -) -``` - -In this example, if the step fails, it will be retried after two seconds. - -### Maximum Retry Interval - -The `retryInterval` property's maximum value is [Number.MAX\_SAFE\_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER). So, you can set a very long wait time before the step is retried, allowing you to retry steps after a long period. - -For example, to retry a step after a day: - -```ts title="src/workflows/hello-world.ts" highlights={[["5"]]} -const step1 = createStep( - { - name: "step-1", - maxRetries: 2, - retryInterval: 86400, // 1 day - }, - async () => { - // ... - } -) -``` - -In this example, if the step fails, it will be retried after `86400` seconds (one day). - -### Interval Changes Workflow to Long-Running - -By setting `retryInterval` on a step, a workflow that uses that step becomes a [long-running workflow](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow/index.html.md) that runs asynchronously in the background. This is useful when creating workflows that may fail and should run for a long time until they succeed, such as waiting for a payment to be captured or a shipment to be delivered. - -However, since the long-running workflow runs in the background, you won't receive its result or errors immediately when you execute the workflow. - -Instead, you must subscribe to the workflow's execution using the Workflow Engine Module Service. Learn more about it in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow#access-long-running-workflow-status-and-result/index.html.md). - - -# Data Manipulation in Workflows with transform - -In this chapter, you'll learn how to use `transform` from the Workflows SDK to manipulate data and variables in a workflow. - -## Why Variable Manipulation isn't Allowed in Workflows - -Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. - -At that point, variables in the workflow don't have any values. They only do when you execute the workflow. - -So, you can only pass variables as parameters to steps. But, in a workflow, you can't change a variable's value or, if the variable is an array, loop over its items. - -Instead, use `transform` from the Workflows SDK. - -Restrictions for variable manipulation is only applicable in a workflow's definition. You can still manipulate variables in your step's code. - -*** - -## What is the transform Utility? - -`transform` creates a new variable as the result of manipulating other variables. - -For example, consider you have two strings as the output of two steps: - -```ts -const str1 = step1() -const str2 = step2() -``` - -To concatenate the strings, you create a new variable `str3` using the `transform` function: - -```ts highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - transform, -} from "@medusajs/framework/workflows-sdk" -// step imports... - -const myWorkflow = createWorkflow( - "hello-world", - function (input) { - const str1 = step1(input) - const str2 = step2(input) - - const str3 = transform( - { str1, str2 }, - (data) => `${data.str1}${data.str2}` - ) - - return new WorkflowResponse(str3) - } -) -``` - -`transform` accepts two parameters: - -1. The first parameter is an object of variables to manipulate. The object is passed as a parameter to `transform`'s second parameter function. -2. The second parameter is the function performing the variable manipulation. - -The value returned by the second parameter function is returned by `transform`. So, the `str3` variable holds the concatenated string. - -You can use the returned value in the rest of the workflow, either to pass it as an input to other steps or to return it in the workflow's response. - -*** - -## Example: Looping Over Array - -Use `transform` to loop over arrays to create another variable from the array's items. - -For example: - -```ts collapsibleLines="1-7" expandButtonLabel="Show Imports" -import { - createWorkflow, - WorkflowResponse, - transform, -} from "@medusajs/framework/workflows-sdk" -// step imports... - -type WorkflowInput = { - items: { - id: string - name: string - }[] -} - -const myWorkflow = createWorkflow( - "hello-world", - function ({ items }: WorkflowInput) { - const ids = transform( - { items }, - (data) => data.items.map((item) => item.id) - ) - - doSomethingStep(ids) - - // ... - } -) -``` - -This workflow receives an `items` array in its input. - -You use `transform` to create an `ids` variable, which is an array of strings holding the `id` of each item in the `items` array. - -You then pass the `ids` variable as a parameter to the `doSomethingStep`. - -*** - -## Example: Creating a Date - -If you create a date with `new Date()` in a workflow's constructor function, Medusa evaluates the date's value when it creates the internal representation of the workflow, not when the workflow is executed. - -So, use `transform` instead to create a date variable with `new Date()`. - -For example: - -```ts -const myWorkflow = createWorkflow( - "hello-world", - () => { - const today = transform({}, () => new Date()) - - doSomethingStep(today) - } -) -``` - -In this workflow, `today` is only evaluated when the workflow is executed. - -*** - -## Caveats - -### Transform Evaluation - -`transform`'s value is only evaluated if you pass its output to a step or in the workflow response. - -For example, if you have the following workflow: - -```ts -const myWorkflow = createWorkflow( - "hello-world", - function (input) { - const str = transform( - { input }, - (data) => `${data.input.str1}${data.input.str2}` - ) - - return new WorkflowResponse("done") - } -) -``` - -Since `str`'s value isn't used as a step's input or passed to `WorkflowResponse`, its value is never evaluated. - -### Data Validation - -`transform` should only be used to perform variable or data manipulation. - -If you want to perform some validation on the data, use a step or [when-then](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md) instead. - -For example: - -```ts -// DON'T -const myWorkflow = createWorkflow( - "hello-world", - function (input) { - const str = transform( - { input }, - (data) => { - if (!input.str1) { - throw new Error("Not allowed!") - } - } - ) - } -) - -// DO -const validateHasStr1Step = createStep( - "validate-has-str1", - ({ input }) => { - if (!input.str1) { - throw new Error("Not allowed!") - } - } -) - -const myWorkflow = createWorkflow( - "hello-world", - function (input) { - validateHasStr1({ - input, - }) - - // workflow continues its execution only if - // the step doesn't throw the error. - } -) -``` - - -# Store Workflow Executions - -In this chapter, you'll learn how to store workflow executions in the database and access them later. - -## Workflow Execution Retention - -Medusa doesn't store your workflow's execution details by default. However, you can configure a workflow to keep its execution details stored in the database. - -This is useful for auditing and debugging purposes. When you store a workflow's execution, you can view details around its steps, their states and their output. You can also check whether the workflow or any of its steps failed. - -You can view stored workflow executions from the Medusa Admin dashboard by going to Settings -> Workflows. - -*** - -## How to Store Workflow's Executions? - -### Prerequisites - -- [Redis Workflow Engine must be installed and configured.](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/redis/index.html.md) - -`createWorkflow` from the Workflows SDK can accept an object as a first parameter to set the workflow's configuration. To enable storing a workflow's executions: - -- Enable the `store` option. If your workflow is a [Long-Running Workflow](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow/index.html.md), this option is enabled by default. -- Set the `retentionTime` option to the number of seconds that the workflow execution should be stored in the database. - -For example: - -```ts highlights={highlights} -import { createStep, createWorkflow } from "@medusajs/framework/workflows-sdk" - -const step1 = createStep( - { - name: "step-1", - }, - async () => { - console.log("Hello from step 1") - } -) - -export const helloWorkflow = createWorkflow( - { - name: "hello-workflow", - retentionTime: 99999, - store: true, - }, - () => { - step1() - } -) -``` - -Whenever you execute the `helloWorkflow` now, its execution details will be stored in the database. - -*** - -## Retrieve Workflow Executions - -You can view stored workflow executions from the Medusa Admin dashboard by going to Settings -> Workflows. - -When you execute a workflow, the returned object has a `transaction` property containing the workflow execution's transaction details: - -```ts -const { transaction } = await helloWorkflow(container).run() -``` - -To retrieve a workflow's execution details from the database, resolve the Workflow Engine Module from the container and use its `listWorkflowExecutions` method. - -For example, you can create a `GET` API Route at `src/workflows/[id]/route.ts` that retrieves a workflow execution for the specified transaction ID: - -```ts title="src/workflows/[id]/route.ts" highlights={retrieveHighlights} -import { MedusaRequest, MedusaResponse } from "@medusajs/framework" -import { Modules } from "@medusajs/framework/utils" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { transaction_id } = req.params - - const workflowEngineService = req.scope.resolve( - Modules.WORKFLOW_ENGINE - ) - - const [workflowExecution] = await workflowEngineService.listWorkflowExecutions({ - transaction_id: transaction_id, - }) - - res.json({ - workflowExecution, - }) -} -``` - -In the above example, you resolve the Workflow Engine Module from the container and use its `listWorkflowExecutions` method, passing the `transaction_id` as a filter to retrieve its workflow execution details. - -A workflow execution object will be similar to the following: - -```json -{ - "workflow_id": "hello-workflow", - "transaction_id": "01JJC2T6AVJCQ3N4BRD1EB88SP", - "id": "wf_exec_01JJC2T6B3P76JD35F12QTTA78", - "execution": { - "state": "done", - "steps": {}, - "modelId": "hello-workflow", - "options": {}, - "metadata": {}, - "startedAt": 1737719880027, - "definition": {}, - "timedOutAt": null, - "hasAsyncSteps": false, - "transactionId": "01JJC2T6AVJCQ3N4BRD1EB88SP", - "hasFailedSteps": false, - "hasSkippedSteps": false, - "hasWaitingSteps": false, - "hasRevertedSteps": false, - "hasSkippedOnFailureSteps": false - }, - "context": { - "data": {}, - "errors": [] - }, - "state": "done", - "created_at": "2025-01-24T09:58:00.036Z", - "updated_at": "2025-01-24T09:58:00.046Z", - "deleted_at": null -} -``` - -### Example: Check if Stored Workflow Execution Failed - -To check if a stored workflow execution failed, you can check its `state` property: - -```ts -if (workflowExecution.state === "failed") { - return res.status(500).json({ - error: "Workflow failed", - }) -} -``` - -Other state values include `done`, `invoking`, and `compensating`. +Your hook handler then receives that passed data in the `additional_data` object. # Workflow Timeout @@ -17224,394 +17233,6 @@ This step's executions fail if they run longer than two seconds. A step’s timeout error is returned in the `errors` property of the workflow’s execution, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/errors/index.html.md). The error’s name is `TransactionStepTimeoutError`. -# Scheduled Jobs Number of Executions - -In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed. - -## numberOfExecutions Option - -The export configuration object of the scheduled job accepts an optional property `numberOfExecutions`. Its value is a number indicating how many times the scheduled job can be executed during the Medusa application's runtime. - -For example: - -```ts highlights={highlights} -export default async function myCustomJob() { - console.log("I'll be executed three times only.") -} - -export const config = { - name: "hello-world", - // execute every minute - schedule: "* * * * *", - numberOfExecutions: 3, -} -``` - -The above scheduled job has the `numberOfExecutions` configuration set to `3`. - -So, it'll only execute 3 times, each every minute, then it won't be executed anymore. - -If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified. - - -# Translate Medusa Admin - -The Medusa Admin supports multiple languages, with the default being English. In this documentation, you'll learn how to contribute to the community by translating the Medusa Admin to a language you're fluent in. - -{/* vale docs.We = NO */} - -You can contribute either by translating the admin to a new language, or fixing translations for existing languages. As we can't validate every language's translations, some translations may be incorrect. Your contribution is welcome to fix any translation errors you find. - -{/* vale docs.We = YES */} - -Check out the translated languages either in the admin dashboard's settings or on [GitHub](https://github.com/medusajs/medusa/blob/develop/packages/admin/dashboard/src/i18n/languages.ts). - -*** - -## How to Contribute Translation - -1. Clone the [Medusa monorepository](https://github.com/medusajs/medusa) to your local machine: - -```bash -git clone https://github.com/medusajs/medusa.git -``` - -If you already have it cloned, make sure to pull the latest changes from the `develop` branch. - -2. Install the monorepository's dependencies. Since it's a Yarn workspace, it's highly recommended to use yarn: - -```bash -yarn install -``` - -3. Create a branch that you'll use to open the pull request later: - -```bash -git checkout -b feat/translate- -``` - -Where `` is your language name. For example, `feat/translate-da`. - -4. Translation files are under `packages/admin/dashboard/src/i18n/translations` as JSON files whose names are the ISO-2 name of the language. - - If you're adding a new language, copy the file `packages/admin/dashboard/src/i18n/translations/en.json` and paste it with the ISO-2 name for your language. For example, if you're adding Danish translations, copy the `en.json` file and paste it as `packages/admin/dashboard/src/i18n/translations/de.json`. - - If you're fixing a translation, find the JSON file of the language under `packages/admin/dashboard/src/i18n/translations`. - -5. Start translating the keys in the JSON file (or updating the targeted ones). All keys in the JSON file must be translated, and your PR tests will fail otherwise. - - You can check whether the JSON file is valid by running the following command in `packages/admin/dashboard`, replacing `da.json` with the JSON file's name: - -```bash title="packages/admin/dashboard" -yarn i18n:validate da.json -``` - -6. After finishing the translation, if you're adding a new language, import its JSON file in `packages/admin/dashboard/src/i18n/translations/index.ts` and add it to the exported object: - -```ts title="packages/admin/dashboard/src/i18n/translations/index.ts" highlights={[["2"], ["6"], ["7"], ["8"]]} -// other imports... -import da from "./da.json" - -export default { - // other languages... - da: { - translation: da, - }, -} -``` - -The language's key in the object is the ISO-2 name of the language. - -7. If you're adding a new language, add it to the file `packages/admin/dashboard/src/i18n/languages.ts`: - -```ts title="packages/admin/dashboard/src/i18n/languages.ts" highlights={languageHighlights} -import { da } from "date-fns/locale" -// other imports... - -export const languages: Language[] = [ - // other languages... - { - code: "da", - display_name: "Danish", - ltr: true, - date_locale: da, - }, -] -``` - -`languages` is an array having the following properties: - -- `code`: The ISO-2 name of the language. For example, `da` for Danish. -- `display_name`: The language's name to be displayed in the admin. -- `ltr`: Whether the language supports a left-to-right layout. For example, set this to `false` for languages like Arabic. -- `date_locale`: An instance of the locale imported from the [date-fns/locale](https://date-fns.org/) package. - -8. Once you're done, push the changes into your branch and open a pull request on GitHub. - -Our team will perform a general review on your PR and merge it if no issues are found. The translation will be available in the admin after the next release. - - -# Docs Contribution Guidelines - -Thank you for your interest in contributing to the documentation! You will be helping the open source community and other developers interested in learning more about Medusa and using it. - -This guide is specific to contributing to the documentation. If you’re interested in contributing to Medusa’s codebase, check out the [contributing guidelines in the Medusa GitHub repository](https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md). - -## What Can You Contribute? - -You can contribute to the Medusa documentation in the following ways: - -- Fixes to existing content. This includes small fixes like typos, or adding missing information. -- Additions to the documentation. If you think a documentation page can be useful to other developers, you can contribute by adding it. - - Make sure to open an issue first in the [medusa repository](https://github.com/medusajs/medusa) to confirm that you can add that documentation page. -- Fixes to UI components and tooling. If you find a bug while browsing the documentation, you can contribute by fixing it. - -*** - -## Documentation Workspace - -Medusa's documentation projects are all part of the documentation yarn workspace, which you can find in the [medusa repository](https://github.com/medusajs/medusa) under the `www` directory. - -The workspace has the following two directories: - -- `apps`: this directory holds the different documentation websites and projects. - - `book`: includes the codebase for the [main Medusa documentation](https://docs.medusajs.com//index.html.md). It's built with [Next.js 15](https://nextjs.org/). - - `resources`: includes the codebase for the resources documentation, which powers different sections of the docs such as the [Integrations](https://docs.medusajs.com/resources/integrations/index.html.md) or [How-to & Tutorials](https://docs.medusajs.com/resources/how-to-tutorials/index.html.md) sections. It's built with [Next.js 15](https://nextjs.org/). - - `api-reference`: includes the codebase for the API reference website. It's built with [Next.js 15](https://nextjs.org/). - - `ui`: includes the codebase for the Medusa UI documentation website. It's built with [Next.js 15](https://nextjs.org/). -- `packages`: this directory holds the shared packages and components necessary for the development of the projects in the `apps` directory. - - `docs-ui` includes the shared React components between the different apps. - - `remark-rehype-plugins` includes Remark and Rehype plugins used by the documentation projects. - -*** - -## Documentation Content - -All documentation projects are built with Next.js. The content is writtin in MDX files. - -### Medusa Main Docs Content - -The content of the Medusa main docs are under the `www/apps/book/app` directory. - -### Medusa Resources Content - -The content of all pages under the `/resources` path are under the `www/apps/resources/app` directory. - -Documentation pages under the `www/apps/resources/references` directory are generated automatically from the source code under the `packages/medusa` directory. So, you can't directly make changes to them. Instead, you'll have to make changes to the comments in the original source code. - -### API Reference - -The API reference's content is split into two types: - -1. Static content, which are the content related to getting started, expanding fields, and more. These are located in the `www/apps/api-reference/markdown` directory. They are MDX files. -2. OpenAPI specs that are shown to developers when checking the reference of an API Route. These are generated from OpenApi Spec comments, which are under the `www/utils/generated/oas-output` directory. - -### Medusa UI Documentation - -The content of the Medusa UI documentation are located under the `www/apps/ui/src/content/docs` directory. They are MDX files. - -The UI documentation also shows code examples, which are under the `www/apps/ui/src/examples` directory. - -The UI component props are generated from the source code and placed into the `www/apps/ui/src/specs` directory. To contribute to these props and their comments, check the comments in the source code under the `packages/design-system/ui` directory. - -*** - -## Style Guide - -When you contribute to the documentation content, make sure to follow the [documentation style guide](https://www.notion.so/Style-Guide-Docs-fad86dd1c5f84b48b145e959f36628e0). - -*** - -## How to Contribute - -If you’re fixing errors in an existing documentation page, you can scroll down to the end of the page and click on the “Edit this page” link. You’ll be redirected to the GitHub edit form of that page and you can make edits directly and submit a pull request (PR). - -If you’re adding a new page or contributing to the codebase, fork the repository, create a new branch, and make all changes necessary in your repository. Then, once you’re done, create a PR in the Medusa repository. - -### Base Branch - -When you make an edit to an existing documentation page or fork the repository to make changes to the documentation, create a new branch. - -Documentation contributions always use `develop` as the base branch. Make sure to also open your PR against the `develop` branch. - -### Branch Name - -Make sure that the branch name starts with `docs/`. For example, `docs/fix-services`. Vercel deployed previews are only triggered for branches starting with `docs/`. - -### Pull Request Conventions - -When you create a pull request, prefix the title with `docs:` or `docs(PROJECT_NAME):`, where `PROJECT_NAME` is the name of the documentation project this pull request pertains to. For example, `docs(ui): fix titles`. - -In the body of the PR, explain clearly what the PR does. If the PR solves an issue, use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) with the issue number. For example, “Closes #1333”. - -*** - -## Images - -If you are adding images to a documentation page, you can host the image on [Imgur](https://imgur.com) for free to include it in the PR. Our team will later upload it to our image hosting. - -*** - -## NPM and Yarn Code Blocks - -If you’re adding code blocks that use NPM and Yarn, you must add the `npm2yarn` meta field. - -For example: - -````md -```bash npm2yarn -npm run start -``` -```` - -The code snippet must be written using NPM. - -### Global Option - -When a command uses the global option `-g`, add it at the end of the NPM command to ensure that it’s transformed to a Yarn command properly. For example: - -```bash npm2yarn -npm install @medusajs/cli -g -``` - -*** - -## Linting with Vale - -Medusa uses [Vale](https://vale.sh/) to lint documentation pages and perform checks on incoming PRs into the repository. - -### Result of Vale PR Checks - -You can check the result of running the "lint" action on your PR by clicking the Details link next to it. You can find there all errors that you need to fix. - -### Run Vale Locally - -If you want to check your work locally, you can do that by: - -1. [Installing Vale](https://vale.sh/docs/vale-cli/installation/) on your machine. -2. Changing to the `www/vale` directory: - -```bash -cd www/vale -``` - -3\. Running the `run-vale` script: - -```bash -# to lint content for the main documentation -./run-vale.sh book/app/learn error resources -# to lint content for the resources documentation -./run-vale.sh resources/app error -# to lint content for the API reference -./run-vale.sh api-reference/markdown error -# to lint content for the Medusa UI documentation -./run-vale.sh ui/src/content/docs error -# to lint content for the user guide -./run-vale.sh user-guide/app error -``` - -{/* TODO need to enable MDX v1 comments first. */} - -{/* ### Linter Exceptions - -If it's needed to break some style guide rules in a document, you can wrap the parts that the linter shouldn't scan with the following comments in the `md` or `mdx` files: - -```md - - -content that shouldn't be scanned for errors here... - - -``` - -You can also disable specific rules. For example: - -```md - - -Medusa supports Node versions 14 and 16. - - -``` - -If you use this in your PR, you must justify its usage. */} - -*** - -## Linting with ESLint - -Medusa uses ESlint to lint code blocks both in the content and the code base of the documentation apps. - -### Linting Content with ESLint - -Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `content-eslint`. - -If you want to check content ESLint errors locally and fix them, you can do that by: - -1\. Install the dependencies in the `www` directory: - -```bash -yarn install -``` - -2\. Run the turbo command in the `www` directory: - -```bash -turbo run lint:content -``` - -This will fix any fixable errors, and show errors that require your action. - -### Linting Code with ESLint - -Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `code-docs-eslint`. - -If you want to check code ESLint errors locally and fix them, you can do that by: - -1\. Install the dependencies in the `www` directory: - -```bash -yarn install -``` - -2\. Run the turbo command in the `www` directory: - -```bash -yarn lint -``` - -This will fix any fixable errors, and show errors that require your action. - -{/* TODO need to enable MDX v1 comments first. */} - -{/* ### ESLint Exceptions - -If some code blocks have errors that can't or shouldn't be fixed, you can add the following command before the code block: - -~~~md - - -```js -console.log("This block isn't linted") -``` - -```js -console.log("This block is linted") -``` -~~~ - -You can also disable specific rules. For example: - -~~~md - - -```js -console.log("This block can use semicolons"); -``` - -```js -console.log("This block can't use semi colons") -``` -~~~ */} - - # Example: Write Integration Tests for API Routes In this chapter, you'll learn how to write integration tests for API routes using [medusaIntegrationTestRunner](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/integration-tests/index.html.md) from Medusa's Testing Framework. @@ -18181,130 +17802,6 @@ const response = await api.post(`/custom`, form, { ``` -# Workflow Hooks - -In this chapter, you'll learn what a workflow hook is and how to consume them. - -## What is a Workflow Hook? - -A workflow hook is a point in a workflow where you can inject custom functionality as a step function, called a hook handler. - -Medusa exposes hooks in many of its workflows that are used in its API routes. You can consume those hooks to add your custom logic. - -Refer to the [Workflows Reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md) to view all workflows and their hooks. - -You want to perform a custom action during a workflow's execution, such as when a product is created. - -*** - -## How to Consume a Hook? - -A workflow has a special `hooks` property which is an object that holds its hooks. - -So, in a TypeScript or JavaScript file created under the `src/workflows/hooks` directory: - -- Import the workflow. -- Access its hook using the `hooks` property. -- Pass the hook a step function as a parameter to consume it. - -For example, to consume the `productsCreated` hook of Medusa's `createProductsWorkflow`, create the file `src/workflows/hooks/product-created.ts` with the following content: - -```ts title="src/workflows/hooks/product-created.ts" highlights={handlerHighlights} -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" - -createProductsWorkflow.hooks.productsCreated( - async ({ products }, { container }) => { - // TODO perform an action - } -) -``` - -The `productsCreated` hook is available on the workflow's `hooks` property by its name. - -You invoke the hook, passing a step function (the hook handler) as a parameter. - -Now, when a product is created using the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), your hook handler is executed after the product is created. - -A hook can have only one handler. - -Refer to the [createProductsWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) to see at which point the hook handler is executed. - -### Hook Handler Parameter - -Since a hook handler is essentially a step function, it receives the hook's input as a first parameter, and an object holding a `container` property as a second parameter. - -Each hook has different input. For example, the `productsCreated` hook receives an object having a `products` property holding the created product. - -### Hook Handler Compensation - -Since the hook handler is a step function, you can set its compensation function as a second parameter of the hook. - -For example: - -```ts title="src/workflows/hooks/product-created.ts" -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" - -createProductsWorkflow.hooks.productsCreated( - async ({ products }, { container }) => { - // TODO perform an action - - return new StepResponse(undefined, { ids }) - }, - async ({ ids }, { container }) => { - // undo the performed action - } -) -``` - -The compensation function is executed if an error occurs in the workflow to undo the actions performed by the hook handler. - -The compensation function receives as an input the second parameter passed to the `StepResponse` returned by the step function. - -It also accepts as a second parameter an object holding a `container` property to resolve resources from the Medusa container. - -### Additional Data Property - -Medusa's workflows pass in the hook's input an `additional_data` property: - -```ts title="src/workflows/hooks/product-created.ts" highlights={[["4", "additional_data"]]} -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" - -createProductsWorkflow.hooks.productsCreated( - async ({ products, additional_data }, { container }) => { - // TODO perform an action - } -) -``` - -This property is an object that holds additional data passed to the workflow through the request sent to the API route using the workflow. - -Learn how to pass `additional_data` in requests to API routes in [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md). - -### Pass Additional Data to Workflow - -You can also pass that additional data when executing the workflow. Pass it as a parameter to the `.run` method of the workflow: - -```ts title="src/workflows/hooks/product-created.ts" highlights={[["10", "additional_data"]]} -import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" - -export async function POST(req: MedusaRequest, res: MedusaResponse) { - await createProductsWorkflow(req.scope).run({ - input: { - products: [ - // ... - ], - additional_data: { - custom_field: "test", - }, - }, - }) -} -``` - -Your hook handler then receives that passed data in the `additional_data` object. - - # Example: Write Integration Tests for Workflows In this chapter, you'll learn how to write integration tests for workflows using [medusaIntegrationTestRunner](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/integration-tests/index.html.md) from Medusa's Testing Framwork. @@ -18436,6 +17933,270 @@ The `errors` property contains an array of errors thrown during the execution of If you threw a `MedusaError`, then you can check the error message in `errors[0].error.message`. +# Docs Contribution Guidelines + +Thank you for your interest in contributing to the documentation! You will be helping the open source community and other developers interested in learning more about Medusa and using it. + +This guide is specific to contributing to the documentation. If you’re interested in contributing to Medusa’s codebase, check out the [contributing guidelines in the Medusa GitHub repository](https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md). + +## What Can You Contribute? + +You can contribute to the Medusa documentation in the following ways: + +- Fixes to existing content. This includes small fixes like typos, or adding missing information. +- Additions to the documentation. If you think a documentation page can be useful to other developers, you can contribute by adding it. + - Make sure to open an issue first in the [medusa repository](https://github.com/medusajs/medusa) to confirm that you can add that documentation page. +- Fixes to UI components and tooling. If you find a bug while browsing the documentation, you can contribute by fixing it. + +*** + +## Documentation Workspace + +Medusa's documentation projects are all part of the documentation yarn workspace, which you can find in the [medusa repository](https://github.com/medusajs/medusa) under the `www` directory. + +The workspace has the following two directories: + +- `apps`: this directory holds the different documentation websites and projects. + - `book`: includes the codebase for the [main Medusa documentation](https://docs.medusajs.com//index.html.md). It's built with [Next.js 15](https://nextjs.org/). + - `resources`: includes the codebase for the resources documentation, which powers different sections of the docs such as the [Integrations](https://docs.medusajs.com/resources/integrations/index.html.md) or [How-to & Tutorials](https://docs.medusajs.com/resources/how-to-tutorials/index.html.md) sections. It's built with [Next.js 15](https://nextjs.org/). + - `api-reference`: includes the codebase for the API reference website. It's built with [Next.js 15](https://nextjs.org/). + - `ui`: includes the codebase for the Medusa UI documentation website. It's built with [Next.js 15](https://nextjs.org/). +- `packages`: this directory holds the shared packages and components necessary for the development of the projects in the `apps` directory. + - `docs-ui` includes the shared React components between the different apps. + - `remark-rehype-plugins` includes Remark and Rehype plugins used by the documentation projects. + +*** + +## Documentation Content + +All documentation projects are built with Next.js. The content is writtin in MDX files. + +### Medusa Main Docs Content + +The content of the Medusa main docs are under the `www/apps/book/app` directory. + +### Medusa Resources Content + +The content of all pages under the `/resources` path are under the `www/apps/resources/app` directory. + +Documentation pages under the `www/apps/resources/references` directory are generated automatically from the source code under the `packages/medusa` directory. So, you can't directly make changes to them. Instead, you'll have to make changes to the comments in the original source code. + +### API Reference + +The API reference's content is split into two types: + +1. Static content, which are the content related to getting started, expanding fields, and more. These are located in the `www/apps/api-reference/markdown` directory. They are MDX files. +2. OpenAPI specs that are shown to developers when checking the reference of an API Route. These are generated from OpenApi Spec comments, which are under the `www/utils/generated/oas-output` directory. + +### Medusa UI Documentation + +The content of the Medusa UI documentation are located under the `www/apps/ui/src/content/docs` directory. They are MDX files. + +The UI documentation also shows code examples, which are under the `www/apps/ui/src/examples` directory. + +The UI component props are generated from the source code and placed into the `www/apps/ui/src/specs` directory. To contribute to these props and their comments, check the comments in the source code under the `packages/design-system/ui` directory. + +*** + +## Style Guide + +When you contribute to the documentation content, make sure to follow the [documentation style guide](https://www.notion.so/Style-Guide-Docs-fad86dd1c5f84b48b145e959f36628e0). + +*** + +## How to Contribute + +If you’re fixing errors in an existing documentation page, you can scroll down to the end of the page and click on the “Edit this page” link. You’ll be redirected to the GitHub edit form of that page and you can make edits directly and submit a pull request (PR). + +If you’re adding a new page or contributing to the codebase, fork the repository, create a new branch, and make all changes necessary in your repository. Then, once you’re done, create a PR in the Medusa repository. + +### Base Branch + +When you make an edit to an existing documentation page or fork the repository to make changes to the documentation, create a new branch. + +Documentation contributions always use `develop` as the base branch. Make sure to also open your PR against the `develop` branch. + +### Branch Name + +Make sure that the branch name starts with `docs/`. For example, `docs/fix-services`. Vercel deployed previews are only triggered for branches starting with `docs/`. + +### Pull Request Conventions + +When you create a pull request, prefix the title with `docs:` or `docs(PROJECT_NAME):`, where `PROJECT_NAME` is the name of the documentation project this pull request pertains to. For example, `docs(ui): fix titles`. + +In the body of the PR, explain clearly what the PR does. If the PR solves an issue, use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) with the issue number. For example, “Closes #1333”. + +*** + +## Images + +If you are adding images to a documentation page, you can host the image on [Imgur](https://imgur.com) for free to include it in the PR. Our team will later upload it to our image hosting. + +*** + +## NPM and Yarn Code Blocks + +If you’re adding code blocks that use NPM and Yarn, you must add the `npm2yarn` meta field. + +For example: + +````md +```bash npm2yarn +npm run start +``` +```` + +The code snippet must be written using NPM. + +### Global Option + +When a command uses the global option `-g`, add it at the end of the NPM command to ensure that it’s transformed to a Yarn command properly. For example: + +```bash npm2yarn +npm install @medusajs/cli -g +``` + +*** + +## Linting with Vale + +Medusa uses [Vale](https://vale.sh/) to lint documentation pages and perform checks on incoming PRs into the repository. + +### Result of Vale PR Checks + +You can check the result of running the "lint" action on your PR by clicking the Details link next to it. You can find there all errors that you need to fix. + +### Run Vale Locally + +If you want to check your work locally, you can do that by: + +1. [Installing Vale](https://vale.sh/docs/vale-cli/installation/) on your machine. +2. Changing to the `www/vale` directory: + +```bash +cd www/vale +``` + +3\. Running the `run-vale` script: + +```bash +# to lint content for the main documentation +./run-vale.sh book/app/learn error resources +# to lint content for the resources documentation +./run-vale.sh resources/app error +# to lint content for the API reference +./run-vale.sh api-reference/markdown error +# to lint content for the Medusa UI documentation +./run-vale.sh ui/src/content/docs error +# to lint content for the user guide +./run-vale.sh user-guide/app error +``` + +{/* TODO need to enable MDX v1 comments first. */} + +{/* ### Linter Exceptions + +If it's needed to break some style guide rules in a document, you can wrap the parts that the linter shouldn't scan with the following comments in the `md` or `mdx` files: + +```md + + +content that shouldn't be scanned for errors here... + + +``` + +You can also disable specific rules. For example: + +```md + + +Medusa supports Node versions 14 and 16. + + +``` + +If you use this in your PR, you must justify its usage. */} + +*** + +## Linting with ESLint + +Medusa uses ESlint to lint code blocks both in the content and the code base of the documentation apps. + +### Linting Content with ESLint + +Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `content-eslint`. + +If you want to check content ESLint errors locally and fix them, you can do that by: + +1\. Install the dependencies in the `www` directory: + +```bash +yarn install +``` + +2\. Run the turbo command in the `www` directory: + +```bash +turbo run lint:content +``` + +This will fix any fixable errors, and show errors that require your action. + +### Linting Code with ESLint + +Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `code-docs-eslint`. + +If you want to check code ESLint errors locally and fix them, you can do that by: + +1\. Install the dependencies in the `www` directory: + +```bash +yarn install +``` + +2\. Run the turbo command in the `www` directory: + +```bash +yarn lint +``` + +This will fix any fixable errors, and show errors that require your action. + +{/* TODO need to enable MDX v1 comments first. */} + +{/* ### ESLint Exceptions + +If some code blocks have errors that can't or shouldn't be fixed, you can add the following command before the code block: + +~~~md + + +```js +console.log("This block isn't linted") +``` + +```js +console.log("This block is linted") +``` +~~~ + +You can also disable specific rules. For example: + +~~~md + + +```js +console.log("This block can use semicolons"); +``` + +```js +console.log("This block can't use semi colons") +``` +~~~ */} + + # Example: Integration Tests for a Module In this chapter, find an example of writing an integration test for a module using [moduleIntegrationTestRunner](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/modules-tests/index.html.md) from Medusa's Testing Framework. @@ -18506,6 +18267,245 @@ If you don't have a `test:integration:modules` script in `package.json`, refer t This runs your Medusa application and runs the tests available in any `__tests__` directory under the `src/modules` directory. +# Store Workflow Executions + +In this chapter, you'll learn how to store workflow executions in the database and access them later. + +## Workflow Execution Retention + +Medusa doesn't store your workflow's execution details by default. However, you can configure a workflow to keep its execution details stored in the database. + +This is useful for auditing and debugging purposes. When you store a workflow's execution, you can view details around its steps, their states and their output. You can also check whether the workflow or any of its steps failed. + +You can view stored workflow executions from the Medusa Admin dashboard by going to Settings -> Workflows. + +*** + +## How to Store Workflow's Executions? + +### Prerequisites + +- [Redis Workflow Engine must be installed and configured.](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/redis/index.html.md) + +`createWorkflow` from the Workflows SDK can accept an object as a first parameter to set the workflow's configuration. To enable storing a workflow's executions: + +- Enable the `store` option. If your workflow is a [Long-Running Workflow](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow/index.html.md), this option is enabled by default. +- Set the `retentionTime` option to the number of seconds that the workflow execution should be stored in the database. + +For example: + +```ts highlights={highlights} +import { createStep, createWorkflow } from "@medusajs/framework/workflows-sdk" + +const step1 = createStep( + { + name: "step-1", + }, + async () => { + console.log("Hello from step 1") + } +) + +export const helloWorkflow = createWorkflow( + { + name: "hello-workflow", + retentionTime: 99999, + store: true, + }, + () => { + step1() + } +) +``` + +Whenever you execute the `helloWorkflow` now, its execution details will be stored in the database. + +*** + +## Retrieve Workflow Executions + +You can view stored workflow executions from the Medusa Admin dashboard by going to Settings -> Workflows. + +When you execute a workflow, the returned object has a `transaction` property containing the workflow execution's transaction details: + +```ts +const { transaction } = await helloWorkflow(container).run() +``` + +To retrieve a workflow's execution details from the database, resolve the Workflow Engine Module from the container and use its `listWorkflowExecutions` method. + +For example, you can create a `GET` API Route at `src/workflows/[id]/route.ts` that retrieves a workflow execution for the specified transaction ID: + +```ts title="src/workflows/[id]/route.ts" highlights={retrieveHighlights} +import { MedusaRequest, MedusaResponse } from "@medusajs/framework" +import { Modules } from "@medusajs/framework/utils" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { transaction_id } = req.params + + const workflowEngineService = req.scope.resolve( + Modules.WORKFLOW_ENGINE + ) + + const [workflowExecution] = await workflowEngineService.listWorkflowExecutions({ + transaction_id: transaction_id, + }) + + res.json({ + workflowExecution, + }) +} +``` + +In the above example, you resolve the Workflow Engine Module from the container and use its `listWorkflowExecutions` method, passing the `transaction_id` as a filter to retrieve its workflow execution details. + +A workflow execution object will be similar to the following: + +```json +{ + "workflow_id": "hello-workflow", + "transaction_id": "01JJC2T6AVJCQ3N4BRD1EB88SP", + "id": "wf_exec_01JJC2T6B3P76JD35F12QTTA78", + "execution": { + "state": "done", + "steps": {}, + "modelId": "hello-workflow", + "options": {}, + "metadata": {}, + "startedAt": 1737719880027, + "definition": {}, + "timedOutAt": null, + "hasAsyncSteps": false, + "transactionId": "01JJC2T6AVJCQ3N4BRD1EB88SP", + "hasFailedSteps": false, + "hasSkippedSteps": false, + "hasWaitingSteps": false, + "hasRevertedSteps": false, + "hasSkippedOnFailureSteps": false + }, + "context": { + "data": {}, + "errors": [] + }, + "state": "done", + "created_at": "2025-01-24T09:58:00.036Z", + "updated_at": "2025-01-24T09:58:00.046Z", + "deleted_at": null +} +``` + +### Example: Check if Stored Workflow Execution Failed + +To check if a stored workflow execution failed, you can check its `state` property: + +```ts +if (workflowExecution.state === "failed") { + return res.status(500).json({ + error: "Workflow failed", + }) +} +``` + +Other state values include `done`, `invoking`, and `compensating`. + + +# Translate Medusa Admin + +The Medusa Admin supports multiple languages, with the default being English. In this documentation, you'll learn how to contribute to the community by translating the Medusa Admin to a language you're fluent in. + +{/* vale docs.We = NO */} + +You can contribute either by translating the admin to a new language, or fixing translations for existing languages. As we can't validate every language's translations, some translations may be incorrect. Your contribution is welcome to fix any translation errors you find. + +{/* vale docs.We = YES */} + +Check out the translated languages either in the admin dashboard's settings or on [GitHub](https://github.com/medusajs/medusa/blob/develop/packages/admin/dashboard/src/i18n/languages.ts). + +*** + +## How to Contribute Translation + +1. Clone the [Medusa monorepository](https://github.com/medusajs/medusa) to your local machine: + +```bash +git clone https://github.com/medusajs/medusa.git +``` + +If you already have it cloned, make sure to pull the latest changes from the `develop` branch. + +2. Install the monorepository's dependencies. Since it's a Yarn workspace, it's highly recommended to use yarn: + +```bash +yarn install +``` + +3. Create a branch that you'll use to open the pull request later: + +```bash +git checkout -b feat/translate- +``` + +Where `` is your language name. For example, `feat/translate-da`. + +4. Translation files are under `packages/admin/dashboard/src/i18n/translations` as JSON files whose names are the ISO-2 name of the language. + - If you're adding a new language, copy the file `packages/admin/dashboard/src/i18n/translations/en.json` and paste it with the ISO-2 name for your language. For example, if you're adding Danish translations, copy the `en.json` file and paste it as `packages/admin/dashboard/src/i18n/translations/de.json`. + - If you're fixing a translation, find the JSON file of the language under `packages/admin/dashboard/src/i18n/translations`. + +5. Start translating the keys in the JSON file (or updating the targeted ones). All keys in the JSON file must be translated, and your PR tests will fail otherwise. + - You can check whether the JSON file is valid by running the following command in `packages/admin/dashboard`, replacing `da.json` with the JSON file's name: + +```bash title="packages/admin/dashboard" +yarn i18n:validate da.json +``` + +6. After finishing the translation, if you're adding a new language, import its JSON file in `packages/admin/dashboard/src/i18n/translations/index.ts` and add it to the exported object: + +```ts title="packages/admin/dashboard/src/i18n/translations/index.ts" highlights={[["2"], ["6"], ["7"], ["8"]]} +// other imports... +import da from "./da.json" + +export default { + // other languages... + da: { + translation: da, + }, +} +``` + +The language's key in the object is the ISO-2 name of the language. + +7. If you're adding a new language, add it to the file `packages/admin/dashboard/src/i18n/languages.ts`: + +```ts title="packages/admin/dashboard/src/i18n/languages.ts" highlights={languageHighlights} +import { da } from "date-fns/locale" +// other imports... + +export const languages: Language[] = [ + // other languages... + { + code: "da", + display_name: "Danish", + ltr: true, + date_locale: da, + }, +] +``` + +`languages` is an array having the following properties: + +- `code`: The ISO-2 name of the language. For example, `da` for Danish. +- `display_name`: The language's name to be displayed in the admin. +- `ltr`: Whether the language supports a left-to-right layout. For example, set this to `false` for languages like Arabic. +- `date_locale`: An instance of the locale imported from the [date-fns/locale](https://date-fns.org/) package. + +8. Once you're done, push the changes into your branch and open a pull request on GitHub. + +Our team will perform a general review on your PR and merge it if no issues are found. The translation will be available in the admin after the next release. + + # Commerce Modules In this section of the documentation, you'll find guides and references related to Medusa's Commerce Modules. @@ -18686,6 +18686,136 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** +# Auth Module + +In this section of the documentation, you will find resources to learn more about the Auth Module and how to use it in your application. + +Medusa has auth related features available out-of-the-box through the Auth Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Auth Module. + +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). + +## Auth Features + +- [Basic User Authentication](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#1-basic-authentication-flow/index.html.md): Authenticate users using their email and password credentials. +- [Third-Party and Social Authentication](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#2-third-party-service-authenticate-flow/index.html.md): Authenticate users using third-party services and social platforms, such as [Google](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/google/index.html.md) and [GitHub](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/github/index.html.md). +- [Authenticate Custom Actor Types](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md): Create custom user or actor types, such as managers, authenticate them in your application, and guard routes based on the custom user types. +- [Custom Authentication Providers](https://docs.medusajs.com/references/auth/provider/index.html.md): Integrate third-party services with custom authentication providors. + +*** + +## How to Use the Auth Module + +In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. + +You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. + +For example: + +```ts title="src/workflows/authenticate-user.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { Modules, MedusaError } from "@medusajs/framework/utils" +import { MedusaRequest } from "@medusajs/framework/http" +import { AuthenticationInput } from "@medusajs/framework/types" + +type Input = { + req: MedusaRequest +} + +const authenticateUserStep = createStep( + "authenticate-user", + async ({ req }: Input, { container }) => { + const authModuleService = container.resolve(Modules.AUTH) + + const { success, authIdentity, error } = await authModuleService + .authenticate( + "emailpass", + { + url: req.url, + headers: req.headers, + query: req.query, + body: req.body, + authScope: "admin", // or custom actor type + protocol: req.protocol, + } as AuthenticationInput + ) + + if (!success) { + // incorrect authentication details + throw new MedusaError( + MedusaError.Types.UNAUTHORIZED, + error || "Incorrect authentication details" + ) + } + + return new StepResponse({ authIdentity }, authIdentity?.id) + }, + async (authIdentityId, { container }) => { + if (!authIdentityId) { + return + } + + const authModuleService = container.resolve(Modules.AUTH) + + await authModuleService.deleteAuthIdentities([authIdentityId]) + } +) + +export const authenticateUserWorkflow = createWorkflow( + "authenticate-user", + (input: Input) => { + const { authIdentity } = authenticateUserStep(input) + + return new WorkflowResponse({ + authIdentity, + }) + } +) +``` + +You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: + +```ts title="API Route" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { authenticateUserWorkflow } from "../../workflows/authenticate-user" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await authenticateUserWorkflow(req.scope) + .run({ + req, + }) + + res.send(result) +} +``` + +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +*** + +## Configure Auth Module + +The Auth Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/module-options/index.html.md) for details on the module's options. + +*** + +## Providers + +Medusa provides the following authentication providers out-of-the-box. You can use them to authenticate admin users, customers, or custom actor types. + +*** + + # Customer Module In this section of the documentation, you will find resources to learn more about the Customer Module and how to use it in your application. @@ -18827,284 +18957,6 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** -# Currency Module - -In this section of the documentation, you will find resources to learn more about the Currency Module and how to use it in your application. - -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/store/index.html.md) to learn how to manage your store's currencies using the dashboard. - -Medusa has currency related features available out-of-the-box through the Currency Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Currency Module. - -Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). - -## Currency Features - -- [Currency Management and Retrieval](https://docs.medusajs.com/references/currency/listAndCountCurrencies/index.html.md): This module adds all common currencies to your application and allows you to retrieve them. -- [Support Currencies in Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/index.html.md): Other Commerce Modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details. - -*** - -## How to Use the Currency Module - -In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. - -You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. - -For example: - -```ts title="src/workflows/retrieve-price-with-currency.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, - transform, -} from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" - -const retrieveCurrencyStep = createStep( - "retrieve-currency", - async ({}, { container }) => { - const currencyModuleService = container.resolve(Modules.CURRENCY) - - const currency = await currencyModuleService - .retrieveCurrency("usd") - - return new StepResponse({ currency }) - } -) - -type Input = { - price: number -} - -export const retrievePriceWithCurrency = createWorkflow( - "create-currency", - (input: Input) => { - const { currency } = retrieveCurrencyStep() - - const formattedPrice = transform({ - input, - currency, - }, (data) => { - return `${data.currency.symbol}${data.input.price}` - }) - - return new WorkflowResponse({ - formattedPrice, - }) - } -) -``` - -You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: - -### API Route - -```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { retrievePriceWithCurrency } from "../../workflows/retrieve-price-with-currency" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await retrievePriceWithCurrency(req.scope) - .run({ - price: 10, - }) - - res.send(result) -} -``` - -### Subscriber - -```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { - type SubscriberConfig, - type SubscriberArgs, -} from "@medusajs/framework" -import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency" - -export default async function handleUserCreated({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - const { result } = await retrievePriceWithCurrency(container) - .run({ - price: 10, - }) - - console.log(result) -} - -export const config: SubscriberConfig = { - event: "user.created", -} -``` - -### Scheduled Job - -```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"]]} -import { MedusaContainer } from "@medusajs/framework/types" -import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency" - -export default async function myCustomJob( - container: MedusaContainer -) { - const { result } = await retrievePriceWithCurrency(container) - .run({ - price: 10, - }) - - console.log(result) -} - -export const config = { - name: "run-once-a-day", - schedule: `0 0 * * *`, -} -``` - -Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). - -*** - - -# Auth Module - -In this section of the documentation, you will find resources to learn more about the Auth Module and how to use it in your application. - -Medusa has auth related features available out-of-the-box through the Auth Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Auth Module. - -Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). - -## Auth Features - -- [Basic User Authentication](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#1-basic-authentication-flow/index.html.md): Authenticate users using their email and password credentials. -- [Third-Party and Social Authentication](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#2-third-party-service-authenticate-flow/index.html.md): Authenticate users using third-party services and social platforms, such as [Google](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/google/index.html.md) and [GitHub](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/github/index.html.md). -- [Authenticate Custom Actor Types](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md): Create custom user or actor types, such as managers, authenticate them in your application, and guard routes based on the custom user types. -- [Custom Authentication Providers](https://docs.medusajs.com/references/auth/provider/index.html.md): Integrate third-party services with custom authentication providors. - -*** - -## How to Use the Auth Module - -In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. - -You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. - -For example: - -```ts title="src/workflows/authenticate-user.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { Modules, MedusaError } from "@medusajs/framework/utils" -import { MedusaRequest } from "@medusajs/framework/http" -import { AuthenticationInput } from "@medusajs/framework/types" - -type Input = { - req: MedusaRequest -} - -const authenticateUserStep = createStep( - "authenticate-user", - async ({ req }: Input, { container }) => { - const authModuleService = container.resolve(Modules.AUTH) - - const { success, authIdentity, error } = await authModuleService - .authenticate( - "emailpass", - { - url: req.url, - headers: req.headers, - query: req.query, - body: req.body, - authScope: "admin", // or custom actor type - protocol: req.protocol, - } as AuthenticationInput - ) - - if (!success) { - // incorrect authentication details - throw new MedusaError( - MedusaError.Types.UNAUTHORIZED, - error || "Incorrect authentication details" - ) - } - - return new StepResponse({ authIdentity }, authIdentity?.id) - }, - async (authIdentityId, { container }) => { - if (!authIdentityId) { - return - } - - const authModuleService = container.resolve(Modules.AUTH) - - await authModuleService.deleteAuthIdentities([authIdentityId]) - } -) - -export const authenticateUserWorkflow = createWorkflow( - "authenticate-user", - (input: Input) => { - const { authIdentity } = authenticateUserStep(input) - - return new WorkflowResponse({ - authIdentity, - }) - } -) -``` - -You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: - -```ts title="API Route" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { authenticateUserWorkflow } from "../../workflows/authenticate-user" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await authenticateUserWorkflow(req.scope) - .run({ - req, - }) - - res.send(result) -} -``` - -Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). - -*** - -## Configure Auth Module - -The Auth Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/module-options/index.html.md) for details on the module's options. - -*** - -## Providers - -Medusa provides the following authentication providers out-of-the-box. You can use them to authenticate admin users, customers, or custom actor types. - -*** - - # Fulfillment Module In this section of the documentation, you will find resources to learn more about the Fulfillment Module and how to use it in your application. @@ -19421,6 +19273,150 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** +# Inventory Module + +In this section of the documentation, you will find resources to learn more about the Inventory Module and how to use it in your application. + +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/inventory/index.html.md) to learn how to manage inventory and related features using the dashboard. + +Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Inventory Module. + +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). + +## Inventory Features + +- [Inventory Items Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts/index.html.md): Store and manage inventory of any stock-kept item, such as product variants. +- [Inventory Across Locations](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#inventorylevel/index.html.md): Manage inventory levels across different locations, such as warehouses. +- [Reservation Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#reservationitem/index.html.md): Reserve quantities of inventory items at specific locations for orders or other purposes. +- [Check Inventory Availability](https://docs.medusajs.com/references/inventory-next/confirmInventory/index.html.md): Check whether an inventory item has the necessary quantity for purchase. +- [Inventory Kits](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. + +*** + +## How to Use the Inventory Module + +In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. + +You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. + +For example: + +```ts title="src/workflows/create-inventory-item.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" + +const createInventoryItemStep = createStep( + "create-inventory-item", + async ({}, { container }) => { + const inventoryModuleService = container.resolve(Modules.INVENTORY) + + const inventoryItem = await inventoryModuleService.createInventoryItems({ + sku: "SHIRT", + title: "Green Medusa Shirt", + requires_shipping: true, + }) + + return new StepResponse({ inventoryItem }, inventoryItem.id) + }, + async (inventoryItemId, { container }) => { + if (!inventoryItemId) { + return + } + const inventoryModuleService = container.resolve(Modules.INVENTORY) + + await inventoryModuleService.deleteInventoryItems([inventoryItemId]) + } +) + +export const createInventoryItemWorkflow = createWorkflow( + "create-inventory-item-workflow", + () => { + const { inventoryItem } = createInventoryItemStep() + + return new WorkflowResponse({ + inventoryItem, + }) + } +) +``` + +You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: + +### API Route + +```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { createInventoryItemWorkflow } from "../../workflows/create-inventory-item" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await createInventoryItemWorkflow(req.scope) + .run() + + res.send(result) +} +``` + +### Subscriber + +```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + type SubscriberConfig, + type SubscriberArgs, +} from "@medusajs/framework" +import { createInventoryItemWorkflow } from "../workflows/create-inventory-item" + +export default async function handleUserCreated({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + const { result } = await createInventoryItemWorkflow(container) + .run() + + console.log(result) +} + +export const config: SubscriberConfig = { + event: "user.created", +} +``` + +### Scheduled Job + +```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} +import { MedusaContainer } from "@medusajs/framework/types" +import { createInventoryItemWorkflow } from "../workflows/create-inventory-item" + +export default async function myCustomJob( + container: MedusaContainer +) { + const { result } = await createInventoryItemWorkflow(container) + .run() + + console.log(result) +} + +export const config = { + name: "run-once-a-day", + schedule: `0 0 * * *`, +} +``` + +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +*** + + # Order Module In this section of the documentation, you will find resources to learn more about the Order Module and how to use it in your application. @@ -19577,6 +19573,462 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** +# Currency Module + +In this section of the documentation, you will find resources to learn more about the Currency Module and how to use it in your application. + +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/store/index.html.md) to learn how to manage your store's currencies using the dashboard. + +Medusa has currency related features available out-of-the-box through the Currency Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Currency Module. + +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). + +## Currency Features + +- [Currency Management and Retrieval](https://docs.medusajs.com/references/currency/listAndCountCurrencies/index.html.md): This module adds all common currencies to your application and allows you to retrieve them. +- [Support Currencies in Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/index.html.md): Other Commerce Modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details. + +*** + +## How to Use the Currency Module + +In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. + +You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. + +For example: + +```ts title="src/workflows/retrieve-price-with-currency.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, + transform, +} from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" + +const retrieveCurrencyStep = createStep( + "retrieve-currency", + async ({}, { container }) => { + const currencyModuleService = container.resolve(Modules.CURRENCY) + + const currency = await currencyModuleService + .retrieveCurrency("usd") + + return new StepResponse({ currency }) + } +) + +type Input = { + price: number +} + +export const retrievePriceWithCurrency = createWorkflow( + "create-currency", + (input: Input) => { + const { currency } = retrieveCurrencyStep() + + const formattedPrice = transform({ + input, + currency, + }, (data) => { + return `${data.currency.symbol}${data.input.price}` + }) + + return new WorkflowResponse({ + formattedPrice, + }) + } +) +``` + +You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: + +### API Route + +```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { retrievePriceWithCurrency } from "../../workflows/retrieve-price-with-currency" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await retrievePriceWithCurrency(req.scope) + .run({ + price: 10, + }) + + res.send(result) +} +``` + +### Subscriber + +```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + type SubscriberConfig, + type SubscriberArgs, +} from "@medusajs/framework" +import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency" + +export default async function handleUserCreated({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + const { result } = await retrievePriceWithCurrency(container) + .run({ + price: 10, + }) + + console.log(result) +} + +export const config: SubscriberConfig = { + event: "user.created", +} +``` + +### Scheduled Job + +```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"]]} +import { MedusaContainer } from "@medusajs/framework/types" +import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency" + +export default async function myCustomJob( + container: MedusaContainer +) { + const { result } = await retrievePriceWithCurrency(container) + .run({ + price: 10, + }) + + console.log(result) +} + +export const config = { + name: "run-once-a-day", + schedule: `0 0 * * *`, +} +``` + +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +*** + + +# Pricing Module + +In this section of the documentation, you will find resources to learn more about the Pricing Module and how to use it in your application. + +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/price-lists/index.html.md) to learn how to manage price lists using the dashboard. + +Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Pricing Module. + +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). + +## Pricing Features + +- [Price Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts/index.html.md): Store and manage prices of a resource, such as a product or a variant. +- [Advanced Rule Engine](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-rules/index.html.md): Create prices with custom rules to condition prices based on different contexts. +- [Price Lists](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts#price-list/index.html.md): Group prices and apply them only in specific conditions with price lists. +- [Price Calculation Strategy](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md): Retrieve the best price in a given context and for the specified rule values. +- [Tax-Inclusive Pricing](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/tax-inclusive-pricing/index.html.md): Calculate prices with taxes included in the price, and Medusa will handle calculating the taxes automatically. + +*** + +## How to Use the Pricing Module + +In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. + +You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. + +For example: + +```ts title="src/workflows/create-price-set.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" + +const createPriceSetStep = createStep( + "create-price-set", + async ({}, { container }) => { + const pricingModuleService = container.resolve(Modules.PRICING) + + const priceSet = await pricingModuleService.createPriceSets({ + prices: [ + { + amount: 500, + currency_code: "USD", + }, + { + amount: 400, + currency_code: "EUR", + min_quantity: 0, + max_quantity: 4, + rules: {}, + }, + ], + }) + + return new StepResponse({ priceSet }, priceSet.id) + }, + async (priceSetId, { container }) => { + if (!priceSetId) { + return + } + const pricingModuleService = container.resolve(Modules.PRICING) + + await pricingModuleService.deletePriceSets([priceSetId]) + } +) + +export const createPriceSetWorkflow = createWorkflow( + "create-price-set", + () => { + const { priceSet } = createPriceSetStep() + + return new WorkflowResponse({ + priceSet, + }) + } +) +``` + +You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: + +### API Route + +```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { createPriceSetWorkflow } from "../../workflows/create-price-set" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await createPriceSetWorkflow(req.scope) + .run() + + res.send(result) +} +``` + +### Subscriber + +```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + type SubscriberConfig, + type SubscriberArgs, +} from "@medusajs/framework" +import { createPriceSetWorkflow } from "../workflows/create-price-set" + +export default async function handleUserCreated({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + const { result } = await createPriceSetWorkflow(container) + .run() + + console.log(result) +} + +export const config: SubscriberConfig = { + event: "user.created", +} +``` + +### Scheduled Job + +```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} +import { MedusaContainer } from "@medusajs/framework/types" +import { createPriceSetWorkflow } from "../workflows/create-price-set" + +export default async function myCustomJob( + container: MedusaContainer +) { + const { result } = await createPriceSetWorkflow(container) + .run() + + console.log(result) +} + +export const config = { + name: "run-once-a-day", + schedule: `0 0 * * *`, +} +``` + +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +*** + + +# Product Module + +In this section of the documentation, you will find resources to learn more about the Product Module and how to use it in your application. + +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/products/index.html.md) to learn how to manage products using the dashboard. + +Medusa has product related features available out-of-the-box through the Product Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Product Module. + +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). + +## Product Features + +- [Products Management](https://docs.medusajs.com/references/product/models/Product/index.html.md): Store and manage products. Products have custom options, such as color or size, and each variant in the product sets the value for these options. +- [Product Organization](https://docs.medusajs.com/references/product/models/index.html.md): The Product Module provides different data models used to organize products, including categories, collections, tags, and more. +- [Bundled and Multi-Part Products](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. + +*** + +## How to Use the Product Module + +In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. + +You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. + +For example: + +```ts title="src/workflows/create-product.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" + +const createProductStep = createStep( + "create-product", + async ({}, { container }) => { + const productService = container.resolve(Modules.PRODUCT) + + const product = await productService.createProducts({ + title: "Medusa Shirt", + options: [ + { + title: "Color", + values: ["Black", "White"], + }, + ], + variants: [ + { + title: "Black Shirt", + options: { + Color: "Black", + }, + }, + ], + }) + + return new StepResponse({ product }, product.id) + }, + async (productId, { container }) => { + if (!productId) { + return + } + const productService = container.resolve(Modules.PRODUCT) + + await productService.deleteProducts([productId]) + } +) + +export const createProductWorkflow = createWorkflow( + "create-product", + () => { + const { product } = createProductStep() + + return new WorkflowResponse({ + product, + }) + } +) +``` + +You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: + +### API Route + +```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { createProductWorkflow } from "../../workflows/create-product" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await createProductWorkflow(req.scope) + .run() + + res.send(result) +} +``` + +### Subscriber + +```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + type SubscriberConfig, + type SubscriberArgs, +} from "@medusajs/framework" +import { createProductWorkflow } from "../workflows/create-product" + +export default async function handleUserCreated({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + const { result } = await createProductWorkflow(container) + .run() + + console.log(result) +} + +export const config: SubscriberConfig = { + event: "user.created", +} +``` + +### Scheduled Job + +```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} +import { MedusaContainer } from "@medusajs/framework/types" +import { createProductWorkflow } from "../workflows/create-product" + +export default async function myCustomJob( + container: MedusaContainer +) { + const { result } = await createProductWorkflow(container) + .run() + + console.log(result) +} + +export const config = { + name: "run-once-a-day", + schedule: `0 0 * * *`, +} +``` + +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +*** + + # Payment Module In this section of the documentation, you will find resources to learn more about the Payment Module and how to use it in your application. @@ -19732,27 +20184,27 @@ Medusa provides the following payment providers out-of-the-box. You can use them *** -# Pricing Module +# Region Module -In this section of the documentation, you will find resources to learn more about the Pricing Module and how to use it in your application. +In this section of the documentation, you will find resources to learn more about the Region Module and how to use it in your application. -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/price-lists/index.html.md) to learn how to manage price lists using the dashboard. +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage regions using the dashboard. -Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Pricing Module. +Medusa has region related features available out-of-the-box through the Region Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Region Module. Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). -## Pricing Features +*** -- [Price Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts/index.html.md): Store and manage prices of a resource, such as a product or a variant. -- [Advanced Rule Engine](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-rules/index.html.md): Create prices with custom rules to condition prices based on different contexts. -- [Price Lists](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts#price-list/index.html.md): Group prices and apply them only in specific conditions with price lists. -- [Price Calculation Strategy](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md): Retrieve the best price in a given context and for the specified rule values. -- [Tax-Inclusive Pricing](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/tax-inclusive-pricing/index.html.md): Calculate prices with taxes included in the price, and Medusa will handle calculating the taxes automatically. +## Region Features + +- [Region Management](https://docs.medusajs.com/references/region/models/Region/index.html.md): Manage regions in your store. You can create regions with different currencies and settings. +- [Multi-Currency Support](https://docs.medusajs.com/references/region/models/Region/index.html.md): Each region has a currency. You can support multiple currencies in your store by creating multiple regions. +- [Different Settings Per Region](https://docs.medusajs.com/references/region/models/Region/index.html.md): Each region has its own settings, such as what countries belong to a region or its tax settings. *** -## How to Use the Pricing Module +## How to Use Region Module's Service In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. @@ -19760,7 +20212,7 @@ You can build custom workflows and steps. You can also re-use Medusa's workflows For example: -```ts title="src/workflows/create-price-set.ts" highlights={highlights} +```ts title="src/workflows/create-region.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -19769,46 +20221,35 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createPriceSetStep = createStep( - "create-price-set", +const createRegionStep = createStep( + "create-region", async ({}, { container }) => { - const pricingModuleService = container.resolve(Modules.PRICING) + const regionModuleService = container.resolve(Modules.REGION) - const priceSet = await pricingModuleService.createPriceSets({ - prices: [ - { - amount: 500, - currency_code: "USD", - }, - { - amount: 400, - currency_code: "EUR", - min_quantity: 0, - max_quantity: 4, - rules: {}, - }, - ], + const region = await regionModuleService.createRegions({ + name: "Europe", + currency_code: "eur", }) - return new StepResponse({ priceSet }, priceSet.id) + return new StepResponse({ region }, region.id) }, - async (priceSetId, { container }) => { - if (!priceSetId) { + async (regionId, { container }) => { + if (!regionId) { return } - const pricingModuleService = container.resolve(Modules.PRICING) + const regionModuleService = container.resolve(Modules.REGION) - await pricingModuleService.deletePriceSets([priceSetId]) + await regionModuleService.deleteRegions([regionId]) } ) -export const createPriceSetWorkflow = createWorkflow( - "create-price-set", +export const createRegionWorkflow = createWorkflow( + "create-region", () => { - const { priceSet } = createPriceSetStep() + const { region } = createRegionStep() return new WorkflowResponse({ - priceSet, + region, }) } ) @@ -19823,13 +20264,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createPriceSetWorkflow } from "../../workflows/create-price-set" +import { createRegionWorkflow } from "../../workflows/create-region" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createPriceSetWorkflow(req.scope) + const { result } = await createRegionWorkflow(req.scope) .run() res.send(result) @@ -19843,13 +20284,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createPriceSetWorkflow } from "../workflows/create-price-set" +import { createRegionWorkflow } from "../workflows/create-region" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createPriceSetWorkflow(container) + const { result } = await createRegionWorkflow(container) .run() console.log(result) @@ -19864,304 +20305,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createPriceSetWorkflow } from "../workflows/create-price-set" +import { createRegionWorkflow } from "../workflows/create-region" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createPriceSetWorkflow(container) - .run() - - console.log(result) -} - -export const config = { - name: "run-once-a-day", - schedule: `0 0 * * *`, -} -``` - -Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). - -*** - - -# Inventory Module - -In this section of the documentation, you will find resources to learn more about the Inventory Module and how to use it in your application. - -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/inventory/index.html.md) to learn how to manage inventory and related features using the dashboard. - -Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Inventory Module. - -Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). - -## Inventory Features - -- [Inventory Items Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts/index.html.md): Store and manage inventory of any stock-kept item, such as product variants. -- [Inventory Across Locations](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#inventorylevel/index.html.md): Manage inventory levels across different locations, such as warehouses. -- [Reservation Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#reservationitem/index.html.md): Reserve quantities of inventory items at specific locations for orders or other purposes. -- [Check Inventory Availability](https://docs.medusajs.com/references/inventory-next/confirmInventory/index.html.md): Check whether an inventory item has the necessary quantity for purchase. -- [Inventory Kits](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. - -*** - -## How to Use the Inventory Module - -In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. - -You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. - -For example: - -```ts title="src/workflows/create-inventory-item.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" - -const createInventoryItemStep = createStep( - "create-inventory-item", - async ({}, { container }) => { - const inventoryModuleService = container.resolve(Modules.INVENTORY) - - const inventoryItem = await inventoryModuleService.createInventoryItems({ - sku: "SHIRT", - title: "Green Medusa Shirt", - requires_shipping: true, - }) - - return new StepResponse({ inventoryItem }, inventoryItem.id) - }, - async (inventoryItemId, { container }) => { - if (!inventoryItemId) { - return - } - const inventoryModuleService = container.resolve(Modules.INVENTORY) - - await inventoryModuleService.deleteInventoryItems([inventoryItemId]) - } -) - -export const createInventoryItemWorkflow = createWorkflow( - "create-inventory-item-workflow", - () => { - const { inventoryItem } = createInventoryItemStep() - - return new WorkflowResponse({ - inventoryItem, - }) - } -) -``` - -You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: - -### API Route - -```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { createInventoryItemWorkflow } from "../../workflows/create-inventory-item" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await createInventoryItemWorkflow(req.scope) - .run() - - res.send(result) -} -``` - -### Subscriber - -```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { - type SubscriberConfig, - type SubscriberArgs, -} from "@medusajs/framework" -import { createInventoryItemWorkflow } from "../workflows/create-inventory-item" - -export default async function handleUserCreated({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - const { result } = await createInventoryItemWorkflow(container) - .run() - - console.log(result) -} - -export const config: SubscriberConfig = { - event: "user.created", -} -``` - -### Scheduled Job - -```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} -import { MedusaContainer } from "@medusajs/framework/types" -import { createInventoryItemWorkflow } from "../workflows/create-inventory-item" - -export default async function myCustomJob( - container: MedusaContainer -) { - const { result } = await createInventoryItemWorkflow(container) - .run() - - console.log(result) -} - -export const config = { - name: "run-once-a-day", - schedule: `0 0 * * *`, -} -``` - -Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). - -*** - - -# Promotion Module - -In this section of the documentation, you will find resources to learn more about the Promotion Module and how to use it in your application. - -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/index.html.md) to learn how to manage promotions using the dashboard. - -Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Promotion Module. - -Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). - -## Promotion Features - -- [Discount Functionalities](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/concepts/index.html.md): A promotion discounts an amount or percentage of a cart's items, shipping methods, or the entire order. -- [Flexible Promotion Rules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/concepts#flexible-rules/index.html.md): A promotion has rules that restricts when the promotion is applied. -- [Campaign Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/campaign/index.html.md): A campaign combines promotions under the same conditions, such as start and end dates, and budget configurations. -- [Apply Promotion on Carts and Orders](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md): Apply promotions on carts and orders to discount items, shipping methods, or the entire order. - -*** - -## How to Use the Promotion Module - -In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. - -You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. - -For example: - -```ts title="src/workflows/create-promotion.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" - -const createPromotionStep = createStep( - "create-promotion", - async ({}, { container }) => { - const promotionModuleService = container.resolve(Modules.PROMOTION) - - const promotion = await promotionModuleService.createPromotions({ - code: "10%OFF", - type: "standard", - application_method: { - type: "percentage", - target_type: "order", - value: 10, - currency_code: "usd", - }, - }) - - return new StepResponse({ promotion }, promotion.id) - }, - async (promotionId, { container }) => { - if (!promotionId) { - return - } - const promotionModuleService = container.resolve(Modules.PROMOTION) - - await promotionModuleService.deletePromotions(promotionId) - } -) - -export const createPromotionWorkflow = createWorkflow( - "create-promotion", - () => { - const { promotion } = createPromotionStep() - - return new WorkflowResponse({ - promotion, - }) - } -) -``` - -You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: - -### API Route - -```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { createPromotionWorkflow } from "../../workflows/create-cart" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await createPromotionWorkflow(req.scope) - .run() - - res.send(result) -} -``` - -### Subscriber - -```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { - type SubscriberConfig, - type SubscriberArgs, -} from "@medusajs/framework" -import { createPromotionWorkflow } from "../workflows/create-cart" - -export default async function handleUserCreated({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - const { result } = await createPromotionWorkflow(container) - .run() - - console.log(result) -} - -export const config: SubscriberConfig = { - event: "user.created", -} -``` - -### Scheduled Job - -```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} -import { MedusaContainer } from "@medusajs/framework/types" -import { createPromotionWorkflow } from "../workflows/create-cart" - -export default async function myCustomJob( - container: MedusaContainer -) { - const { result } = await createPromotionWorkflow(container) + const { result } = await createRegionWorkflow(container) .run() console.log(result) @@ -20315,303 +20464,6 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** -# Product Module - -In this section of the documentation, you will find resources to learn more about the Product Module and how to use it in your application. - -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/products/index.html.md) to learn how to manage products using the dashboard. - -Medusa has product related features available out-of-the-box through the Product Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Product Module. - -Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). - -## Product Features - -- [Products Management](https://docs.medusajs.com/references/product/models/Product/index.html.md): Store and manage products. Products have custom options, such as color or size, and each variant in the product sets the value for these options. -- [Product Organization](https://docs.medusajs.com/references/product/models/index.html.md): The Product Module provides different data models used to organize products, including categories, collections, tags, and more. -- [Bundled and Multi-Part Products](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. - -*** - -## How to Use the Product Module - -In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. - -You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. - -For example: - -```ts title="src/workflows/create-product.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" - -const createProductStep = createStep( - "create-product", - async ({}, { container }) => { - const productService = container.resolve(Modules.PRODUCT) - - const product = await productService.createProducts({ - title: "Medusa Shirt", - options: [ - { - title: "Color", - values: ["Black", "White"], - }, - ], - variants: [ - { - title: "Black Shirt", - options: { - Color: "Black", - }, - }, - ], - }) - - return new StepResponse({ product }, product.id) - }, - async (productId, { container }) => { - if (!productId) { - return - } - const productService = container.resolve(Modules.PRODUCT) - - await productService.deleteProducts([productId]) - } -) - -export const createProductWorkflow = createWorkflow( - "create-product", - () => { - const { product } = createProductStep() - - return new WorkflowResponse({ - product, - }) - } -) -``` - -You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: - -### API Route - -```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { createProductWorkflow } from "../../workflows/create-product" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await createProductWorkflow(req.scope) - .run() - - res.send(result) -} -``` - -### Subscriber - -```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { - type SubscriberConfig, - type SubscriberArgs, -} from "@medusajs/framework" -import { createProductWorkflow } from "../workflows/create-product" - -export default async function handleUserCreated({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - const { result } = await createProductWorkflow(container) - .run() - - console.log(result) -} - -export const config: SubscriberConfig = { - event: "user.created", -} -``` - -### Scheduled Job - -```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} -import { MedusaContainer } from "@medusajs/framework/types" -import { createProductWorkflow } from "../workflows/create-product" - -export default async function myCustomJob( - container: MedusaContainer -) { - const { result } = await createProductWorkflow(container) - .run() - - console.log(result) -} - -export const config = { - name: "run-once-a-day", - schedule: `0 0 * * *`, -} -``` - -Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). - -*** - - -# Region Module - -In this section of the documentation, you will find resources to learn more about the Region Module and how to use it in your application. - -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage regions using the dashboard. - -Medusa has region related features available out-of-the-box through the Region Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Region Module. - -Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). - -*** - -## Region Features - -- [Region Management](https://docs.medusajs.com/references/region/models/Region/index.html.md): Manage regions in your store. You can create regions with different currencies and settings. -- [Multi-Currency Support](https://docs.medusajs.com/references/region/models/Region/index.html.md): Each region has a currency. You can support multiple currencies in your store by creating multiple regions. -- [Different Settings Per Region](https://docs.medusajs.com/references/region/models/Region/index.html.md): Each region has its own settings, such as what countries belong to a region or its tax settings. - -*** - -## How to Use Region Module's Service - -In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. - -You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. - -For example: - -```ts title="src/workflows/create-region.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" - -const createRegionStep = createStep( - "create-region", - async ({}, { container }) => { - const regionModuleService = container.resolve(Modules.REGION) - - const region = await regionModuleService.createRegions({ - name: "Europe", - currency_code: "eur", - }) - - return new StepResponse({ region }, region.id) - }, - async (regionId, { container }) => { - if (!regionId) { - return - } - const regionModuleService = container.resolve(Modules.REGION) - - await regionModuleService.deleteRegions([regionId]) - } -) - -export const createRegionWorkflow = createWorkflow( - "create-region", - () => { - const { region } = createRegionStep() - - return new WorkflowResponse({ - region, - }) - } -) -``` - -You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: - -### API Route - -```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import type { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { createRegionWorkflow } from "../../workflows/create-region" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await createRegionWorkflow(req.scope) - .run() - - res.send(result) -} -``` - -### Subscriber - -```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { - type SubscriberConfig, - type SubscriberArgs, -} from "@medusajs/framework" -import { createRegionWorkflow } from "../workflows/create-region" - -export default async function handleUserCreated({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - const { result } = await createRegionWorkflow(container) - .run() - - console.log(result) -} - -export const config: SubscriberConfig = { - event: "user.created", -} -``` - -### Scheduled Job - -```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} -import { MedusaContainer } from "@medusajs/framework/types" -import { createRegionWorkflow } from "../workflows/create-region" - -export default async function myCustomJob( - container: MedusaContainer -) { - const { result } = await createRegionWorkflow(container) - .run() - - console.log(result) -} - -export const config = { - name: "run-once-a-day", - schedule: `0 0 * * *`, -} -``` - -Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). - -*** - - # Sales Channel Module In this section of the documentation, you will find resources to learn more about the Sales Channel Module and how to use it in your application. @@ -20913,6 +20765,153 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** +# User Module + +In this section of the documentation, you will find resources to learn more about the User Module and how to use it in your application. + +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/users/index.html.md) to learn how to manage users using the dashboard. + +Medusa has user related features available out-of-the-box through the User Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this User Module. + +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). + +## User Features + +- [User Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows/index.html.md): Store and manage users in your store. +- [Invite Users](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows#invite-users/index.html.md): Invite users to join your store and manage those invites. + +*** + +## How to Use User Module's Service + +In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. + +You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package. + +For example: + +```ts title="src/workflows/create-user.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" + +const createUserStep = createStep( + "create-user", + async ({}, { container }) => { + const userModuleService = container.resolve(Modules.USER) + + const user = await userModuleService.createUsers({ + email: "user@example.com", + first_name: "John", + last_name: "Smith", + }) + + return new StepResponse({ user }, user.id) + }, + async (userId, { container }) => { + if (!userId) { + return + } + const userModuleService = container.resolve(Modules.USER) + + await userModuleService.deleteUsers([userId]) + } +) + +export const createUserWorkflow = createWorkflow( + "create-user", + () => { + const { user } = createUserStep() + + return new WorkflowResponse({ + user, + }) + } +) +``` + +You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers: + +### API Route + +```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { createUserWorkflow } from "../../workflows/create-user" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await createUserWorkflow(req.scope) + .run() + + res.send(result) +} +``` + +### Subscriber + +```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + type SubscriberConfig, + type SubscriberArgs, +} from "@medusajs/framework" +import { createUserWorkflow } from "../workflows/create-user" + +export default async function handleUserCreated({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + const { result } = await createUserWorkflow(container) + .run() + + console.log(result) +} + +export const config: SubscriberConfig = { + event: "user.created", +} +``` + +### Scheduled Job + +```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} +import { MedusaContainer } from "@medusajs/framework/types" +import { createUserWorkflow } from "../workflows/create-user" + +export default async function myCustomJob( + container: MedusaContainer +) { + const { result } = await createUserWorkflow(container) + .run() + + console.log(result) +} + +export const config = { + name: "run-once-a-day", + schedule: `0 0 * * *`, +} +``` + +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +*** + +## Configure User Module + +The User Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/module-options/index.html.md) for details on the module's options. + +*** + + # Tax Module In this section of the documentation, you will find resources to learn more about the Tax Module and how to use it in your application. @@ -21085,24 +21084,26 @@ The associated token is no longer usable or verifiable. To verify a token received as an input or in a request, use the [authenticate method of the module’s main service](https://docs.medusajs.com/references/api-key/authenticate/index.html.md) which validates the token against all non-expired tokens. -# User Module +# Promotion Module -In this section of the documentation, you will find resources to learn more about the User Module and how to use it in your application. +In this section of the documentation, you will find resources to learn more about the Promotion Module and how to use it in your application. -Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/users/index.html.md) to learn how to manage users using the dashboard. +Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/index.html.md) to learn how to manage promotions using the dashboard. -Medusa has user related features available out-of-the-box through the User Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this User Module. +Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Promotion Module. Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). -## User Features +## Promotion Features -- [User Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows/index.html.md): Store and manage users in your store. -- [Invite Users](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows#invite-users/index.html.md): Invite users to join your store and manage those invites. +- [Discount Functionalities](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/concepts/index.html.md): A promotion discounts an amount or percentage of a cart's items, shipping methods, or the entire order. +- [Flexible Promotion Rules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/concepts#flexible-rules/index.html.md): A promotion has rules that restricts when the promotion is applied. +- [Campaign Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/campaign/index.html.md): A campaign combines promotions under the same conditions, such as start and end dates, and budget configurations. +- [Apply Promotion on Carts and Orders](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md): Apply promotions on carts and orders to discount items, shipping methods, or the entire order. *** -## How to Use User Module's Service +## How to Use the Promotion Module In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism. @@ -21110,7 +21111,7 @@ You can build custom workflows and steps. You can also re-use Medusa's workflows For example: -```ts title="src/workflows/create-user.ts" highlights={highlights} +```ts title="src/workflows/create-promotion.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -21119,36 +21120,41 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createUserStep = createStep( - "create-user", +const createPromotionStep = createStep( + "create-promotion", async ({}, { container }) => { - const userModuleService = container.resolve(Modules.USER) + const promotionModuleService = container.resolve(Modules.PROMOTION) - const user = await userModuleService.createUsers({ - email: "user@example.com", - first_name: "John", - last_name: "Smith", + const promotion = await promotionModuleService.createPromotions({ + code: "10%OFF", + type: "standard", + application_method: { + type: "percentage", + target_type: "order", + value: 10, + currency_code: "usd", + }, }) - return new StepResponse({ user }, user.id) + return new StepResponse({ promotion }, promotion.id) }, - async (userId, { container }) => { - if (!userId) { + async (promotionId, { container }) => { + if (!promotionId) { return } - const userModuleService = container.resolve(Modules.USER) + const promotionModuleService = container.resolve(Modules.PROMOTION) - await userModuleService.deleteUsers([userId]) + await promotionModuleService.deletePromotions(promotionId) } ) -export const createUserWorkflow = createWorkflow( - "create-user", +export const createPromotionWorkflow = createWorkflow( + "create-promotion", () => { - const { user } = createUserStep() + const { promotion } = createPromotionStep() return new WorkflowResponse({ - user, + promotion, }) } ) @@ -21163,13 +21169,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createUserWorkflow } from "../../workflows/create-user" +import { createPromotionWorkflow } from "../../workflows/create-cart" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createUserWorkflow(req.scope) + const { result } = await createPromotionWorkflow(req.scope) .run() res.send(result) @@ -21183,13 +21189,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createUserWorkflow } from "../workflows/create-user" +import { createPromotionWorkflow } from "../workflows/create-cart" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createUserWorkflow(container) + const { result } = await createPromotionWorkflow(container) .run() console.log(result) @@ -21204,12 +21210,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createUserWorkflow } from "../workflows/create-user" +import { createPromotionWorkflow } from "../workflows/create-cart" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createUserWorkflow(container) + const { result } = await createPromotionWorkflow(container) .run() console.log(result) @@ -21225,12 +21231,6 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc *** -## Configure User Module - -The User Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/module-options/index.html.md) for details on the module's options. - -*** - # Links between API Key Module and Other Modules @@ -21330,261 +21330,275 @@ createRemoteLinkStep({ ``` -# Customer Accounts +# Authentication Flows with the Auth Main Service -In this document, you’ll learn how registered and unregistered accounts are distinguished in the Medusa application. +In this document, you'll learn how to use the Auth Module's main service's methods to implement authentication flows and reset a user's password. -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/customers/index.html.md) to learn how to manage customers using the dashboard. +## Authentication Methods -## `has_account` Property +### Register -The [Customer data model](https://docs.medusajs.com/references/customer/models/Customer/index.html.md) has a `has_account` property, which is a boolean that indicates whether a customer is registered. +The [register method of the Auth Module's main service](https://docs.medusajs.com/references/auth/register/index.html.md) creates an auth identity that can be authenticated later. -When a guest customer places an order, a new `Customer` record is created with `has_account` set to `false`. +For example: -When this or another guest customer registers an account with the same email, a new `Customer` record is created with `has_account` set to `true`. +```ts +const data = await authModuleService.register( + "emailpass", + // passed to auth provider + { + // ... + } +) +``` + +This method calls the `register` method of the provider specified in the first parameter and returns its data. + +### Authenticate + +To authenticate a user, you use the [authenticate method of the Auth Module's main service](https://docs.medusajs.com/references/auth/authenticate/index.html.md). For example: + +```ts +const data = await authModuleService.authenticate( + "emailpass", + // passed to auth provider + { + // ... + } +) +``` + +This method calls the `authenticate` method of the provider specified in the first parameter and returns its data. *** -## Email Uniqueness +## Auth Flow 1: Basic Authentication -The above behavior means that two `Customer` records may exist with the same email. However, the main difference is the `has_account` property's value. +The basic authentication flow requires first using the `register` method, then the `authenticate` method: -So, there can only be one guest customer (having `has_account=false`) and one registered customer (having `has_account=true`) with the same email. +```ts +const { success, authIdentity, error } = await authModuleService.register( + "emailpass", + // passed to auth provider + { + // ... + } +) +if (error) { + // registration failed + // TODO return an error + return +} -# Links between Customer Module and Other Modules +// later (can be another route for log-in) +const { success, authIdentity, location } = await authModuleService.authenticate( + "emailpass", + // passed to auth provider + { + // ... + } +) -This document showcases the module links defined between the Customer Module and other Commerce Modules. +if (success && !location) { + // user is authenticated +} +``` -## Summary +If `success` is true and `location` isn't set, the user is authenticated successfully, and their authentication details are available within the `authIdentity` object. -The Customer Module has the following links to other modules: +The next section explains the flow if `location` is set. -Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. +Check out the [AuthIdentity](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) reference for the received properties in `authIdentity`. -|First Data Model|Second Data Model|Type|Description| -|---|---|---|---| -|| in |Stored - many-to-many|| -| in ||Read-only - has one|| -| in ||Read-only - has one|| +![Diagram showcasing the basic authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711373749/Medusa%20Resources/basic-auth_lgpqsj.jpg) + +### Auth Identity with Same Identifier + +If an auth identity, such as a `customer`, tries to register with an email of another auth identity, the `register` method returns an error. This can happen either if another customer is using the same email, or an admin user has the same email. + +There are two ways to handle this: + +- Consider the customer authenticated if the `authenticate` method validates that the email and password are correct. This allows admin users, for example, to authenticate as customers. +- Return an error message to the customer, informing them that the email is already in use. *** -## Payment Module +## Auth Flow 2: Third-Party Service Authentication -Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it. - -This link is available starting from Medusa `v2.5.0`. - -### Retrieve with Query - -To retrieve the account holder associated with a customer with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: - -### query.graph +The third-party service authentication method requires using the `authenticate` method first: ```ts -const { data: customers } = await query.graph({ - entity: "customer", - fields: [ - "account_holder_link.account_holder.*", - ], -}) +const { success, authIdentity, location } = await authModuleService.authenticate( + "google", + // passed to auth provider + { + // ... + } +) -// customers[0].account_holder_link?.[0]?.account_holder +if (location) { + // return the location for the front-end to redirect to +} + +if (!success) { + // authentication failed +} + +// authentication successful ``` -### useQueryGraphStep +If the `authenticate` method returns a `location` property, the authentication process requires the user to perform an action with a third-party service. So, you return the `location` to the front-end or client to redirect to that URL. + +For example, when using the `google` provider, the `location` is the URL that the user is navigated to login. + +![Diagram showcasing the first part of the third-party authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711374847/Medusa%20Resources/third-party-auth-1_enyedy.jpg) + +### Overriding Callback URL + +The Google and GitHub providers allow you to override their `callbackUrl` option during authentication. This is useful when you redirect the user after authentication to a URL based on its actor type. For example, you redirect admin users and customers to different pages. ```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: customers } = useQueryGraphStep({ - entity: "customer", - fields: [ - "account_holder_link.account_holder.*", - ], -}) - -// customers[0].account_holder_link?.[0]?.account_holder +const { success, authIdentity, location } = await authModuleService.authenticate( + "google", + // passed to auth provider + { + // ... + callback_url: "example.com", + } +) ``` -### Manage with Link +### validateCallback -To manage the account holders of a customer, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): +Providers handling this authentication flow must implement the `validateCallback` method. It implements the logic to validate the authentication with the third-party service. -### link.create +So, once the user performs the required action with the third-party service (for example, log-in with Google), the frontend must redirect to an API route that uses the [validateCallback method of the Auth Module's main service](https://docs.medusajs.com/references/auth/validateCallback/index.html.md). + +The method calls the specified provider’s `validateCallback` method passing it the authentication details it received in the second parameter: ```ts -import { Modules } from "@medusajs/framework/utils" +const { success, authIdentity } = await authModuleService.validateCallback( + "google", + // passed to auth provider + { + // request data, such as + url, + headers, + query, + body, + protocol, + } +) -// ... - -await link.create({ - [Modules.CUSTOMER]: { - customer_id: "cus_123", - }, - [Modules.PAYMENT]: { - account_holder_id: "acchld_123", - }, -}) +if (success) { + // authentication succeeded +} ``` -### createRemoteLinkStep +For providers like Google, the `query` object contains the query parameters from the original callback URL, such as the `code` and `state` parameters. -```ts -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" +If the returned `success` property is `true`, the authentication with the third-party provider was successful. -// ... - -createRemoteLinkStep({ - [Modules.CUSTOMER]: { - customer_id: "cus_123", - }, - [Modules.PAYMENT]: { - account_holder_id: "acchld_123", - }, -}) -``` +![Diagram showcasing the second part of the third-party authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711375123/Medusa%20Resources/third-party-auth-2_kmjxju.jpg) *** -## Cart Module +## Reset Password -Medusa defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model and the `Customer` data model. Because the link is read-only from the `Cart`'s side, you can only retrieve the customer of a cart, and not the other way around. +To update a user's password or other authentication details, use the `updateProvider` method of the Auth Module's main service. It calls the `update` method of the specified authentication provider. -### Retrieve with Query - -To retrieve the customer of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: - -### query.graph +For example: ```ts -const { data: carts } = await query.graph({ - entity: "cart", - fields: [ - "customer.*", - ], -}) +const { success } = await authModuleService.updateProvider( + "emailpass", + // passed to the auth provider + { + entity_id: "user@example.com", + password: "supersecret", + } +) -// carts.customer +if (success) { + // password reset successfully +} ``` -### useQueryGraphStep +The method accepts as a first parameter the ID of the provider, and as a second parameter the data necessary to reset the password. -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +In the example above, you use the `emailpass` provider, so you have to pass an object having an `email` and `password` properties. -// ... +If the returned `success` property is `true`, the password has reset successfully. -const { data: carts } = useQueryGraphStep({ - entity: "cart", - fields: [ - "customer.*", - ], -}) -// carts.customer -``` +# Auth Identity and Actor Types + +In this document, you’ll learn about concepts related to identity and actors in the Auth Module. + +## What is an Auth Identity? + +The [AuthIdentity data model](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) represents a user registered by an [authentication provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/index.html.md). When a user is registered using an authentication provider, the provider creates a record of `AuthIdentity`. + +Then, when the user logs-in in the future with the same authentication provider, the associated auth identity is used to validate their credentials. *** -## Order Module +## Actor Types -Medusa defines a read-only link between the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model and the `Customer` data model. Because the link is read-only from the `Order`'s side, you can only retrieve the customer of an order, and not the other way around. +An actor type is a type of user that can be authenticated. The Auth Module doesn't store or manage any user-like models, such as for customers or users. Instead, the user types are created and managed by other modules. For example, a customer is managed by the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md). -### Retrieve with Query +Then, when an auth identity is created for the actor type, the ID of the user is stored in the `app_metadata` property of the auth identity. -To retrieve the customer of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: +For example, an auth identity of a customer has the following `app_metadata` property: -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "customer.*", - ], -}) - -// orders.customer +```json +{ + "app_metadata": { + "customer_id": "cus_123" + } +} ``` -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "customer.*", - ], -}) - -// orders.customer -``` - - -# Links between Currency Module and Other Modules - -This document showcases the module links defined between the Currency Module and other Commerce Modules. - -## Summary - -The Currency Module has the following links to other modules: - -Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. - -|First Data Model|Second Data Model|Type|Description| -|---|---|---|---| -| in ||Read-only - has one|| +The ID of the user is stored in the key `{actor_type}_id` of the `app_metadata` property. *** -## Store Module +## Protect Routes by Actor Type -The Store Module has a `Currency` data model that stores the supported currencies of a store. However, these currencies don't hold all the details of a currency, such as its name or symbol. +When you protect routes with the `authenticate` middleware, you specify in its first parameter the actor type that must be authenticated to access the specified API routes. -Instead, Medusa defines a read-only link between the [Store Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/store/index.html.md)'s `StoreCurrency` data model and the Currency Module's `Currency` data model. Because the link is read-only from the `Store`'s side, you can only retrieve the details of a store's supported currencies, and not the other way around. +For example: -### Retrieve with Query +```ts title="src/api/middlewares.ts" highlights={highlights} +import { + defineMiddlewares, + authenticate, +} from "@medusajs/framework/http" -To retrieve the details of a store's currencies with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `supported_currencies.currency.*` in `fields`: - -### query.graph - -```ts -const { data: stores } = await query.graph({ - entity: "store", - fields: [ - "supported_currencies.currency.*", +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom/admin*", + middlewares: [ + authenticate("user", ["session", "bearer", "api-key"]), + ], + }, ], }) - -// stores[0].supported_currencies[0].currency ``` -### useQueryGraphStep +By specifying `user` as the first parameter of `authenticate`, only authenticated users of actor type `user` (admin users) can access API routes starting with `/custom/admin`. -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +*** -// ... +## Custom Actor Types -const { data: stores } = useQueryGraphStep({ - entity: "store", - fields: [ - "supported_currencies.currency.*", - ], -}) +You can define custom actor types that allows a custom user, managed by your custom module, to authenticate into Medusa. -// stores[0].supported_currencies[0].currency -``` +For example, if you have a custom module with a `Manager` data model, you can authenticate managers with the `manager` actor type. + +Learn how to create a custom actor type in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md). # Auth Providers @@ -21979,275 +21993,384 @@ If the authentication is successful, the request returns an object with a `succe ``` -# Auth Identity and Actor Types +# Auth Module Options -In this document, you’ll learn about concepts related to identity and actors in the Auth Module. +In this document, you'll learn about the options of the Auth Module. -## What is an Auth Identity? +## providers -The [AuthIdentity data model](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) represents a user registered by an [authentication provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/index.html.md). When a user is registered using an authentication provider, the provider creates a record of `AuthIdentity`. +The `providers` option is an array of auth module providers. -Then, when the user logs-in in the future with the same authentication provider, the associated auth identity is used to validate their credentials. +When the Medusa application starts, these providers are registered and can be used to handle authentication. -*** - -## Actor Types - -An actor type is a type of user that can be authenticated. The Auth Module doesn't store or manage any user-like models, such as for customers or users. Instead, the user types are created and managed by other modules. For example, a customer is managed by the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md). - -Then, when an auth identity is created for the actor type, the ID of the user is stored in the `app_metadata` property of the auth identity. - -For example, an auth identity of a customer has the following `app_metadata` property: - -```json -{ - "app_metadata": { - "customer_id": "cus_123" - } -} -``` - -The ID of the user is stored in the key `{actor_type}_id` of the `app_metadata` property. - -*** - -## Protect Routes by Actor Type - -When you protect routes with the `authenticate` middleware, you specify in its first parameter the actor type that must be authenticated to access the specified API routes. +By default, the `emailpass` provider is registered to authenticate customers and admin users. For example: -```ts title="src/api/middlewares.ts" highlights={highlights} -import { - defineMiddlewares, - authenticate, -} from "@medusajs/framework/http" +```ts title="medusa-config.ts" +import { Modules, ContainerRegistrationKeys } from "@medusajs/framework/utils" -export default defineMiddlewares({ - routes: [ +// ... + +module.exports = defineConfig({ + // ... + modules: [ { - matcher: "/custom/admin*", - middlewares: [ - authenticate("user", ["session", "bearer", "api-key"]), - ], + resolve: "@medusajs/medusa/auth", + dependencies: [Modules.CACHE, ContainerRegistrationKeys.LOGGER], + options: { + providers: [ + { + resolve: "@medusajs/medusa/auth-emailpass", + id: "emailpass", + options: { + // provider options... + }, + }, + ], + }, }, ], }) ``` -By specifying `user` as the first parameter of `authenticate`, only authenticated users of actor type `user` (admin users) can access API routes starting with `/custom/admin`. +The `providers` option is an array of objects that accept the following properties: + +- `resolve`: A string indicating the package name of the module provider or the path to it relative to the `src` directory. +- `id`: A string indicating the provider's unique name or ID. +- `options`: An optional object of the module provider's options. *** -## Custom Actor Types +## Auth CORS -You can define custom actor types that allows a custom user, managed by your custom module, to authenticate into Medusa. +The Medusa application's authentication API routes are defined under the `/auth` prefix that requires setting the `authCors` property of the `http` configuration. -For example, if you have a custom module with a `Manager` data model, you can authenticate managers with the `manager` actor type. +By default, the Medusa application you created will have an `AUTH_CORS` environment variable, which is used as the value of `authCors`. -Learn how to create a custom actor type in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md). - - -# Authentication Flows with the Auth Main Service - -In this document, you'll learn how to use the Auth Module's main service's methods to implement authentication flows and reset a user's password. - -## Authentication Methods - -### Register - -The [register method of the Auth Module's main service](https://docs.medusajs.com/references/auth/register/index.html.md) creates an auth identity that can be authenticated later. - -For example: - -```ts -const data = await authModuleService.register( - "emailpass", - // passed to auth provider - { - // ... - } -) -``` - -This method calls the `register` method of the provider specified in the first parameter and returns its data. - -### Authenticate - -To authenticate a user, you use the [authenticate method of the Auth Module's main service](https://docs.medusajs.com/references/auth/authenticate/index.html.md). For example: - -```ts -const data = await authModuleService.authenticate( - "emailpass", - // passed to auth provider - { - // ... - } -) -``` - -This method calls the `authenticate` method of the provider specified in the first parameter and returns its data. +Refer to [Medusa's configuration guide](https://docs.medusajs.com/docs/learn/configurations/medusa-config#httpauthCors/index.html.md) to learn more about the `authCors` configuration. *** -## Auth Flow 1: Basic Authentication +## authMethodsPerActor Configuration -The basic authentication flow requires first using the `register` method, then the `authenticate` method: +The Medusa application's configuration accept an `authMethodsPerActor` configuration which restricts the allowed auth providers used with an actor type. -```ts -const { success, authIdentity, error } = await authModuleService.register( - "emailpass", - // passed to auth provider - { - // ... - } -) +Learn more about the `authMethodsPerActor` configuration in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers#configure-allowed-auth-providers-of-actor-types/index.html.md). -if (error) { - // registration failed - // TODO return an error - return + +# How to Handle Password Reset Token Event + +In this guide, you'll learn how to handle the `auth.password_reset` event, which is emitted when a request is sent to the [Generate Reset Password Token API route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#generate-reset-password-token-route/index.html.md). + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/reset-password/index.html.md) to learn how to reset your user admin password using the dashboard. + +You'll create a subscriber that listens to the event. When the event is emitted, the subscriber sends an email notification to the user. + +### Prerequisites + +- [A notification provider module, such as SendGrid](https://docs.medusajs.com/infrastructure-modules/notification/sendgrid/index.html.md) + +## 1. Create Subscriber + +The first step is to create a subscriber that listens to the `auth.password_reset` and sends the user a notification with instructions to reset their password. + +Create the file `src/subscribers/handle-reset.ts` with the following content: + +```ts title="src/subscribers/handle-reset.ts" highlights={highlights} collapsibleLines="1-6" expandMoreLabel="Show Imports" +import { + SubscriberArgs, + type SubscriberConfig, +} from "@medusajs/medusa" +import { Modules } from "@medusajs/framework/utils" + +export default async function resetPasswordTokenHandler({ + event: { data: { + entity_id: email, + token, + actor_type, + } }, + container, +}: SubscriberArgs<{ entity_id: string, token: string, actor_type: string }>) { + const notificationModuleService = container.resolve( + Modules.NOTIFICATION + ) + + const urlPrefix = actor_type === "customer" ? + "https://storefront.com" : + "https://admin.com/app" + + await notificationModuleService.createNotifications({ + to: email, + channel: "email", + template: "reset-password-template", + data: { + // a URL to a frontend application + url: `${urlPrefix}/reset-password?token=${token}&email=${email}`, + }, + }) } -// later (can be another route for log-in) -const { success, authIdentity, location } = await authModuleService.authenticate( - "emailpass", - // passed to auth provider - { - // ... - } -) - -if (success && !location) { - // user is authenticated +export const config: SubscriberConfig = { + event: "auth.password_reset", } ``` -If `success` is true and `location` isn't set, the user is authenticated successfully, and their authentication details are available within the `authIdentity` object. +You subscribe to the `auth.password_reset` event. The event has a data payload object with the following properties: -The next section explains the flow if `location` is set. +- `entity_id`: The identifier of the user. When using the `emailpass` provider, it's the user's email. +- `token`: The token to reset the user's password. +- `actor_type`: The user's actor type. For example, if the user is a customer, the `actor_type` is `customer`. If it's an admin user, the `actor_type` is `user`. -Check out the [AuthIdentity](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) reference for the received properties in `authIdentity`. +This event's payload previously had an `actorType` field. It was renamed to `actor_type` after [Medusa v2.0.7](https://github.com/medusajs/medusa/releases/tag/v2.0.7). -![Diagram showcasing the basic authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711373749/Medusa%20Resources/basic-auth_lgpqsj.jpg) +In the subscriber, you: -### Auth Identity with Same Identifier - -If an auth identity, such as a `customer`, tries to register with an email of another auth identity, the `register` method returns an error. This can happen either if another customer is using the same email, or an admin user has the same email. - -There are two ways to handle this: - -- Consider the customer authenticated if the `authenticate` method validates that the email and password are correct. This allows admin users, for example, to authenticate as customers. -- Return an error message to the customer, informing them that the email is already in use. +- Decide the frontend URL based on whether the user is a customer or admin user by checking the value of `actor_type`. +- Resolve the Notification Module and use its `createNotifications` method to send the notification. +- You pass to the `createNotifications` method an object having the following properties: + - `to`: The identifier to send the notification to, which in this case is the email. + - `channel`: The channel to send the notification through, which in this case is email. + - `template`: The template ID in the third-party service. + - `data`: The data payload to pass to the template. You pass the URL to redirect the user to. You must pass the token and email in the URL so that the frontend can send them later to the Medusa application when reseting the password. *** -## Auth Flow 2: Third-Party Service Authentication +## 2. Test it Out: Generate Reset Password Token -The third-party service authentication method requires using the `authenticate` method first: +To test the subscriber out, send a request to the `/auth/{actor_type}/{auth_provider}/reset-password` API route, replacing `{actor_type}` and `{auth_provider}` with the user's actor type and provider used for authentication respectively. -```ts -const { success, authIdentity, location } = await authModuleService.authenticate( - "google", - // passed to auth provider - { - // ... - } -) +For example, to generate a reset password token for an admin user using the `emailpass` provider, send the following request: -if (location) { - // return the location for the front-end to redirect to -} - -if (!success) { - // authentication failed -} - -// authentication successful +```bash +curl --location 'http://localhost:9000/auth/user/emailpass/reset-password' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "identifier": "admin-test@gmail.com" +}' ``` -If the `authenticate` method returns a `location` property, the authentication process requires the user to perform an action with a third-party service. So, you return the `location` to the front-end or client to redirect to that URL. +In the request body, you must pass an `identifier` parameter. Its value is the user's identifier, which is the email in this case. -For example, when using the `google` provider, the `location` is the URL that the user is navigated to login. +If the token is generated successfully, the request returns a response with `201` status code. In the terminal, you'll find the following message indicating that the `auth.password_reset` event was emitted and your subscriber ran: -![Diagram showcasing the first part of the third-party authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711374847/Medusa%20Resources/third-party-auth-1_enyedy.jpg) - -### Overriding Callback URL - -The Google and GitHub providers allow you to override their `callbackUrl` option during authentication. This is useful when you redirect the user after authentication to a URL based on its actor type. For example, you redirect admin users and customers to different pages. - -```ts -const { success, authIdentity, location } = await authModuleService.authenticate( - "google", - // passed to auth provider - { - // ... - callback_url: "example.com", - } -) +```plain +info: Processing auth.password_reset which has 1 subscribers ``` -### validateCallback - -Providers handling this authentication flow must implement the `validateCallback` method. It implements the logic to validate the authentication with the third-party service. - -So, once the user performs the required action with the third-party service (for example, log-in with Google), the frontend must redirect to an API route that uses the [validateCallback method of the Auth Module's main service](https://docs.medusajs.com/references/auth/validateCallback/index.html.md). - -The method calls the specified provider’s `validateCallback` method passing it the authentication details it received in the second parameter: - -```ts -const { success, authIdentity } = await authModuleService.validateCallback( - "google", - // passed to auth provider - { - // request data, such as - url, - headers, - query, - body, - protocol, - } -) - -if (success) { - // authentication succeeded -} -``` - -For providers like Google, the `query` object contains the query parameters from the original callback URL, such as the `code` and `state` parameters. - -If the returned `success` property is `true`, the authentication with the third-party provider was successful. - -![Diagram showcasing the second part of the third-party authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711375123/Medusa%20Resources/third-party-auth-2_kmjxju.jpg) +The notification is sent to the user with the frontend URL to enter a new password. *** -## Reset Password +## Next Steps: Implementing Frontend -To update a user's password or other authentication details, use the `updateProvider` method of the Auth Module's main service. It calls the `update` method of the specified authentication provider. +In your frontend, you must have a page that accepts `token` and `email` query parameters. -For example: +The page shows the user password fields to enter their new password, then submits the new password, token, and email to the [Reset Password Route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#reset-password-route/index.html.md). + +### Examples + +- [Storefront Guide: Reset Customer Password](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/reset-password/index.html.md) + + +# Customer Accounts + +In this document, you’ll learn how registered and unregistered accounts are distinguished in the Medusa application. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/customers/index.html.md) to learn how to manage customers using the dashboard. + +## `has_account` Property + +The [Customer data model](https://docs.medusajs.com/references/customer/models/Customer/index.html.md) has a `has_account` property, which is a boolean that indicates whether a customer is registered. + +When a guest customer places an order, a new `Customer` record is created with `has_account` set to `false`. + +When this or another guest customer registers an account with the same email, a new `Customer` record is created with `has_account` set to `true`. + +*** + +## Email Uniqueness + +The above behavior means that two `Customer` records may exist with the same email. However, the main difference is the `has_account` property's value. + +So, there can only be one guest customer (having `has_account=false`) and one registered customer (having `has_account=true`) with the same email. + + +# Links between Customer Module and Other Modules + +This document showcases the module links defined between the Customer Module and other Commerce Modules. + +## Summary + +The Customer Module has the following links to other modules: + +Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. + +|First Data Model|Second Data Model|Type|Description| +|---|---|---|---| +|| in |Stored - many-to-many|| +| in ||Read-only - has one|| +| in ||Read-only - has one|| + +*** + +## Payment Module + +Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it. + +This link is available starting from Medusa `v2.5.0`. + +### Retrieve with Query + +To retrieve the account holder associated with a customer with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: + +### query.graph ```ts -const { success } = await authModuleService.updateProvider( - "emailpass", - // passed to the auth provider - { - entity_id: "user@example.com", - password: "supersecret", - } -) +const { data: customers } = await query.graph({ + entity: "customer", + fields: [ + "account_holder_link.account_holder.*", + ], +}) -if (success) { - // password reset successfully -} +// customers[0].account_holder_link?.[0]?.account_holder ``` -The method accepts as a first parameter the ID of the provider, and as a second parameter the data necessary to reset the password. +### useQueryGraphStep -In the example above, you use the `emailpass` provider, so you have to pass an object having an `email` and `password` properties. +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" -If the returned `success` property is `true`, the password has reset successfully. +// ... + +const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: [ + "account_holder_link.account_holder.*", + ], +}) + +// customers[0].account_holder_link?.[0]?.account_holder +``` + +### Manage with Link + +To manage the account holders of a customer, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + +*** + +## Cart Module + +Medusa defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model and the `Customer` data model. Because the link is read-only from the `Cart`'s side, you can only retrieve the customer of a cart, and not the other way around. + +### Retrieve with Query + +To retrieve the customer of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: + +### query.graph + +```ts +const { data: carts } = await query.graph({ + entity: "cart", + fields: [ + "customer.*", + ], +}) + +// carts.customer +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: carts } = useQueryGraphStep({ + entity: "cart", + fields: [ + "customer.*", + ], +}) + +// carts.customer +``` + +*** + +## Order Module + +Medusa defines a read-only link between the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model and the `Customer` data model. Because the link is read-only from the `Order`'s side, you can only retrieve the customer of an order, and not the other way around. + +### Retrieve with Query + +To retrieve the customer of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: + +### query.graph + +```ts +const { data: orders } = await query.graph({ + entity: "order", + fields: [ + "customer.*", + ], +}) + +// orders.customer +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "customer.*", + ], +}) + +// orders.customer +``` # How to Create an Actor Type @@ -22643,213 +22766,6 @@ In the workflow, you: You can use this workflow when deleting a manager, such as in an API route. -# Auth Module Options - -In this document, you'll learn about the options of the Auth Module. - -## providers - -The `providers` option is an array of auth module providers. - -When the Medusa application starts, these providers are registered and can be used to handle authentication. - -By default, the `emailpass` provider is registered to authenticate customers and admin users. - -For example: - -```ts title="medusa-config.ts" -import { Modules, ContainerRegistrationKeys } from "@medusajs/framework/utils" - -// ... - -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "@medusajs/medusa/auth", - dependencies: [Modules.CACHE, ContainerRegistrationKeys.LOGGER], - options: { - providers: [ - { - resolve: "@medusajs/medusa/auth-emailpass", - id: "emailpass", - options: { - // provider options... - }, - }, - ], - }, - }, - ], -}) -``` - -The `providers` option is an array of objects that accept the following properties: - -- `resolve`: A string indicating the package name of the module provider or the path to it relative to the `src` directory. -- `id`: A string indicating the provider's unique name or ID. -- `options`: An optional object of the module provider's options. - -*** - -## Auth CORS - -The Medusa application's authentication API routes are defined under the `/auth` prefix that requires setting the `authCors` property of the `http` configuration. - -By default, the Medusa application you created will have an `AUTH_CORS` environment variable, which is used as the value of `authCors`. - -Refer to [Medusa's configuration guide](https://docs.medusajs.com/docs/learn/configurations/medusa-config#httpauthCors/index.html.md) to learn more about the `authCors` configuration. - -*** - -## authMethodsPerActor Configuration - -The Medusa application's configuration accept an `authMethodsPerActor` configuration which restricts the allowed auth providers used with an actor type. - -Learn more about the `authMethodsPerActor` configuration in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers#configure-allowed-auth-providers-of-actor-types/index.html.md). - - -# How to Handle Password Reset Token Event - -In this guide, you'll learn how to handle the `auth.password_reset` event, which is emitted when a request is sent to the [Generate Reset Password Token API route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#generate-reset-password-token-route/index.html.md). - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/reset-password/index.html.md) to learn how to reset your user admin password using the dashboard. - -You'll create a subscriber that listens to the event. When the event is emitted, the subscriber sends an email notification to the user. - -### Prerequisites - -- [A notification provider module, such as SendGrid](https://docs.medusajs.com/infrastructure-modules/notification/sendgrid/index.html.md) - -## 1. Create Subscriber - -The first step is to create a subscriber that listens to the `auth.password_reset` and sends the user a notification with instructions to reset their password. - -Create the file `src/subscribers/handle-reset.ts` with the following content: - -```ts title="src/subscribers/handle-reset.ts" highlights={highlights} collapsibleLines="1-6" expandMoreLabel="Show Imports" -import { - SubscriberArgs, - type SubscriberConfig, -} from "@medusajs/medusa" -import { Modules } from "@medusajs/framework/utils" - -export default async function resetPasswordTokenHandler({ - event: { data: { - entity_id: email, - token, - actor_type, - } }, - container, -}: SubscriberArgs<{ entity_id: string, token: string, actor_type: string }>) { - const notificationModuleService = container.resolve( - Modules.NOTIFICATION - ) - - const urlPrefix = actor_type === "customer" ? - "https://storefront.com" : - "https://admin.com/app" - - await notificationModuleService.createNotifications({ - to: email, - channel: "email", - template: "reset-password-template", - data: { - // a URL to a frontend application - url: `${urlPrefix}/reset-password?token=${token}&email=${email}`, - }, - }) -} - -export const config: SubscriberConfig = { - event: "auth.password_reset", -} -``` - -You subscribe to the `auth.password_reset` event. The event has a data payload object with the following properties: - -- `entity_id`: The identifier of the user. When using the `emailpass` provider, it's the user's email. -- `token`: The token to reset the user's password. -- `actor_type`: The user's actor type. For example, if the user is a customer, the `actor_type` is `customer`. If it's an admin user, the `actor_type` is `user`. - -This event's payload previously had an `actorType` field. It was renamed to `actor_type` after [Medusa v2.0.7](https://github.com/medusajs/medusa/releases/tag/v2.0.7). - -In the subscriber, you: - -- Decide the frontend URL based on whether the user is a customer or admin user by checking the value of `actor_type`. -- Resolve the Notification Module and use its `createNotifications` method to send the notification. -- You pass to the `createNotifications` method an object having the following properties: - - `to`: The identifier to send the notification to, which in this case is the email. - - `channel`: The channel to send the notification through, which in this case is email. - - `template`: The template ID in the third-party service. - - `data`: The data payload to pass to the template. You pass the URL to redirect the user to. You must pass the token and email in the URL so that the frontend can send them later to the Medusa application when reseting the password. - -*** - -## 2. Test it Out: Generate Reset Password Token - -To test the subscriber out, send a request to the `/auth/{actor_type}/{auth_provider}/reset-password` API route, replacing `{actor_type}` and `{auth_provider}` with the user's actor type and provider used for authentication respectively. - -For example, to generate a reset password token for an admin user using the `emailpass` provider, send the following request: - -```bash -curl --location 'http://localhost:9000/auth/user/emailpass/reset-password' \ ---header 'Content-Type: application/json' \ ---data-raw '{ - "identifier": "admin-test@gmail.com" -}' -``` - -In the request body, you must pass an `identifier` parameter. Its value is the user's identifier, which is the email in this case. - -If the token is generated successfully, the request returns a response with `201` status code. In the terminal, you'll find the following message indicating that the `auth.password_reset` event was emitted and your subscriber ran: - -```plain -info: Processing auth.password_reset which has 1 subscribers -``` - -The notification is sent to the user with the frontend URL to enter a new password. - -*** - -## Next Steps: Implementing Frontend - -In your frontend, you must have a page that accepts `token` and `email` query parameters. - -The page shows the user password fields to enter their new password, then submits the new password, token, and email to the [Reset Password Route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#reset-password-route/index.html.md). - -### Examples - -- [Storefront Guide: Reset Customer Password](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/reset-password/index.html.md) - - -# Fulfillment Module Provider - -In this document, you’ll learn what a fulfillment module provider is. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/locations#manage-fulfillment-providers/index.html.md) to learn how to add a fulfillment provider to a location using the dashboard. - -## What’s a Fulfillment Module Provider? - -A fulfillment module provider handles fulfilling items, typically using a third-party integration. - -Fulfillment module providers registered in the Fulfillment Module's [options](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md) are stored and represented by the [FulfillmentProvider data model](https://docs.medusajs.com/references/fulfillment/models/FulfillmentProvider/index.html.md). - -*** - -## Configure Fulfillment Providers - -The Fulfillment Module accepts a `providers` option that allows you to register providers in your application. - -Learn more about the `providers` option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md). - -*** - -## How to Create a Fulfillment Provider? - -Refer to [this guide](https://docs.medusajs.com/references/fulfillment/provider/index.html.md) to learn how to create a fulfillment module provider. - - # Fulfillment Concepts In this document, you’ll learn about some basic fulfillment concepts. @@ -22898,57 +22814,31 @@ A shipping profile defines a type of items that are shipped in a similar manner. A shipping profile is represented by the [ShippingProfile data model](https://docs.medusajs.com/references/fulfillment/models/ShippingProfile/index.html.md). It only defines the profile’s details, but it’s associated with the shipping options available for the item type. -# Item Fulfillment +# Fulfillment Module Provider -In this document, you’ll learn about the concepts of item fulfillment. +In this document, you’ll learn what a fulfillment module provider is. -## Fulfillment Data Model +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/locations#manage-fulfillment-providers/index.html.md) to learn how to add a fulfillment provider to a location using the dashboard. -A fulfillment is the shipping and delivery of one or more items to the customer. It’s represented by the [Fulfillment data model](https://docs.medusajs.com/references/fulfillment/models/Fulfillment/index.html.md). +## What’s a Fulfillment Module Provider? + +A fulfillment module provider handles fulfilling items, typically using a third-party integration. + +Fulfillment module providers registered in the Fulfillment Module's [options](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md) are stored and represented by the [FulfillmentProvider data model](https://docs.medusajs.com/references/fulfillment/models/FulfillmentProvider/index.html.md). *** -## Fulfillment Processing by a Fulfillment Provider +## Configure Fulfillment Providers -A fulfillment is associated with a fulfillment provider that handles all its processing, such as creating a shipment for the fulfillment’s items. +The Fulfillment Module accepts a `providers` option that allows you to register providers in your application. -The fulfillment is also associated with a shipping option of that provider, which determines how the item is shipped. - -![A diagram showcasing the relation between a fulfillment, fulfillment provider, and shipping option](https://res.cloudinary.com/dza7lstvk/image/upload/v1712331947/Medusa%20Resources/fulfillment-shipping-option_jk9ndp.jpg) +Learn more about the `providers` option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md). *** -## data Property +## How to Create a Fulfillment Provider? -The `Fulfillment` data model has a `data` property that holds any necessary data for the third-party fulfillment provider to process the fulfillment. - -For example, the `data` property can hold the ID of the fulfillment in the third-party provider. The associated fulfillment provider then uses it whenever it retrieves the fulfillment’s details. - -*** - -## Fulfillment Items - -A fulfillment is used to fulfill one or more items. Each item is represented by the `FulfillmentItem` data model. - -The fulfillment item holds details relevant to fulfilling the item, such as barcode, SKU, and quantity to fulfill. - -![A diagram showcasing the relation between fulfillment and fulfillment items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712332114/Medusa%20Resources/fulfillment-item_etzxb0.jpg) - -*** - -## Fulfillment Label - -Once a shipment is created for the fulfillment, you can store its tracking number, URL, or other related details as a label, represented by the `FulfillmentLabel` data model. - -*** - -## Fulfillment Status - -The `Fulfillment` data model has three properties to keep track of the current status of the fulfillment: - -- `packed_at`: The date the fulfillment was packed. If set, then the fulfillment has been packed. -- `shipped_at`: The date the fulfillment was shipped. If set, then the fulfillment has been shipped. -- `delivered_at`: The date the fulfillment was delivered. If set, then the fulfillment has been delivered. +Refer to [this guide](https://docs.medusajs.com/references/fulfillment/provider/index.html.md) to learn how to create a fulfillment module provider. # Links between Fulfillment Module and Other Modules @@ -23311,6 +23201,59 @@ createRemoteLinkStep({ ``` +# Item Fulfillment + +In this document, you’ll learn about the concepts of item fulfillment. + +## Fulfillment Data Model + +A fulfillment is the shipping and delivery of one or more items to the customer. It’s represented by the [Fulfillment data model](https://docs.medusajs.com/references/fulfillment/models/Fulfillment/index.html.md). + +*** + +## Fulfillment Processing by a Fulfillment Provider + +A fulfillment is associated with a fulfillment provider that handles all its processing, such as creating a shipment for the fulfillment’s items. + +The fulfillment is also associated with a shipping option of that provider, which determines how the item is shipped. + +![A diagram showcasing the relation between a fulfillment, fulfillment provider, and shipping option](https://res.cloudinary.com/dza7lstvk/image/upload/v1712331947/Medusa%20Resources/fulfillment-shipping-option_jk9ndp.jpg) + +*** + +## data Property + +The `Fulfillment` data model has a `data` property that holds any necessary data for the third-party fulfillment provider to process the fulfillment. + +For example, the `data` property can hold the ID of the fulfillment in the third-party provider. The associated fulfillment provider then uses it whenever it retrieves the fulfillment’s details. + +*** + +## Fulfillment Items + +A fulfillment is used to fulfill one or more items. Each item is represented by the `FulfillmentItem` data model. + +The fulfillment item holds details relevant to fulfilling the item, such as barcode, SKU, and quantity to fulfill. + +![A diagram showcasing the relation between fulfillment and fulfillment items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712332114/Medusa%20Resources/fulfillment-item_etzxb0.jpg) + +*** + +## Fulfillment Label + +Once a shipment is created for the fulfillment, you can store its tracking number, URL, or other related details as a label, represented by the `FulfillmentLabel` data model. + +*** + +## Fulfillment Status + +The `Fulfillment` data model has three properties to keep track of the current status of the fulfillment: + +- `packed_at`: The date the fulfillment was packed. If set, then the fulfillment has been packed. +- `shipped_at`: The date the fulfillment was shipped. If set, then the fulfillment has been shipped. +- `delivered_at`: The date the fulfillment was delivered. If set, then the fulfillment has been delivered. + + # Fulfillment Module Options In this document, you'll learn about the options of the Fulfillment Module. @@ -23356,43 +23299,6 @@ The `providers` option is an array of objects that accept the following properti - `options`: An optional object of the module provider's options. -# Cart Concepts - -In this document, you’ll get an overview of the main concepts of a cart. - -## Shipping and Billing Addresses - -A cart has a shipping and billing address. Both of these addresses are represented by the [Address data model](https://docs.medusajs.com/references/cart/models/Address/index.html.md). - -![A diagram showcasing the relation between the Cart and Address data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711532392/Medusa%20Resources/cart-addresses_ls6qmv.jpg) - -*** - -## Line Items - -A line item, represented by the [LineItem](https://docs.medusajs.com/references/cart/models/LineItem/index.html.md) data model, is a quantity of a product variant added to the cart. A cart has multiple line items. - -A line item stores some of the product variant’s properties, such as the `product_title` and `product_description`. It also stores data related to the item’s quantity and price. - -In the Medusa application, a product variant is implemented in the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md). - -*** - -## Shipping Methods - -A shipping method, represented by the [ShippingMethod data model](https://docs.medusajs.com/references/cart/models/ShippingMethod/index.html.md), is used to fulfill the items in the cart after the order is placed. A cart can have more than one shipping method. - -In the Medusa application, the shipping method is created from a shipping option, available through the [Fulfillment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/index.html.md). Its ID is stored in the `shipping_option_id` property of the method. - -### data Property - -After an order is placed, you can use a third-party fulfillment provider to fulfill its shipments. - -If the fulfillment provider requires additional custom data to be passed along from the checkout process, set this data in the `ShippingMethod`'s `data` property. - -The `data` property is an object used to store custom data relevant later for fulfillment. - - # Shipping Option In this document, you’ll learn about shipping options and their rules. @@ -23461,6 +23367,43 @@ When fulfilling an item, you might use a third-party fulfillment provider that r The `ShippingOption` data model has a `data` property. It's an object that stores custom data relevant later when creating and processing a fulfillment. +# Cart Concepts + +In this document, you’ll get an overview of the main concepts of a cart. + +## Shipping and Billing Addresses + +A cart has a shipping and billing address. Both of these addresses are represented by the [Address data model](https://docs.medusajs.com/references/cart/models/Address/index.html.md). + +![A diagram showcasing the relation between the Cart and Address data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711532392/Medusa%20Resources/cart-addresses_ls6qmv.jpg) + +*** + +## Line Items + +A line item, represented by the [LineItem](https://docs.medusajs.com/references/cart/models/LineItem/index.html.md) data model, is a quantity of a product variant added to the cart. A cart has multiple line items. + +A line item stores some of the product variant’s properties, such as the `product_title` and `product_description`. It also stores data related to the item’s quantity and price. + +In the Medusa application, a product variant is implemented in the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md). + +*** + +## Shipping Methods + +A shipping method, represented by the [ShippingMethod data model](https://docs.medusajs.com/references/cart/models/ShippingMethod/index.html.md), is used to fulfill the items in the cart after the order is placed. A cart can have more than one shipping method. + +In the Medusa application, the shipping method is created from a shipping option, available through the [Fulfillment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/index.html.md). Its ID is stored in the `shipping_option_id` property of the method. + +### data Property + +After an order is placed, you can use a third-party fulfillment provider to fulfill its shipments. + +If the fulfillment provider requires additional custom data to be passed along from the checkout process, set this data in the `ShippingMethod`'s `data` property. + +The `data` property is an object used to store custom data relevant later for fulfillment. + + # Links between Cart Module and Other Modules This document showcases the module links defined between the Cart Module and other Commerce Modules. @@ -23900,6 +23843,81 @@ const { data: carts } = useQueryGraphStep({ ``` +# Tax Lines in Cart Module + +In this document, you’ll learn about tax lines in a cart and how to retrieve tax lines with the Tax Module. + +## What are Tax Lines? + +A tax line indicates the tax rate of a line item or a shipping method. The [LineItemTaxLine data model](https://docs.medusajs.com/references/cart/models/LineItemTaxLine/index.html.md) represents a line item’s tax line, and the [ShippingMethodTaxLine data model](https://docs.medusajs.com/references/cart/models/ShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line. + +![A diagram showcasing the relation between other data models and the tax line models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711534431/Medusa%20Resources/cart-tax-lines_oheaq6.jpg) + +*** + +## Tax Inclusivity + +By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount, and then adding them to the item/method’s subtotal. + +However, line items and shipping methods have an `is_tax_inclusive` property that, when enabled, indicates that the item or method’s price already includes taxes. + +So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal. + +The following diagram is a simplified showcase of how a subtotal is calculated from the taxes perspective. + +![A diagram showing an example of calculating the subtotal of a line item using its taxes](https://res.cloudinary.com/dza7lstvk/image/upload/v1711535295/Medusa%20Resources/cart-tax-inclusive_shpr3t.jpg) + +For example, if a line item's amount is `5000`, the tax rate is `10`, and tax inclusivity is enabled, the tax amount is 10% of `5000`, which is `500`, making the unit price of the line item `4500`. + +*** + +## Retrieve Tax Lines + +When using the Cart and Tax modules together, you can use the `getTaxLines` method of the Tax Module’s main service. It retrieves the tax lines for a cart’s line items and shipping methods. + +```ts +// retrieve the cart +const cart = await cartModuleService.retrieveCart("cart_123", { + relations: [ + "items.tax_lines", + "shipping_methods.tax_lines", + "shipping_address", + ], +}) + +// retrieve the tax lines +const taxLines = await taxModuleService.getTaxLines( + [ + ...(cart.items as TaxableItemDTO[]), + ...(cart.shipping_methods as TaxableShippingDTO[]), + ], + { + address: { + ...cart.shipping_address, + country_code: + cart.shipping_address.country_code || "us", + }, + } +) +``` + +Then, use the returned tax lines to set the line items and shipping methods’ tax lines: + +```ts +// set line item tax lines +await cartModuleService.setLineItemTaxLines( + cart.id, + taxLines.filter((line) => "line_item_id" in line) +) + +// set shipping method tax lines +await cartModuleService.setLineItemTaxLines( + cart.id, + taxLines.filter((line) => "shipping_line_id" in line) +) +``` + + # Promotions Adjustments in Carts In this document, you’ll learn how a promotion is applied to a cart’s line items and shipping methods using adjustment lines. @@ -24018,2581 +24036,6 @@ await cartModuleService.setShippingMethodAdjustments( ``` -# Order Claim - -In this document, you’ll learn about order claims. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/claims/index.html.md) to learn how to manage an order's claims using the dashboard. - -## What is a Claim? - -When a customer receives a defective or incorrect item, the merchant can create a claim to refund or replace the item. - -The [OrderClaim data model](https://docs.medusajs.com/references/order/models/OrderClaim/index.html.md) represents a claim. - -*** - -## Claim Type - -The `Claim` data model has a `type` property whose value indicates the type of the claim: - -- `refund`: the items are returned, and the customer is refunded. -- `replace`: the items are returned, and the customer receives new items. - -*** - -## Old and Replacement Items - -When the claim is created, a return, represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md), is also created to handle receiving the old items from the customer. - -Learn more about returns in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). - -If the claim’s type is `replace`, replacement items are represented by the [ClaimItem data model](https://docs.medusajs.com/references/order/models/OrderClaimItem/index.html.md). - -*** - -## Claim Shipping Methods - -A claim uses shipping methods to send the replacement items to the customer. These methods are represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md). - -The shipping methods for the returned items are associated with the claim's return, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods/index.html.md). - -*** - -## Claim Refund - -If the claim’s type is `refund`, the amount to be refunded is stored in the `refund_amount` property. - -The [Transaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md) represents the refunds made for the claim. - -*** - -## How Claims Impact an Order’s Version - -When a claim is confirmed, the order’s version is incremented. - - -# Tax Lines in Cart Module - -In this document, you’ll learn about tax lines in a cart and how to retrieve tax lines with the Tax Module. - -## What are Tax Lines? - -A tax line indicates the tax rate of a line item or a shipping method. The [LineItemTaxLine data model](https://docs.medusajs.com/references/cart/models/LineItemTaxLine/index.html.md) represents a line item’s tax line, and the [ShippingMethodTaxLine data model](https://docs.medusajs.com/references/cart/models/ShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line. - -![A diagram showcasing the relation between other data models and the tax line models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711534431/Medusa%20Resources/cart-tax-lines_oheaq6.jpg) - -*** - -## Tax Inclusivity - -By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount, and then adding them to the item/method’s subtotal. - -However, line items and shipping methods have an `is_tax_inclusive` property that, when enabled, indicates that the item or method’s price already includes taxes. - -So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal. - -The following diagram is a simplified showcase of how a subtotal is calculated from the taxes perspective. - -![A diagram showing an example of calculating the subtotal of a line item using its taxes](https://res.cloudinary.com/dza7lstvk/image/upload/v1711535295/Medusa%20Resources/cart-tax-inclusive_shpr3t.jpg) - -For example, if a line item's amount is `5000`, the tax rate is `10`, and tax inclusivity is enabled, the tax amount is 10% of `5000`, which is `500`, making the unit price of the line item `4500`. - -*** - -## Retrieve Tax Lines - -When using the Cart and Tax modules together, you can use the `getTaxLines` method of the Tax Module’s main service. It retrieves the tax lines for a cart’s line items and shipping methods. - -```ts -// retrieve the cart -const cart = await cartModuleService.retrieveCart("cart_123", { - relations: [ - "items.tax_lines", - "shipping_methods.tax_lines", - "shipping_address", - ], -}) - -// retrieve the tax lines -const taxLines = await taxModuleService.getTaxLines( - [ - ...(cart.items as TaxableItemDTO[]), - ...(cart.shipping_methods as TaxableShippingDTO[]), - ], - { - address: { - ...cart.shipping_address, - country_code: - cart.shipping_address.country_code || "us", - }, - } -) -``` - -Then, use the returned tax lines to set the line items and shipping methods’ tax lines: - -```ts -// set line item tax lines -await cartModuleService.setLineItemTaxLines( - cart.id, - taxLines.filter((line) => "line_item_id" in line) -) - -// set shipping method tax lines -await cartModuleService.setLineItemTaxLines( - cart.id, - taxLines.filter((line) => "shipping_line_id" in line) -) -``` - - -# Order Edit - -In this document, you'll learn about order edits. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/edit/index.html.md) to learn how to edit an order's items using the dashboard. - -## What is an Order Edit? - -A merchant can edit an order to add new items or change the quantity of existing items in the order. - -An order edit is represented by the [OrderChange data model](https://docs.medusajs.com/references/order/models/OrderChange/index.html.md). - -The `OrderChange` data model is associated with any type of change, including a return or exchange. However, its `change_type` property distinguishes the type of change it's making. - -In the case of an order edit, the `OrderChange`'s type is `edit`. - -*** - -## Add Items in an Order Edit - -When the merchant adds new items to the order in the order edit, the item is added as an [OrderItem](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). - -Also, an `OrderChangeAction` is created. The [OrderChangeAction data model](https://docs.medusajs.com/references/order/models/OrderChangeAction/index.html.md) represents a change made by an `OrderChange`, such as an item added. - -So, when an item is added, an `OrderChangeAction` is created with the type `ITEM_ADD`. In its `details` property, the item's ID, price, and quantity are stored. - -*** - -## Update Items in an Order Edit - -A merchant can update an existing item's quantity or price. - -This change is added as an `OrderChangeAction` with the type `ITEM_UPDATE`. In its `details` property, the item's ID, new price, and new quantity are stored. - -*** - -## Shipping Methods of New Items in the Edit - -Adding new items to the order requires adding shipping methods for those items. - -These shipping methods are represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). Also, an `OrderChangeAction` is created with the type `SHIPPING_ADD` - -*** - -## How Order Edits Impact an Order’s Version - -When an order edit is confirmed, the order’s version is incremented. - -*** - -## Payments and Refunds for Order Edit Changes - -Once the Order Edit is confirmed, any additional payment or refund required can be made on the original order. - -This is determined by the comparison between the `OrderSummary` and the order's transactions, as mentioned in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/transactions#checking-outstanding-amount/index.html.md). - - -# Order Concepts - -In this document, you’ll learn about orders and related concepts - -## Order Items - -The items purchased in the order are represented by the [OrderItem data model](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). An order can have multiple items. - -![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712304722/Medusa%20Resources/order-order-items_uvckxd.jpg) - -### Item’s Product Details - -The details of the purchased products are represented by the [LineItem data model](https://docs.medusajs.com/references/order/models/OrderLineItem/index.html.md). Not only does a line item hold the details of the product, but also details related to its price, adjustments due to promotions, and taxes. - -*** - -## Order’s Shipping Method - -An order has one or more shipping methods used to handle item shipment. - -Each shipping method is represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md) that holds its details. The shipping method is linked to the order through the [OrderShipping data model](https://docs.medusajs.com/references/order/models/OrderShipping/index.html.md). - -![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719570409/Medusa%20Resources/order-shipping-method_tkggvd.jpg) - -### data Property - -When fulfilling the order, you can use a third-party fulfillment provider that requires additional custom data to be passed along from the order creation process. - -The `OrderShippingMethod` data model has a `data` property. It’s an object used to store custom data relevant later for fulfillment. - -The Medusa application passes the `data` property to the Fulfillment Module when fulfilling items. - -*** - -## Order Totals - -The order’s total amounts (including tax total, total after an item is returned, etc…) are represented by the [OrderSummary data model](https://docs.medusajs.com/references/order/models/OrderSummary/index.html.md). - -*** - -## Order Payments - -Payments made on an order, whether they’re capture or refund payments, are recorded as transactions represented by the [OrderTransaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). - -An order can have multiple transactions. The sum of these transactions must be equal to the order summary’s total. Otherwise, there’s an outstanding amount. - -Learn more about transactions in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/transactions/index.html.md). - - -# Order Change - -In this document, you'll learn about the Order Change data model and possible actions in it. - -## OrderChange Data Model - -The [OrderChange data model](https://docs.medusajs.com/references/order/models/OrderChange/index.html.md) represents any kind of change to an order, such as a return, exchange, or edit. - -Its `change_type` property indicates what the order change is created for: - -1. `edit`: The order change is making edits to the order, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/edit/index.html.md). -2. `exchange`: The order change is associated with an exchange, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/exchange/index.html.md). -3. `claim`: The order change is associated with a claim, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/claim/index.html.md). -4. `return_request` or `return_receive`: The order change is associated with a return, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). - -Once the order change is confirmed, its changes are applied on the order. - -*** - -## Order Change Actions - -The actions to perform on the original order by a change, such as adding an item, are represented by the [OrderChangeAction data model](https://docs.medusajs.com/references/order/models/OrderChangeAction/index.html.md). - -The `OrderChangeAction` has an `action` property that indicates the type of action to perform on the order, and a `details` property that holds more details related to the action. - -The following table lists the possible `action` values that Medusa uses and what `details` they carry. - -|Action|Description|Details| -|---|---|---|---|---| -|\`ITEM\_ADD\`|Add an item to the order.|\`details\`| -|\`ITEM\_UPDATE\`|Update an item in the order.|\`details\`| -|\`RETURN\_ITEM\`|Set an item to be returned.|\`details\`| -|\`RECEIVE\_RETURN\_ITEM\`|Mark a return item as received.|\`details\`| -|\`RECEIVE\_DAMAGED\_RETURN\_ITEM\`|Mark a return item that's damaged as received.|\`details\`| -|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | -|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | -|\`WRITE\_OFF\_ITEM\`|Remove an item's quantity as part of the claim, without adding the quantity back to the item variant's inventory.|\`details\`| - - -# Links between Order Module and Other Modules - -This document showcases the module links defined between the Order Module and other Commerce Modules. - -## Summary - -The Order Module has the following links to other modules: - -Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. - -|First Data Model|Second Data Model|Type|Description| -|---|---|---|---| -|| in |Read-only - has one|| -|| in |Stored - one-to-one|| -|| in |Stored - one-to-many|| -|| in |Stored - one-to-many|| -|| in |Stored - one-to-many|| -|| in |Stored - one-to-many|| -|| in |Stored - one-to-many|| -|| in |Read-only - has many|| -|| in |Stored - many-to-many|| -|| in |Read-only - has one|| -|| in |Read-only - has one|| - -*** - -## Customer Module - -Medusa defines a read-only link between the `Order` data model and the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md)'s `Customer` data model. This means you can retrieve the details of an order's customer, but you don't manage the links in a pivot table in the database. The customer of an order is determined by the `customer_id` property of the `Order` data model. - -### Retrieve with Query - -To retrieve the customer of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "customer.*", - ], -}) - -// orders[0].customer -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "customer.*", - ], -}) - -// orders[0].customer -``` - -*** - -## Cart Module - -The [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md) provides cart-management features. - -Medusa defines a link between the `Order` and `Cart` data models. The order is linked to the cart used for the purchased. - -![A diagram showcasing an example of how data models from the Cart and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728375735/Medusa%20Resources/cart-order_ijwmfs.jpg) - -### Retrieve with Query - -To retrieve the cart of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `cart.*` in `fields`: - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "cart.*", - ], -}) - -// orders[0].cart -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "cart.*", - ], -}) - -// orders[0].cart -``` - -### Manage with Link - -To manage the cart of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.CART]: { - cart_id: "cart_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.CART]: { - cart_id: "cart_123", - }, -}) -``` - -*** - -## Fulfillment Module - -A fulfillment is created for an orders' items. Medusa defines a link between the `Fulfillment` and `Order` data models. - -![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716549903/Medusa%20Resources/order-fulfillment_h0vlps.jpg) - -A fulfillment is also created for a return's items. So, Medusa defines a link between the `Fulfillment` and `Return` data models. - -![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399052/Medusa%20Resources/Social_Media_Graphics_2024_Order_Return_vetimk.jpg) - -### Retrieve with Query - -To retrieve the fulfillments of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `fulfillments.*` in `fields`: - -To retrieve the fulfillments of a return, pass `fulfillments.*` in `fields`. - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "fulfillments.*", - ], -}) - -// orders[0].fulfillments -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "fulfillments.*", - ], -}) - -// orders[0].fulfillments -``` - -### Manage with Link - -To manage the fulfillments of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.FULFILLMENT]: { - fulfillment_id: "ful_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.FULFILLMENT]: { - fulfillment_id: "ful_123", - }, -}) -``` - -*** - -## Payment Module - -An order's payment details are stored in a payment collection. This also applies for claims and exchanges. - -So, Medusa defines links between the `PaymentCollection` data model and the `Order`, `OrderClaim`, and `OrderExchange` data models. - -![A diagram showcasing an example of how data models from the Order and Payment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716554726/Medusa%20Resources/order-payment_ubdwok.jpg) - -### Retrieve with Query - -To retrieve the payment collections of an order, order exchange, or order claim with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `payment_collections.*` in `fields`: - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "payment_collections.*", - ], -}) - -// orders[0].payment_collections -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "payment_collections.*", - ], -}) - -// orders[0].payment_collections -``` - -### Manage with Link - -To manage the payment collections of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.PAYMENT]: { - payment_collection_id: "paycol_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.PAYMENT]: { - payment_collection_id: "paycol_123", - }, -}) -``` - -*** - -## Product Module - -Medusa defines read-only links between: - -- the `OrderLineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `Product` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `OrderLineItem` data model. -- the `OrderLineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `ProductVariant` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `OrderLineItem` data model. - -### Retrieve with Query - -To retrieve the variant of a line item with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`: - -To retrieve the product, pass `product.*` in `fields`. - -### query.graph - -```ts -const { data: lineItems } = await query.graph({ - entity: "order_line_item", - fields: [ - "variant.*", - ], -}) - -// lineItems.variant -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: lineItems } = useQueryGraphStep({ - entity: "order_line_item", - fields: [ - "variant.*", - ], -}) - -// lineItems.variant -``` - -*** - -## Promotion Module - -An order is associated with the promotion applied on it. Medusa defines a link between the `Order` and `Promotion` data models. - -![A diagram showcasing an example of how data models from the Order and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716555015/Medusa%20Resources/order-promotion_dgjzzd.jpg) - -### Retrieve with Query - -To retrieve the promotion applied on an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `promotion.*` in `fields`: - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "promotion.*", - ], -}) - -// orders[0].promotion -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "promotion.*", - ], -}) - -// orders[0].promotion -``` - -### Manage with Link - -To manage the promotion of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", - }, -}) -``` - -*** - -## Region Module - -Medusa defines a read-only link between the `Order` data model and the [Region Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/region/index.html.md)'s `Region` data model. This means you can retrieve the details of an order's region, but you don't manage the links in a pivot table in the database. The region of an order is determined by the `region_id` property of the `Order` data model. - -### Retrieve with Query - -To retrieve the region of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `region.*` in `fields`: - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "region.*", - ], -}) - -// orders[0].region -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "region.*", - ], -}) - -// orders[0].region -``` - -*** - -## Sales Channel Module - -Medusa defines a read-only link between the `Order` data model and the [Sales Channel Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md)'s `SalesChannel` data model. This means you can retrieve the details of an order's sales channel, but you don't manage the links in a pivot table in the database. The sales channel of an order is determined by the `sales_channel_id` property of the `Order` data model. - -### Retrieve with Query - -To retrieve the sales channel of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channel.*` in `fields`: - -### query.graph - -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "sales_channel.*", - ], -}) - -// orders[0].sales_channel -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "sales_channel.*", - ], -}) - -// orders[0].sales_channel -``` - - -# Order Exchange - -In this document, you’ll learn about order exchanges. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/exchanges/index.html.md) to learn how to manage an order's exchanges using the dashboard. - -## What is an Exchange? - -An exchange is the replacement of an item that the customer ordered with another. - -A merchant creates the exchange, specifying the items to be replaced and the new items to be sent. - -The [OrderExchange data model](https://docs.medusajs.com/references/order/models/OrderExchange/index.html.md) represents an exchange. - -*** - -## Returned and New Items - -When the exchange is created, a return, represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md), is created to handle receiving the items back from the customer. - -Learn more about returns in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). - -The [OrderExchangeItem data model](https://docs.medusajs.com/references/order/models/OrderExchangeItem/index.html.md) represents the new items to be sent to the customer. - -*** - -## Exchange Shipping Methods - -An exchange has shipping methods used to send the new items to the customer. They’re represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md). - -The shipping methods for the returned items are associated with the exchange's return, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods/index.html.md). - -*** - -## Exchange Payment - -The `Exchange` data model has a `difference_due` property that stores the outstanding amount. - -|Condition|Result| -|---|---|---| -|\`difference\_due \< 0\`|Merchant owes the customer a refund of the | -|\`difference\_due > 0\`|Merchant requires additional payment from the customer of the | -|\`difference\_due = 0\`|No payment processing is required.| - -Any payment or refund made is stored in the [Transaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). - -*** - -## How Exchanges Impact an Order’s Version - -When an exchange is confirmed, the order’s version is incremented. - - -# Order Versioning - -In this document, you’ll learn how an order and its details are versioned. - -## What's Versioning? - -Versioning means assigning a version number to a record, such as an order and its items. This is useful to view the different versions of the order following changes in its lifetime. - -When changes are made on an order, such as an item is added or returned, the order's version changes. - -*** - -## version Property - -The `Order` and `OrderSummary` data models have a `version` property that indicates the current version. By default, its value is `1`. - -Other order-related data models, such as `OrderItem`, also has a `version` property, but it indicates the version it belongs to. - -*** - -## How the Version Changes - -When the order is changed, such as an item is exchanged, this changes the version of the order and its related data: - -1. The version of the order and its summary is incremented. -2. Related order data that have a `version` property, such as the `OrderItem`, are duplicated. The duplicated item has the new version, whereas the original item has the previous version. - -When the order is retrieved, only the related data having the same version is retrieved. - - -# Promotions Adjustments in Orders - -In this document, you’ll learn how a promotion is applied to an order’s items and shipping methods using adjustment lines. - -## What are Adjustment Lines? - -An adjustment line indicates a change to a line item or a shipping method’s amount. It’s used to apply promotions or discounts on an order. - -The [OrderLineItemAdjustment data model](https://docs.medusajs.com/references/order/models/OrderLineItemAdjustment/index.html.md) represents changes on a line item, and the [OrderShippingMethodAdjustment data model](https://docs.medusajs.com/references/order/models/OrderShippingMethodAdjustment/index.html.md) represents changes on a shipping method. - -![A diagram showcasing the relation between an order, its items and shipping methods, and their adjustment lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712306017/Medusa%20Resources/order-adjustments_myflir.jpg) - -The `amount` property of the adjustment line indicates the amount to be discounted from the original amount. - -The ID of the applied promotion is stored in the `promotion_id` property of the adjustment line. - -*** - -## Discountable Option - -The `OrderLineItem` data model has an `is_discountable` property that indicates whether promotions can be applied to the line item. It’s enabled by default. - -When disabled, a promotion can’t be applied to a line item. In the context of the Promotion Module, the promotion isn’t applied to the line item even if it matches its rules. - -*** - -## Promotion Actions - -When using the Order and Promotion modules together, use the [computeActions method of the Promotion Module’s main service](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). It retrieves the actions of line items and shipping methods. - -Learn more about actions in the [Promotion Module’s documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md). - -```ts collapsibleLines="1-10" expandButtonLabel="Show Imports" -import { - ComputeActionAdjustmentLine, - ComputeActionItemLine, - ComputeActionShippingLine, - // ... -} from "@medusajs/framework/types" - -// ... - -// retrieve the order -const order = await orderModuleService.retrieveOrder("ord_123", { - relations: [ - "items.item.adjustments", - "shipping_methods.shipping_method.adjustments", - ], -}) -// retrieve the line item adjustments -const lineItemAdjustments: ComputeActionItemLine[] = [] -order.items.forEach((item) => { - const filteredAdjustments = item.adjustments?.filter( - (adjustment) => adjustment.code !== undefined - ) as unknown as ComputeActionAdjustmentLine[] - if (filteredAdjustments.length) { - lineItemAdjustments.push({ - ...item, - ...item.detail, - adjustments: filteredAdjustments, - }) - } -}) - -//retrieve shipping method adjustments -const shippingMethodAdjustments: ComputeActionShippingLine[] = - [] -order.shipping_methods.forEach((shippingMethod) => { - const filteredAdjustments = - shippingMethod.adjustments?.filter( - (adjustment) => adjustment.code !== undefined - ) as unknown as ComputeActionAdjustmentLine[] - if (filteredAdjustments.length) { - shippingMethodAdjustments.push({ - ...shippingMethod, - adjustments: filteredAdjustments, - }) - } -}) - -// compute actions -const actions = await promotionModuleService.computeActions( - ["promo_123"], - { - items: lineItemAdjustments, - shipping_methods: shippingMethodAdjustments, - // TODO infer from cart or region - currency_code: "usd", - } -) -``` - -The `computeActions` method accepts the existing adjustments of line items and shipping methods to compute the actions accurately. - -Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the order’s line items and the shipping method’s adjustments. - -```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" -import { - AddItemAdjustmentAction, - AddShippingMethodAdjustment, - // ... -} from "@medusajs/framework/types" - -// ... - -await orderModuleService.setOrderLineItemAdjustments( - order.id, - actions.filter( - (action) => action.action === "addItemAdjustment" - ) as AddItemAdjustmentAction[] -) - -await orderModuleService.setOrderShippingMethodAdjustments( - order.id, - actions.filter( - (action) => - action.action === "addShippingMethodAdjustment" - ) as AddShippingMethodAdjustment[] -) -``` - - -# Order Return - -In this document, you’ll learn about order returns. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/returns/index.html.md) to learn how to manage an order's returns using the dashboard. - -## What is a Return? - -A return is the return of items delivered from the customer back to the merchant. It is represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md). - -A return is requested either by the customer from the storefront, or the merchant from the admin. Medusa supports an automated Return Merchandise Authorization (RMA) flow. - -![Diagram showcasing the automated RMA flow.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719578128/Medusa%20Resources/return-rma_pzprwq.jpg) - -Once the merchant receives the returned items, they mark the return as received. - -*** - -## Returned Items - -The items to be returned are represented by the [ReturnItem data model](references/order/models/ReturnItem). - -The `ReturnItem` model has two properties storing the item's quantity: - -1. `received_quantity`: The quantity of the item that's received and can be added to the item's inventory quantity. -2. `damaged_quantity`: The quantity of the item that's damaged, meaning it can't be sold again or added to the item's inventory quantity. - -*** - -## Return Shipping Methods - -A return has shipping methods used to return the items to the merchant. The shipping methods are represented by the [OrderShippingMethod data model](references/order/models/OrderShippingMethod). - -In the Medusa application, the shipping method for a return is created only from a shipping option, provided by the Fulfillment Module, that has the rule `is_return` enabled. - -*** - -## Refund Payment - -The `refund_amount` property of the `Return` data model holds the amount a merchant must refund the customer. - -The [OrderTransaction data model](references/order/models/OrderTransaction) represents the refunds made for the return. - -*** - -## Returns in Exchanges and Claims - -When a merchant creates an exchange or a claim, it includes returning items from the customer. - -The `Return` data model also represents the return of these items. In this case, the return is associated with the exchange or claim it was created for. - -*** - -## How Returns Impact an Order’s Version - -The order’s version is incremented when: - -1. A return is requested. -2. A return is marked as received. - - -# Transactions - -In this document, you’ll learn about an order’s transactions and its use. - -## What is a Transaction? - -A transaction represents any order payment process, such as capturing or refunding an amount. It’s represented by the [OrderTransaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). - -The transaction’s main purpose is to ensure a correct balance between paid and outstanding amounts. - -Transactions are also associated with returns, claims, and exchanges if additional payment or refund is required. - -*** - -## Checking Outstanding Amount - -The order’s total amounts are stored in the `OrderSummary`'s `totals` property, which is a JSON object holding the total details of the order. - -```json -{ - "totals": { - "total": 30, - "subtotal": 30, - // ... - } -} -``` - -To check the outstanding amount of the order, its transaction amounts are summed. Then, the following conditions are checked: - -|Condition|Result| -|---|---|---| -|summary’s total - transaction amounts total = 0|There’s no outstanding amount.| -|summary’s total - transaction amounts total > 0|The customer owes additional payment to the merchant.| -|summary’s total - transaction amounts total \< 0|The merchant owes the customer a refund.| - -*** - -## Transaction Reference - -The Order Module doesn’t provide payment processing functionalities, so it doesn’t store payments that can be processed. Payment functionalities are provided by the [Payment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/index.html.md). - -The `OrderTransaction` data model has two properties that determine which data model and record holds the actual payment’s details: - -- `reference`: indicates the table’s name in the database. For example, `payment` from the Payment Module. -- `reference_id`: indicates the ID of the record in the table. For example, `pay_123`. - - -# Account Holders and Saved Payment Methods - -In this documentation, you'll learn about account holders, and how they're used to save payment methods in third-party payment providers. - -Account holders are available starting from Medusa `v2.5.0`. - -## What's an Account Holder? - -An account holder represents a customer that can have saved payment methods in a third-party service. It's represented by the `AccountHolder` data model. - -It holds fields retrieved from the third-party provider, such as: - -- `external_id`: The ID of the equivalent customer or account holder in the third-party provider. -- `data`: Data returned by the payment provider when the account holder is created. - -A payment provider that supports saving payment methods for customers would create the equivalent of an account holder in the third-party provider. Then, whenever a payment method is saved, it would be saved under the account holder in the third-party provider. - -### Relation between Account Holder and Customer - -The Medusa application creates a link between the [Customer](https://docs.medusajs.com/references/customer/models/Customer/index.html.md) data model of the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md) and the `AccountHolder` data model of the Payment Module. - -This link indicates that a customer can have more than one account holder, each representing saved payment methods in different payment providers. - -Learn more about this link in the [Link to Other Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/index.html.md) guide. - -*** - -## Save Payment Methods - -If a payment provider supports saving payment methods for a customer, they must implement the following methods: - -- `createAccountHolder`: Creates an account holder in the payment provider. The Payment Module uses this method before creating the account holder in Medusa, and uses the returned data to set fields like `external_id` and `data` in the created `AccountHolder` record. -- `deleteAccountHolder`: Deletes an account holder in the payment provider. The Payment Module uses this method when an account holder is deleted in Medusa. -- `savePaymentMethod`: Saves a payment method for an account holder in the payment provider. -- `listPaymentMethods`: Lists saved payment methods in the third-party service for an account holder. This is useful when displaying the customer's saved payment methods in the storefront. - -Learn more about implementing these methods in the [Create Payment Provider guide](https://docs.medusajs.com/references/payment/provider/index.html.md). - -*** - -## Account Holder in Medusa Payment Flows - -In the Medusa application, when a payment session is created for a registered customer, the Medusa application uses the Payment Module to create an account holder for the customer. - -Consequently, the Payment Module uses the payment provider to create an account holder in the third-party service, then creates the account holder in Medusa. - -This flow is only supported if the chosen payment provider has implemented the necessary [save payment methods](#save-payment-methods). - - -# Tax Lines in Order Module - -In this document, you’ll learn about tax lines in an order. - -## What are Tax Lines? - -A tax line indicates the tax rate of a line item or a shipping method. - -The [OrderLineItemTaxLine data model](https://docs.medusajs.com/references/order/models/OrderLineItemTaxLine/index.html.md) represents a line item’s tax line, and the [OrderShippingMethodTaxLine data model](https://docs.medusajs.com/references/order/models/OrderShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line. - -![A diagram showcasing the relation between orders, items and shipping methods, and tax lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307225/Medusa%20Resources/order-tax-lines_sixujd.jpg) - -*** - -## Tax Inclusivity - -By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount and then adding it to the item/method’s subtotal. - -However, line items and shipping methods have an `is_tax_inclusive` property that, when enabled, indicates that the item or method’s price already includes taxes. - -So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal. - -The following diagram is a simplified showcase of how a subtotal is calculated from the tax perspective. - -![A diagram showcasing how a subtotal is calculated from the tax perspective](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307395/Medusa%20Resources/order-tax-inclusive_oebdnm.jpg) - -For example, if a line item's amount is `5000`, the tax rate is `10`, and `is_tax_inclusive` is enabled, the tax amount is 10% of `5000`, which is `500`. The item's unit price becomes `4500`. - - -# Links between Payment Module and Other Modules - -This document showcases the module links defined between the Payment Module and other Commerce Modules. - -## Summary - -The Payment Module has the following links to other modules: - -|First Data Model|Second Data Model|Type|Description| -|---|---|---|---| -| in ||Stored - one-to-one|| -| in ||Stored - many-to-many|| -| in ||Stored - one-to-many|| -| in ||Stored - one-to-many|| -| in ||Stored - one-to-many|| -| in ||Stored - many-to-many|| - -*** - -## Cart Module - -The Cart Module provides cart-related features, but not payment processing. - -Medusa defines a link between the `Cart` and `PaymentCollection` data models. A cart has a payment collection which holds all the authorized payment sessions and payments made related to the cart. - -Learn more about this relation in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-collection#usage-with-the-cart-module/index.html.md). - -### Retrieve with Query - -To retrieve the cart associated with the payment collection with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `cart.*` in `fields`: - -### query.graph - -```ts -const { data: paymentCollections } = await query.graph({ - entity: "payment_collection", - fields: [ - "cart.*", - ], -}) - -// paymentCollections[0].cart -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: paymentCollections } = useQueryGraphStep({ - entity: "payment_collection", - fields: [ - "cart.*", - ], -}) - -// paymentCollections[0].cart -``` - -### Manage with Link - -To manage the payment collection of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.CART]: { - cart_id: "cart_123", - }, - [Modules.PAYMENT]: { - payment_collection_id: "paycol_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.CART]: { - cart_id: "cart_123", - }, - [Modules.PAYMENT]: { - payment_collection_id: "paycol_123", - }, -}) -``` - -*** - -## Customer Module - -Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it. - -This link is available starting from Medusa `v2.5.0`. - -### Retrieve with Query - -To retrieve the customer associated with an account holder with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: - -### query.graph - -```ts -const { data: accountHolders } = await query.graph({ - entity: "account_holder", - fields: [ - "customer.*", - ], -}) - -// accountHolders[0].customer -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: accountHolders } = useQueryGraphStep({ - entity: "account_holder", - fields: [ - "customer.*", - ], -}) - -// accountHolders[0].customer -``` - -### Manage with Link - -To manage the account holders of a customer, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.CUSTOMER]: { - customer_id: "cus_123", - }, - [Modules.PAYMENT]: { - account_holder_id: "acchld_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.CUSTOMER]: { - customer_id: "cus_123", - }, - [Modules.PAYMENT]: { - account_holder_id: "acchld_123", - }, -}) -``` - -*** - -## Order Module - -An order's payment details are stored in a payment collection. This also applies for claims and exchanges. - -So, Medusa defines links between the `PaymentCollection` data model and the `Order`, `OrderClaim`, and `OrderExchange` data models. - -![A diagram showcasing an example of how data models from the Order and Payment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716554726/Medusa%20Resources/order-payment_ubdwok.jpg) - -### Retrieve with Query - -To retrieve the order of a payment collection with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `order.*` in `fields`: - -### query.graph - -```ts -const { data: paymentCollections } = await query.graph({ - entity: "payment_collection", - fields: [ - "order.*", - ], -}) - -// paymentCollections[0].order -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: paymentCollections } = useQueryGraphStep({ - entity: "payment_collection", - fields: [ - "order.*", - ], -}) - -// paymentCollections[0].order -``` - -### Manage with Link - -To manage the payment collections of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.PAYMENT]: { - payment_collection_id: "paycol_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.ORDER]: { - order_id: "order_123", - }, - [Modules.PAYMENT]: { - payment_collection_id: "paycol_123", - }, -}) -``` - -*** - -## Region Module - -You can specify for each region which payment providers are available. The Medusa application defines a link between the `PaymentProvider` and the `Region` data models. - -![A diagram showcasing an example of how resources from the Payment and Region modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711569520/Medusa%20Resources/payment-region_jyo2dz.jpg) - -This increases the flexibility of your store. For example, you only show during checkout the payment providers associated with the cart's region. - -### Retrieve with Query - -To retrieve the regions of a payment provider with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `regions.*` in `fields`: - -### query.graph - -```ts -const { data: paymentProviders } = await query.graph({ - entity: "payment_provider", - fields: [ - "regions.*", - ], -}) - -// paymentProviders[0].regions -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: paymentProviders } = useQueryGraphStep({ - entity: "payment_provider", - fields: [ - "regions.*", - ], -}) - -// paymentProviders[0].regions -``` - -### Manage with Link - -To manage the payment providers in a region, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.REGION]: { - region_id: "reg_123", - }, - [Modules.PAYMENT]: { - payment_provider_id: "pp_stripe_stripe", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.REGION]: { - region_id: "reg_123", - }, - [Modules.PAYMENT]: { - payment_provider_id: "pp_stripe_stripe", - }, -}) -``` - - -# Payment Collection - -In this document, you’ll learn what a payment collection is and how the Medusa application uses it with the Cart Module. - -## What's a Payment Collection? - -A payment collection stores payment details related to a resource, such as a cart or an order. It’s represented by the [PaymentCollection data model](https://docs.medusajs.com/references/payment/models/PaymentCollection/index.html.md). - -Every purchase or request for payment starts with a payment collection. The collection holds details necessary to complete the payment, including: - -- The [payment sessions](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-session/index.html.md) that represents the payment amount to authorize. -- The [payments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment/index.html.md) that are created when a payment session is authorized. They can be captured and refunded. -- The [payment providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/index.html.md) that handle the processing of each payment session, including the authorization, capture, and refund. - -*** - -## Multiple Payments - -The payment collection supports multiple payment sessions and payments. - -You can use this to accept payments in increments or split payments across payment providers. - -![Diagram showcasing how a payment collection can have multiple payment sessions and payments](https://res.cloudinary.com/dza7lstvk/image/upload/v1711554695/Medusa%20Resources/payment-collection-multiple-payments_oi3z3n.jpg) - -*** - -## Usage with the Cart Module - -The Cart Module provides cart management features. However, it doesn’t provide any features related to accepting payment. - -During checkout, the Medusa application links a cart to a payment collection, which will be used for further payment processing. - -It also implements the payment flow during checkout as explained in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-flow/index.html.md). - -![Diagram showcasing the relation between the Payment and Cart modules](https://res.cloudinary.com/dza7lstvk/image/upload/v1711537849/Medusa%20Resources/cart-payment_ixziqm.jpg) - - -# Payment - -In this document, you’ll learn what a payment is and how it's created, captured, and refunded. - -## What's a Payment? - -When a payment session is authorized, a payment, represented by the [Payment data model](https://docs.medusajs.com/references/payment/models/Payment/index.html.md), is created. This payment can later be captured or refunded. - -A payment carries many of the data and relations of a payment session: - -- It belongs to the same payment collection. -- It’s associated with the same payment provider, which handles further payment processing. -- It stores the payment session’s `data` property in its `data` property, as it’s still useful for the payment provider’s processing. - -*** - -## Capture Payments - -When a payment is captured, a capture, represented by the [Capture data model](https://docs.medusajs.com/references/payment/models/Capture/index.html.md), is created. It holds details related to the capture, such as the amount, the capture date, and more. - -The payment can also be captured incrementally, each time a capture record is created for that amount. - -![A diagram showcasing how a payment's multiple captures are stored](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565445/Medusa%20Resources/payment-capture_f5fve1.jpg) - -*** - -## Refund Payments - -When a payment is refunded, a refund, represented by the [Refund data model](https://docs.medusajs.com/references/payment/models/Refund/index.html.md), is created. It holds details related to the refund, such as the amount, refund date, and more. - -A payment can be refunded multiple times, and each time a refund record is created. - -![A diagram showcasing how a payment's multiple refunds are stored](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565555/Medusa%20Resources/payment-refund_lgfvyy.jpg) - - -# Accept Payment Flow - -In this document, you’ll learn how to implement an accept-payment flow using workflows or the Payment Module's main service. - -It's highly recommended to use Medusa's workflows to implement this flow. Use the Payment Module's main service for more complex cases. - -For a guide on how to implement this flow in the storefront, check out [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/checkout/payment/index.html.md). - -## Flow Overview - -![A diagram showcasing the payment flow's steps](https://res.cloudinary.com/dza7lstvk/image/upload/v1711566781/Medusa%20Resources/payment-flow_jblrvw.jpg) - -*** - -## 1. Create a Payment Collection - -A payment collection holds all details related to a resource’s payment operations. So, you start off by creating a payment collection. - -For example: - -### Using Workflow - -```ts -import { createPaymentCollectionForCartWorkflow } from "@medusajs/medusa/core-flows" - -// ... - -await createPaymentCollectionForCartWorkflow(req.scope) - .run({ - input: { - cart_id: "cart_123", - }, - }) -``` - -### Using Service - -```ts -const paymentCollection = - await paymentModuleService.createPaymentCollections({ - currency_code: "usd", - amount: 5000, - }) -``` - -*** - -## 2. Create Payment Sessions - -The payment collection has one or more payment sessions, each being a payment amount to be authorized by a payment provider. - -So, after creating the payment collection, create at least one payment session for a provider. - -For example: - -### Using Workflow - -```ts -import { createPaymentSessionsWorkflow } from "@medusajs/medusa/core-flows" - -// ... - -const { result: paymentSesion } = await createPaymentSessionsWorkflow(req.scope) - .run({ - input: { - payment_collection_id: "paycol_123", - provider_id: "stripe", - }, - }) -``` - -### Using Service - -```ts -const paymentSession = - await paymentModuleService.createPaymentSession( - paymentCollection.id, - { - provider_id: "stripe", - currency_code: "usd", - amount: 5000, - data: { - // any necessary data for the - // payment provider - }, - } - ) -``` - -*** - -## 3. Authorize Payment Session - -Once the customer chooses a payment session, start the authorization process. This may involve some action performed by the third-party payment provider, such as entering a 3DS code. - -For example: - -### Using Step - -```ts -import { authorizePaymentSessionStep } from "@medusajs/medusa/core-flows" - -// ... - -authorizePaymentSessionStep({ - id: "payses_123", - context: {}, -}) -``` - -### Using Service - -```ts -const payment = authorizePaymentSessionStep({ - id: "payses_123", - context: {}, -}) -``` - -When the payment authorization is successful, a payment is created and returned. - -### Handling Additional Action - -If you used the `authorizePaymentSessionStep`, you don't need to implement this logic as it's implemented in the step. - -If the payment authorization isn’t successful, whether because it requires additional action or for another reason, the method updates the payment session with the new status and throws an error. - -In that case, you can catch that error and, if the session's `status` property is `requires_more`, handle the additional action, then retry the authorization. - -For example: - -```ts -try { - const payment = - await paymentModuleService.authorizePaymentSession( - paymentSession.id, - {} - ) -} catch (e) { - // retrieve the payment session again - const updatedPaymentSession = ( - await paymentModuleService.listPaymentSessions({ - id: [paymentSession.id], - }) - )[0] - - if (updatedPaymentSession.status === "requires_more") { - // TODO perform required action - // TODO authorize payment again. - } -} -``` - -*** - -## 4. Payment Flow Complete - -The payment flow is complete once the payment session is authorized and the payment is created. - -You can then: - -- Capture the payment either using the [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/index.html.md) or [capturePayment method](https://docs.medusajs.com/references/payment/capturePayment/index.html.md). -- Refund captured amounts using the [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md) or [refundPayment method](https://docs.medusajs.com/references/payment/refundPayment/index.html.md). - -Some payment providers allow capturing the payment automatically once it’s authorized. In that case, you don’t need to do it manually. - - -# Payment Module Options - -In this document, you'll learn about the options of the Payment Module. - -## All Module Options - -|Option|Description|Required|Default| -|---|---|---|---|---|---|---| -|\`webhook\_delay\`|A number indicating the delay in milliseconds before processing a webhook event.|No|\`5000\`| -|\`webhook\_retries\`|The number of times to retry the webhook event processing in case of an error.|No|\`3\`| -|\`providers\`|An array of payment providers to install and register. Learn more |No|-| - -*** - -## providers Option - -The `providers` option is an array of payment module providers. - -When the Medusa application starts, these providers are registered and can be used to process payments. - -For example: - -```ts title="medusa-config.ts" -import { Modules } from "@medusajs/framework/utils" - -// ... - -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "@medusajs/medusa/payment", - options: { - providers: [ - { - resolve: "@medusajs/medusa/payment-stripe", - id: "stripe", - options: { - // ... - }, - }, - ], - }, - }, - ], -}) -``` - -The `providers` option is an array of objects that accept the following properties: - -- `resolve`: A string indicating the package name of the module provider or the path to it relative to the `src` directory. -- `id`: A string indicating the provider's unique name or ID. -- `options`: An optional object of the module provider's options. - - -# Payment Module Provider - -In this document, you’ll learn what a payment module provider is. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage the payment providers available in a region using the dashboard. - -## What's a Payment Module Provider? - -A payment module provider registers a payment provider that handles payment processing in the Medusa application. It integrates third-party payment providers, such as Stripe. - -To authorize a payment amount with a payment provider, a payment session is created and associated with that payment provider. The payment provider is then used to handle the authorization. - -After the payment session is authorized, the payment provider is associated with the resulting payment and handles its payment processing, such as to capture or refund payment. - -### List of Payment Module Providers - -- [Stripe](https://docs.medusajs.com/commerce-modules/payment/payment-provider/stripe/index.html.md) - -*** - -## System Payment Provider - -The Payment Module provides a `system` payment provider that acts as a placeholder payment provider. - -It doesn’t handle payment processing and delegates that to the merchant. It acts similarly to a cash-on-delivery (COD) payment method. - -*** - -## How are Payment Providers Created? - -A payment provider is a module whose main service extends the `AbstractPaymentProvider` imported from `@medusajs/framework/utils`. - -Refer to [this guide](https://docs.medusajs.com/references/payment/provider/index.html.md) on how to create a payment provider for the Payment Module. - -*** - -## Configure Payment Providers - -The Payment Module accepts a `providers` option that allows you to register providers in your application. - -Learn more about this option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/module-options#providers/index.html.md). - -*** - -## PaymentProvider Data Model - -When the Medusa application starts and registers the payment providers, it also creates a record of the `PaymentProvider` data model if none exists. - -This data model is used to reference a payment provider and determine whether it’s installed in the application. - - -# Payment Session - -In this document, you’ll learn what a payment session is. - -## What's a Payment Session? - -A payment session, represented by the [PaymentSession data model](https://docs.medusajs.com/references/payment/models/PaymentSession/index.html.md), is a payment amount to be authorized. It’s associated with a payment provider that handles authorizing it. - -A payment collection can have multiple payment sessions. Using this feature, you can implement payment in installments or payments using multiple providers. - -![Diagram showcasing how every payment session has a different payment provider](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565056/Medusa%20Resources/payment-session-provider_guxzqt.jpg) - -*** - -## data Property - -Payment providers may need additional data to process the payment later. The `PaymentSession` data model has a `data` property used to store that data. - -For example, the customer's ID in Stripe is stored in the `data` property. - -*** - -## Payment Session Status - -The `status` property of a payment session indicates its current status. Its value can be: - -- `pending`: The payment session is awaiting authorization. -- `requires_more`: The payment session requires an action before it’s authorized. For example, to enter a 3DS code. -- `authorized`: The payment session is authorized. -- `error`: An error occurred while authorizing the payment. -- `canceled`: The authorization of the payment session has been canceled. - - -# Webhook Events - -In this document, you’ll learn how the Payment Module supports listening to webhook events. - -## What's a Webhook Event? - -A webhook event is sent from a third-party payment provider to your application. It indicates a change in a payment’s status. - -This is useful in many cases such as when a payment is being processed asynchronously or when a request is interrupted and the payment provider is sending details on the process later. - -*** - -## getWebhookActionAndData Method - -The Payment Module’s main service has a [getWebhookActionAndData method](https://docs.medusajs.com/references/payment/getWebhookActionAndData/index.html.md) used to handle incoming webhook events from third-party payment services. The method delegates the handling to the associated payment provider, which returns the event's details. - -Medusa implements a webhook listener route at the `/hooks/payment/[identifier]_[provider]` API route, where: - -- `[identifier]` is the `identifier` static property defined in the payment provider. For example, `stripe`. -- `[provider]` is the ID of the provider. For example, `stripe`. - -For example, when integrating basic Stripe payments with the [Stripe Module Provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/stripe/index.html.md), the webhook listener route is `/hooks/payment/stripe_stripe`. If you're integrating Stripe's Bancontact payments, the webhook listener route is `/hooks/payment/stripe-bancontact_stripe`. - -Use that webhook listener in your third-party payment provider's configurations. - -![A diagram showcasing the steps of how the getWebhookActionAndData method words](https://res.cloudinary.com/dza7lstvk/image/upload/v1711567415/Medusa%20Resources/payment-webhook_seaocg.jpg) - -If the event's details indicate that the payment should be authorized, then the [authorizePaymentSession method of the main service](https://docs.medusajs.com/references/payment/authorizePaymentSession/index.html.md) is executed on the specified payment session. - -If the event's details indicate that the payment should be captured, then the [capturePayment method of the main service](https://docs.medusajs.com/references/payment/capturePayment/index.html.md) is executed on the payment of the specified payment session. - -### Actions After Webhook Payment Processing - -After the payment webhook actions are processed and the payment is authorized or captured, the Medusa application completes the cart associated with the payment's collection if it's not completed yet. - - -# Pricing Concepts - -In this document, you’ll learn about the main concepts in the Pricing Module. - -## Price Set - -A [PriceSet](https://docs.medusajs.com/references/pricing/models/PriceSet/index.html.md) represents a collection of prices that are linked to a resource (for example, a product or a shipping option). - -Each of these prices are represented by the [Price data module](https://docs.medusajs.com/references/pricing/models/Price/index.html.md). - -![A diagram showcasing the relation between the price set and price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648650/Medusa%20Resources/price-set-money-amount_xeees0.jpg) - -*** - -## Price List - -A [PriceList](https://docs.medusajs.com/references/pricing/models/PriceList/index.html.md) is a group of prices only enabled if their conditions and rules are satisfied. - -A price list has optional `start_date` and `end_date` properties that indicate the date range in which a price list can be applied. - -Its associated prices are represented by the `Price` data model. - - -# Links between Pricing Module and Other Modules - -This document showcases the module links defined between the Pricing Module and other Commerce Modules. - -## Summary - -The Pricing Module has the following links to other modules: - -|First Data Model|Second Data Model|Type|Description| -|---|---|---|---| -| in ||Stored - one-to-one|| -| in ||Stored - one-to-one|| - -*** - -## Fulfillment Module - -The Fulfillment Module provides fulfillment-related functionalities, including shipping options that the customer chooses from when they place their order. However, it doesn't provide pricing-related functionalities for these options. - -Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set. - -![A diagram showcasing an example of how data models from the Pricing and Fulfillment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716561747/Medusa%20Resources/pricing-fulfillment_spywwa.jpg) - -### Retrieve with Query - -To retrieve the shipping option of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `shipping_option.*` in `fields`: - -### query.graph - -```ts -const { data: priceSets } = await query.graph({ - entity: "price_set", - fields: [ - "shipping_option.*", - ], -}) - -// priceSets[0].shipping_option -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: priceSets } = useQueryGraphStep({ - entity: "price_set", - fields: [ - "shipping_option.*", - ], -}) - -// priceSets[0].shipping_option -``` - -### Manage with Link - -To manage the price set of a shipping option, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.FULFILLMENT]: { - shipping_option_id: "so_123", - }, - [Modules.PRICING]: { - price_set_id: "pset_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.FULFILLMENT]: { - shipping_option_id: "so_123", - }, - [Modules.PRICING]: { - price_set_id: "pset_123", - }, -}) -``` - -*** - -## Product Module - -The Product Module doesn't store or manage the prices of product variants. - -Medusa defines a link between the `ProductVariant` and the `PriceSet`. A product variant’s prices are stored as prices belonging to a price set. - -![A diagram showcasing an example of how data models from the Pricing and Product Module are linked. The PriceSet is linked to the ProductVariant of the Product Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651039/Medusa%20Resources/pricing-product_m4xaut.jpg) - -So, when you want to add prices for a product variant, you create a price set and add the prices to it. - -You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context. - -### Retrieve with Query - -To retrieve the variant of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`: - -### query.graph - -```ts -const { data: priceSets } = await query.graph({ - entity: "price_set", - fields: [ - "variant.*", - ], -}) - -// priceSets[0].variant -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: priceSets } = useQueryGraphStep({ - entity: "price_set", - fields: [ - "variant.*", - ], -}) - -// priceSets[0].variant -``` - -### Manage with Link - -To manage the price set of a variant, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.PRODUCT]: { - variant_id: "variant_123", - }, - [Modules.PRICING]: { - price_set_id: "pset_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.PRODUCT]: { - variant_id: "variant_123", - }, - [Modules.PRICING]: { - price_set_id: "pset_123", - }, -}) -``` - - -# Price Rules - -In this Pricing Module guide, you'll learn about price rules for price sets and price lists, and how to add rules to a price. - -## Price Rule - -You can restrict prices by rules. Each rule of a price is represented by the [PriceRule data model](https://docs.medusajs.com/references/pricing/models/PriceRule/index.html.md). - -The `Price` data model has a `rules_count` property, which indicates how many rules, represented by `PriceRule`, are applied to the price. - -For exmaple, you create a price restricted to `10557` zip codes. - -![A diagram showcasing the relation between the PriceRule and Price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg) - -A price can have multiple price rules. - -For example, a price can be restricted by a region and a zip code. - -![A diagram showcasing the relation between the PriceRule and Price with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg) - -*** - -## Price List Rules - -Rules applied to a price list are represented by the [PriceListRule data model](https://docs.medusajs.com/references/pricing/models/PriceListRule/index.html.md). - -The `rules_count` property of a `PriceList` indicates how many rules are applied to it. - -![A diagram showcasing the relation between the PriceSet, PriceList, Price, RuleType, and PriceListRuleValue](https://res.cloudinary.com/dza7lstvk/image/upload/v1709641999/Medusa%20Resources/price-list_zd10yd.jpg) - -*** - -## How to Set Rules on a Price? - -### Using Workflows - -Medusa uses the Pricing Module to store prices of different resources, such as product variants and shipping options. - -When you manage one of these resources using [Medusa's workflows](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/medusa-workflows-reference/index.html.md) or using the API routes that use them, you can set rules on a price using the `rules` property of the price object. - -For example, when creating a shipping option using the [createShippingOptionsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createShippingOptionsWorkflow/index.html.md) to create a shipping option, you can make the shipping price free based on the cart total: - -```ts highlights={workflowHighlights} -const { result } = await createShippingOptionsWorkflow(container) - .run({ - input: [{ - name: "Standard Shipping", - service_zone_id: "serzo_123", - shipping_profile_id: "sp_123", - provider_id: "prov_123", - type: { - label: "Standard", - description: "Standard shipping", - code: "standard", - }, - price_type: "flat", - prices: [ - // default price - { - currency_code: "usd", - amount: 10, - rules: {}, - }, - // price if cart total >= $100 - { - currency_code: "usd", - amount: 0, - rules: { - item_total: { - operator: "gte", - value: 100, - }, - }, - }, - ], - }], - }) -``` - -In this example, you create a shipping option whose default price is `$10`. When the total of the cart or order using this shipping option is greater than `$100`, the shipping option's price becomes free. - -### Using Pricing Module's Service - -For most use cases, it's recommended to use [workflows](#using-workflows) instead of directly using the module's service. - -When adding a price using the [addPrices](https://docs.medusajs.com/resources/references/pricing/addPrices/index.html.md) method of the Pricing Module's service, pass the `rules` property to a price object. - -For example: - -```ts -const priceSet = await pricingModule.addPrices({ - priceSetId: "pset_1", - prices: [ - // default price - { - currency_code: "usd", - amount: 10, - rules: {}, - }, - // price if cart total >= $100 - { - currency_code: "usd", - amount: 0, - rules: { - item_total: { - operator: "gte", - value: 100, - }, - }, - }, - ], -}) -``` - -In this example, you set the default price of a resource (for example, a shipping option), to `$10`. You also add a conditioned price that sets the price to `0` when the cart or order's total is greater than or equal to `$100`. - -### How is the Price Rule Applied? - -The [price calculation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md) mechanism considers a price applicable when the resource that this price is in matches the specified rules. - -For example, a [cart object](https://docs.medusajs.com/api/store#carts_cart_schema) has an `item_total` property. So, if a shipping option has the following price: - -```json -{ - "currency_code": "usd", - "amount": 0, - "rules": { - "item_total": { - "operator": "gte", - "value": 100, - } - } -} -``` - -The shipping option's price is applied when the cart's `item_total` is greater than or equal to `$100`. - -You can also apply the rule on nested relations and properties. For example, to apply a shipping option's price based on the customer's group, you can apply a rule on the `customer.group.id` attribute: - -```json -{ - "currency_code": "usd", - "amount": 0, - "rules": { - "customer.group.id": { - "operator": "eq", - "value": "cusgrp_123" - } - } -} -``` - -In this example, the price is only applied if a cart's customer belongs to the customer group of ID `cusgrp_123`. - -These same rules apply to product variant prices as well, or any other resource that has a price. - - -# Prices Calculation - -In this document, you'll learn how prices are calculated when you use the [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) of the Pricing Module's main service. - -## calculatePrices Method - -The [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) accepts as parameters the ID of one or more price sets and a context. - -It returns a price object with the best matching price for each price set. - -### Calculation Context - -The calculation context is an optional object passed as a second parameter to the `calculatePrices` method. It accepts rules to restrict the selected prices in the price set. - -For example: - -```ts -const price = await pricingModuleService.calculatePrices( - { id: [priceSetId] }, - { - context: { - currency_code: currencyCode, - region_id: "reg_123", - }, - } -) -``` - -In this example, you retrieve the prices in a price set for the specified currency code and region ID. - -### Returned Price Object - -For each price set, the `calculatePrices` method selects two prices: - -- A calculated price: Either a price that belongs to a price list and best matches the specified context, or the same as the original price. -- An original price, which is either: - - The same price as the calculated price if the price list it belongs to is of type `override`; - - Or a price that doesn't belong to a price list and best matches the specified context. - -Both prices are returned in an object that has the following properties: - -- id: (\`string\`) The ID of the price set from which the price was selected. -- is\_calculated\_price\_price\_list: (\`boolean\`) Whether the calculated price belongs to a price list. -- calculated\_amount: (\`number\`) The amount of the calculated price, or \`null\` if there isn't a calculated price. This is the amount shown to the customer. -- is\_original\_price\_price\_list: (\`boolean\`) Whether the original price belongs to a price list. -- original\_amount: (\`number\`) The amount of the original price, or \`null\` if there isn't an original price. This amount is useful to compare with the \`calculated\_amount\`, such as to check for discounted value. -- currency\_code: (\`string\`) The currency code of the calculated price, or \`null\` if there isn't a calculated price. -- is\_calculated\_price\_tax\_inclusive: (\`boolean\`) Whether the calculated price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx) -- is\_original\_price\_tax\_inclusive: (\`boolean\`) Whether the original price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx) -- calculated\_price: (\`object\`) The calculated price's price details. - - - id: (\`string\`) The ID of the price. - - - price\_list\_id: (\`string\`) The ID of the associated price list. - - - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`. - - - min\_quantity: (\`number\`) The price's min quantity condition. - - - max\_quantity: (\`number\`) The price's max quantity condition. -- original\_price: (\`object\`) The original price's price details. - - - id: (\`string\`) The ID of the price. - - - price\_list\_id: (\`string\`) The ID of the associated price list. - - - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`. - - - min\_quantity: (\`number\`) The price's min quantity condition. - - - max\_quantity: (\`number\`) The price's max quantity condition. - -*** - -## Examples - -Consider the following price set: - -```ts -const priceSet = await pricingModuleService.createPriceSets({ - prices: [ - // default price - { - amount: 500, - currency_code: "EUR", - rules: {}, - }, - // prices with rules - { - amount: 400, - currency_code: "EUR", - rules: { - region_id: "reg_123", - }, - }, - { - amount: 450, - currency_code: "EUR", - rules: { - city: "krakow", - }, - }, - { - amount: 500, - currency_code: "EUR", - rules: { - city: "warsaw", - region_id: "reg_123", - }, - }, - ], -}) -``` - -### Default Price Selection - -### Code - -```ts -const price = await pricingModuleService.calculatePrices( - { id: [priceSet.id] }, - { - context: { - currency_code: "EUR" - } - } -) -``` - -### Result - -### Calculate Prices with Rules - -### Code - -```ts -const price = await pricingModuleService.calculatePrices( - { id: [priceSet.id] }, - { - context: { - currency_code: "EUR", - region_id: "reg_123", - city: "krakow" - } - } -) -``` - -### Result - -### Price Selection with Price List - -### Code - -```ts -const priceList = pricingModuleService.createPriceLists([{ - title: "Summer Price List", - description: "Price list for summer sale", - starts_at: Date.parse("01/10/2023").toString(), - ends_at: Date.parse("31/10/2023").toString(), - rules: { - region_id: ['PL'] - }, - type: "sale", - prices: [ - { - amount: 400, - currency_code: "EUR", - price_set_id: priceSet.id, - }, - { - amount: 450, - currency_code: "EUR", - price_set_id: priceSet.id, - }, - ], -}]); - -const price = await pricingModuleService.calculatePrices( - { id: [priceSet.id] }, - { - context: { - currency_code: "EUR", - region_id: "PL", - city: "krakow" - } - } -) -``` - -### Result - - -# Tax-Inclusive Pricing - -In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices. - -## What is Tax-Inclusive Pricing? - -A tax-inclusive price is a price of a resource that includes taxes. Medusa calculates the tax amount from the price rather than adds the amount to it. - -For example, if a product’s price is $50, the tax rate is 2%, and tax-inclusive pricing is enabled, then the product's price is $49, and the applied tax amount is $1. - -*** - -## How is Tax-Inclusive Pricing Set? - -The [PricePreference data model](https://docs.medusajs.com/references/pricing/models/PricePreference/index.html.md) holds the tax-inclusive setting for a context. It has two properties that indicate the context: - -- `attribute`: The name of the attribute to compare against. For example, `region_id` or `currency_code`. -- `value`: The attribute’s value. For example, `reg_123` or `usd`. - -Only `region_id` and `currency_code` are supported as an `attribute` at the moment. - -The `is_tax_inclusive` property indicates whether tax-inclusivity is enabled in the specified context. - -For example: - -```json -{ - "attribute": "currency_code", - "value": "USD", - "is_tax_inclusive": true, -} -``` - -In this example, tax-inclusivity is enabled for the `USD` currency code. - -*** - -## Tax-Inclusive Pricing in Price Calculation - -### Tax Context - -As mentioned in the [Price Calculation documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md), The `calculatePrices` method accepts as a parameter a calculation context. - -To get accurate tax results, pass the `region_id` and / or `currency_code` in the calculation context. - -### Returned Tax Properties - -The `calculatePrices` method returns two properties related to tax-inclusivity: - -Learn more about the returned properties in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md). - -- `is_calculated_price_tax_inclusive`: Whether the selected `calculated_price` is tax-inclusive. -- `is_original_price_tax_inclusive` : Whether the selected `original_price` is tax-inclusive. - -A price is considered tax-inclusive if: - -1. It belongs to the region or currency code specified in the calculation context; -2. and the region or currency code has a price preference with `is_tax_inclusive` enabled. - -### Tax Context Precedence - -A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if: - -- both the `region_id` and `currency_code` are provided in the calculation context; -- the selected price belongs to the region; -- and the region has a price preference - - # Inventory Concepts In this document, you’ll learn about the main concepts in the Inventory Module, and how data is stored and related. @@ -27226,407 +24669,264 @@ const { data: inventoryLevels } = useQueryGraphStep({ ``` -# Promotion Actions +# Order Claim -In this document, you’ll learn about promotion actions and how they’re computed using the [computeActions method](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). +In this document, you’ll learn about order claims. -## computeActions Method +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/claims/index.html.md) to learn how to manage an order's claims using the dashboard. -The Promotion Module's main service has a [computeActions method](https://docs.medusajs.com/references/promotion/computeActions/index.html.md) that returns an array of actions to perform on a cart when one or more promotions are applied. +## What is a Claim? -Actions inform you what adjustment must be made to a cart item or shipping method. Each action is an object having the `action` property indicating the type of action. +When a customer receives a defective or incorrect item, the merchant can create a claim to refund or replace the item. + +The [OrderClaim data model](https://docs.medusajs.com/references/order/models/OrderClaim/index.html.md) represents a claim. *** -## Action Types +## Claim Type -### `addItemAdjustment` Action +The `Claim` data model has a `type` property whose value indicates the type of the claim: -The `addItemAdjustment` action indicates that an adjustment must be made to an item. For example, removing $5 off its amount. - -This action has the following format: - -```ts -export interface AddItemAdjustmentAction { - action: "addItemAdjustment" - item_id: string - amount: number - code: string - description?: string -} -``` - -This action means that a new record should be created of the `LineItemAdjustment` data model in the Cart Module, or `OrderLineItemAdjustment` data model in the Order Module. - -Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.AddItemAdjustmentAction/index.html.md) for details on the object’s properties. - -### `removeItemAdjustment` Action - -The `removeItemAdjustment` action indicates that an adjustment must be removed from a line item. For example, remove the $5 discount. - -The `computeActions` method accepts any previous item adjustments in the `items` property of the second parameter. - -This action has the following format: - -```ts -export interface RemoveItemAdjustmentAction { - action: "removeItemAdjustment" - adjustment_id: string - description?: string - code: string -} -``` - -This action means that a new record should be removed of the `LineItemAdjustment` (or `OrderLineItemAdjustment`) with the specified ID in the `adjustment_id` property. - -Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.RemoveItemAdjustmentAction/index.html.md) for details on the object’s properties. - -### `addShippingMethodAdjustment` Action - -The `addShippingMethodAdjustment` action indicates that an adjustment must be made on a shipping method. For example, make the shipping method free. - -This action has the following format: - -```ts -export interface AddShippingMethodAdjustment { - action: "addShippingMethodAdjustment" - shipping_method_id: string - amount: number - code: string - description?: string -} -``` - -This action means that a new record should be created of the `ShippingMethodAdjustment` data model in the Cart Module, or `OrderShippingMethodAdjustment` data model in the Order Module. - -Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.AddShippingMethodAdjustment/index.html.md) for details on the object’s properties. - -### `removeShippingMethodAdjustment` Action - -The `removeShippingMethodAdjustment` action indicates that an adjustment must be removed from a shipping method. For example, remove the free shipping discount. - -The `computeActions` method accepts any previous shipping method adjustments in the `shipping_methods` property of the second parameter. - -This action has the following format: - -```ts -export interface RemoveShippingMethodAdjustment { - action: "removeShippingMethodAdjustment" - adjustment_id: string - code: string -} -``` - -When the Medusa application receives this action type, it removes the `ShippingMethodAdjustment` (or `OrderShippingMethodAdjustment`) with the specified ID in the `adjustment_id` property. - -Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.RemoveShippingMethodAdjustment/index.html.md) for details on the object’s properties. - -### `campaignBudgetExceeded` Action - -When the `campaignBudgetExceeded` action is returned, the promotions within a campaign can no longer be used as the campaign budget has been exceeded. - -This action has the following format: - -```ts -export interface CampaignBudgetExceededAction { - action: "campaignBudgetExceeded" - code: string -} -``` - -Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.CampaignBudgetExceededAction/index.html.md) for details on the object’s properties. - - -# Application Method - -In this document, you'll learn what an application method is. - -## What is an Application Method? - -The [ApplicationMethod data model](https://docs.medusajs.com/references/promotion/models/ApplicationMethod/index.html.md) defines how a promotion is applied: - -|Property|Purpose| -|---|---| -|\`type\`|Does the promotion discount a fixed amount or a percentage?| -|\`target\_type\`|Is the promotion applied on a cart item, shipping method, or the entire order?| -|\`allocation\`|Is the discounted amount applied on each item or split between the applicable items?| - -## Target Promotion Rules - -When the promotion is applied to a cart item or a shipping method, you can restrict which items/shipping methods the promotion is applied to. - -The `ApplicationMethod` data model has a collection of `PromotionRule` records to restrict which items or shipping methods the promotion applies to. The `target_rules` property represents this relation. - -![A diagram showcasing the target\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898273/Medusa%20Resources/application-method-target-rules_hqaymz.jpg) - -In this example, the promotion is only applied on products in the cart having the SKU `SHIRT`. +- `refund`: the items are returned, and the customer is refunded. +- `replace`: the items are returned, and the customer receives new items. *** -## Buy Promotion Rules +## Old and Replacement Items -When the promotion’s type is `buyget`, you must specify the “buy X” condition. For example, a cart must have two shirts before the promotion can be applied. +When the claim is created, a return, represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md), is also created to handle receiving the old items from the customer. -The application method has a collection of `PromotionRule` items to define the “buy X” rule. The `buy_rules` property represents this relation. +Learn more about returns in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). -![A diagram showcasing the buy\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898453/Medusa%20Resources/application-method-buy-rules_djjuhw.jpg) - -In this example, the cart must have two products with the SKU `SHIRT` for the promotion to be applied. - - -# Campaign - -In this document, you'll learn about campaigns. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/campaigns/index.html.md) to learn how to manage campaigns using the dashboard. - -## What is a Campaign? - -A [Campaign](https://docs.medusajs.com/references/promotion/models/Campaign/index.html.md) combines promotions under the same conditions, such as start and end dates. - -![A diagram showcasing the relation between the Campaign and Promotion data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899225/Medusa%20Resources/campagin-promotion_hh3qsi.jpg) +If the claim’s type is `replace`, replacement items are represented by the [ClaimItem data model](https://docs.medusajs.com/references/order/models/OrderClaimItem/index.html.md). *** -## Campaign Limits +## Claim Shipping Methods -Each campaign has a budget represented by the [CampaignBudget data model](https://docs.medusajs.com/references/promotion/models/CampaignBudget/index.html.md). The budget limits how many times the promotion can be used. +A claim uses shipping methods to send the replacement items to the customer. These methods are represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md). -There are two types of budgets: - -- `spend`: An amount that, when crossed, the promotion becomes unusable. For example, if the amount limit is set to `$100`, and the total amount of usage of this promotion crosses that threshold, the promotion can no longer be applied. -- `usage`: The number of times that a promotion can be used. For example, if the usage limit is set to `10`, the promotion can be used only 10 times by customers. After that, it can no longer be applied. - -![A diagram showcasing the relation between the Campaign and CampaignBudget data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899463/Medusa%20Resources/campagin-budget_rvqlmi.jpg) - - -# Promotion Concepts - -In this guide, you’ll learn about the main promotion and rule concepts in the Promotion Module. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/index.html.md) to learn how to manage promotions using the dashboard. - -## What is a Promotion? - -A promotion, represented by the [Promotion data model](https://docs.medusajs.com/references/promotion/models/Promotion/index.html.md), is a discount that can be applied on cart items, shipping methods, or entire orders. - -A promotion has two types: - -- `standard`: A standard promotion with rules. -- `buyget`: “A buy X get Y” promotion with rules. - -|\`standard\`|\`buyget\`| -|---|---| -|A coupon code that gives customers 10% off their entire order.|Buy two shirts and get another for free.| -|A coupon code that gives customers $15 off any shirt in their order.|Buy two shirts and get 10% off the entire order.| -|A discount applied automatically for VIP customers that removes 10% off their shipping method’s amount.|Spend $100 and get free shipping.| - -The Medusa Admin UI may not provide a way to create each of these promotion examples. However, they are supported by the Promotion Module and Medusa's workflows and API routes. +The shipping methods for the returned items are associated with the claim's return, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods/index.html.md). *** -## Promotion Rules +## Claim Refund -A promotion can be restricted by a set of rules, each rule is represented by the [PromotionRule data model](https://docs.medusajs.com/references/promotion/models/PromotionRule/index.html.md). +If the claim’s type is `refund`, the amount to be refunded is stored in the `refund_amount` property. -For example, you can create a promotion that only customers of the `VIP` customer group can use. - -![A diagram showcasing the relation between Promotion and PromotionRule](https://res.cloudinary.com/dza7lstvk/image/upload/v1709833196/Medusa%20Resources/promotion-promotion-rule_msbx0w.jpg) - -A `PromotionRule`'s `attribute` property indicates the property's name to which this rule is applied. For example, `customer_group_id`. - -The expected value for the attribute is stored in the `PromotionRuleValue` data model. So, a rule can have multiple values. - -When testing whether a promotion can be applied to a cart, the rule's `attribute` property and its values are tested on the cart itself. - -For example, the cart's customer must be part of the customer group(s) indicated in the promotion rule's value. - -### Flexible Rules - -The `PromotionRule`'s `operator` property adds more flexibility to the rule’s condition rather than simple equality (`eq`). - -For example, to restrict the promotion to only `VIP` and `B2B` customer groups: - -- Add a `PromotionRule` record with its `attribute` property set to `customer_group_id` and `operator` property to `in`. -- Add two `PromotionRuleValue` records associated with the rule: one with the value `VIP` and the other `B2B`. - -![A diagram showcasing the relation between PromotionRule and PromotionRuleValue when a rule has multiple values](https://res.cloudinary.com/dza7lstvk/image/upload/v1709897383/Medusa%20Resources/promotion-promotion-rule-multiple_hctpmt.jpg) - -In this case, a customer’s group must be in the `VIP` and `B2B` set of values to use the promotion. +The [Transaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md) represents the refunds made for the claim. *** -## How to Apply Rules on a Promotion? +## How Claims Impact an Order’s Version -### Using Workflows - -If you're managing promotions using [Medusa's workflows](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/medusa-workflows-reference/index.html.md) or the API routes that use them, you can specify rules for the promotion or its [application method](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/application-method/index.html.md). - -For example, if you're creating a promotion using the [createPromotionsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createPromotionsWorkflow/index.html.md): - -```ts -const { result } = await createPromotionsWorkflow(container) - .run({ - input: { - promotionsData: [{ - code: "10OFF", - type: "standard", - status: "active", - application_method: { - type: "percentage", - target_type: "items", - allocation: "across", - value: 10, - currency_code: "usd", - }, - rules: [ - { - attribute: "customer.group.id", - operator: "eq", - values: [ - "cusgrp_123", - ], - }, - ], - }], - }, - }) -``` - -In this example, the promotion is restricted to customers with the `cusgrp_123` customer group. - -### Using Promotion Module's Service - -For most use cases, it's recommended to use [workflows](#using-workflows) instead of directly using the module's service. - -If you're managing promotions using the Promotion Module's service, you can specify rules for the promotion or its [application method](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/application-method/index.html.md) in its methods. - -For example, if you're creating a promotion with the [createPromotions](https://docs.medusajs.com/resources/references/promotion/createPromotions/index.html.md) method: - -```ts -const promotions = await promotionModuleService.createPromotions([ - { - code: "50OFF", - type: "standard", - status: "active", - application_method: { - type: "percentage", - target_type: "items", - value: 50, - }, - rules: [ - { - attribute: "customer.group.id", - operator: "eq", - values: [ - "cusgrp_123", - ], - }, - ], - }, -]) -``` - -In this example, the promotion is restricted to customers with the `cusgrp_123` customer group. - -### How is the Promotion Rule Applied? - -A promotion is applied on a resource if its attributes match the promotion's rules. - -For example, consider you have the following promotion with a rule that restricts the promotion to a specific customer: - -```json -{ - "code": "10OFF", - "type": "standard", - "status": "active", - "application_method": { - "type": "percentage", - "target_type": "items", - "allocation": "across", - "value": 10, - "currency_code": "usd" - }, - "rules": [ - { - "attribute": "customer_id", - "operator": "eq", - "values": [ - "cus_123" - ] - } - ] -} -``` - -When you try to apply this promotion on a cart, the cart's `customer_id` is compared to the promotion rule's value based on the specified operator. So, the promotion will only be applied if the cart's `customer_id` is equal to `cus_123`. +When a claim is confirmed, the order’s version is incremented. -# Stock Location Concepts +# Order Edit -In this document, you’ll learn about the main concepts in the Stock Location Module. +In this document, you'll learn about order edits. -## Stock Location +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/edit/index.html.md) to learn how to edit an order's items using the dashboard. -A stock location, represented by the `StockLocation` data model, represents a location where stock items are kept. For example, a warehouse. +## What is an Order Edit? -Medusa uses stock locations to provide inventory details, from the Inventory Module, per location. +A merchant can edit an order to add new items or change the quantity of existing items in the order. + +An order edit is represented by the [OrderChange data model](https://docs.medusajs.com/references/order/models/OrderChange/index.html.md). + +The `OrderChange` data model is associated with any type of change, including a return or exchange. However, its `change_type` property distinguishes the type of change it's making. + +In the case of an order edit, the `OrderChange`'s type is `edit`. *** -## StockLocationAddress +## Add Items in an Order Edit -The `StockLocationAddress` data model belongs to the `StockLocation` data model. It provides more detailed information of the location, such as country code or street address. +When the merchant adds new items to the order in the order edit, the item is added as an [OrderItem](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). + +Also, an `OrderChangeAction` is created. The [OrderChangeAction data model](https://docs.medusajs.com/references/order/models/OrderChangeAction/index.html.md) represents a change made by an `OrderChange`, such as an item added. + +So, when an item is added, an `OrderChangeAction` is created with the type `ITEM_ADD`. In its `details` property, the item's ID, price, and quantity are stored. + +*** + +## Update Items in an Order Edit + +A merchant can update an existing item's quantity or price. + +This change is added as an `OrderChangeAction` with the type `ITEM_UPDATE`. In its `details` property, the item's ID, new price, and new quantity are stored. + +*** + +## Shipping Methods of New Items in the Edit + +Adding new items to the order requires adding shipping methods for those items. + +These shipping methods are represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). Also, an `OrderChangeAction` is created with the type `SHIPPING_ADD` + +*** + +## How Order Edits Impact an Order’s Version + +When an order edit is confirmed, the order’s version is incremented. + +*** + +## Payments and Refunds for Order Edit Changes + +Once the Order Edit is confirmed, any additional payment or refund required can be made on the original order. + +This is determined by the comparison between the `OrderSummary` and the order's transactions, as mentioned in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/transactions#checking-outstanding-amount/index.html.md). -# Links between Stock Location Module and Other Modules +# Order Concepts -This document showcases the module links defined between the Stock Location Module and other Commerce Modules. +In this document, you’ll learn about orders and related concepts + +## Order Items + +The items purchased in the order are represented by the [OrderItem data model](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). An order can have multiple items. + +![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712304722/Medusa%20Resources/order-order-items_uvckxd.jpg) + +### Item’s Product Details + +The details of the purchased products are represented by the [LineItem data model](https://docs.medusajs.com/references/order/models/OrderLineItem/index.html.md). Not only does a line item hold the details of the product, but also details related to its price, adjustments due to promotions, and taxes. + +*** + +## Order’s Shipping Method + +An order has one or more shipping methods used to handle item shipment. + +Each shipping method is represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md) that holds its details. The shipping method is linked to the order through the [OrderShipping data model](https://docs.medusajs.com/references/order/models/OrderShipping/index.html.md). + +![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719570409/Medusa%20Resources/order-shipping-method_tkggvd.jpg) + +### data Property + +When fulfilling the order, you can use a third-party fulfillment provider that requires additional custom data to be passed along from the order creation process. + +The `OrderShippingMethod` data model has a `data` property. It’s an object used to store custom data relevant later for fulfillment. + +The Medusa application passes the `data` property to the Fulfillment Module when fulfilling items. + +*** + +## Order Totals + +The order’s total amounts (including tax total, total after an item is returned, etc…) are represented by the [OrderSummary data model](https://docs.medusajs.com/references/order/models/OrderSummary/index.html.md). + +*** + +## Order Payments + +Payments made on an order, whether they’re capture or refund payments, are recorded as transactions represented by the [OrderTransaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). + +An order can have multiple transactions. The sum of these transactions must be equal to the order summary’s total. Otherwise, there’s an outstanding amount. + +Learn more about transactions in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/transactions/index.html.md). + + +# Order Exchange + +In this document, you’ll learn about order exchanges. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/exchanges/index.html.md) to learn how to manage an order's exchanges using the dashboard. + +## What is an Exchange? + +An exchange is the replacement of an item that the customer ordered with another. + +A merchant creates the exchange, specifying the items to be replaced and the new items to be sent. + +The [OrderExchange data model](https://docs.medusajs.com/references/order/models/OrderExchange/index.html.md) represents an exchange. + +*** + +## Returned and New Items + +When the exchange is created, a return, represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md), is created to handle receiving the items back from the customer. + +Learn more about returns in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). + +The [OrderExchangeItem data model](https://docs.medusajs.com/references/order/models/OrderExchangeItem/index.html.md) represents the new items to be sent to the customer. + +*** + +## Exchange Shipping Methods + +An exchange has shipping methods used to send the new items to the customer. They’re represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md). + +The shipping methods for the returned items are associated with the exchange's return, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods/index.html.md). + +*** + +## Exchange Payment + +The `Exchange` data model has a `difference_due` property that stores the outstanding amount. + +|Condition|Result| +|---|---|---| +|\`difference\_due \< 0\`|Merchant owes the customer a refund of the | +|\`difference\_due > 0\`|Merchant requires additional payment from the customer of the | +|\`difference\_due = 0\`|No payment processing is required.| + +Any payment or refund made is stored in the [Transaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). + +*** + +## How Exchanges Impact an Order’s Version + +When an exchange is confirmed, the order’s version is incremented. + + +# Links between Order Module and Other Modules + +This document showcases the module links defined between the Order Module and other Commerce Modules. ## Summary -The Stock Location Module has the following links to other modules: +The Order Module has the following links to other modules: Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. |First Data Model|Second Data Model|Type|Description| |---|---|---|---| -| in ||Stored - many-to-one|| -| in ||Stored - many-to-many|| -| in ||Read-only - has many|| -| in ||Stored - many-to-many|| +|| in |Read-only - has one|| +|| in |Stored - one-to-one|| +|| in |Stored - one-to-many|| +|| in |Stored - one-to-many|| +|| in |Stored - one-to-many|| +|| in |Stored - one-to-many|| +|| in |Stored - one-to-many|| +|| in |Read-only - has many|| +|| in |Stored - many-to-many|| +|| in |Read-only - has one|| +|| in |Read-only - has one|| *** -## Fulfillment Module +## Customer Module -A fulfillment set can be conditioned to a specific stock location. - -Medusa defines a link between the `FulfillmentSet` and `StockLocation` data models. - -![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1712567101/Medusa%20Resources/fulfillment-stock-location_nlkf7e.jpg) - -Medusa also defines a link between the `FulfillmentProvider` and `StockLocation` data models to indicate the providers that can be used in a location. - -![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399492/Medusa%20Resources/fulfillment-provider-stock-location_b0mulo.jpg) +Medusa defines a read-only link between the `Order` data model and the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md)'s `Customer` data model. This means you can retrieve the details of an order's customer, but you don't manage the links in a pivot table in the database. The customer of an order is determined by the `customer_id` property of the `Order` data model. ### Retrieve with Query -To retrieve the fulfillment sets of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `fulfillment_sets.*` in `fields`: - -To retrieve the fulfillment providers, pass `fulfillment_providers.*` in `fields`. +To retrieve the customer of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: ### query.graph ```ts -const { data: stockLocations } = await query.graph({ - entity: "stock_location", +const { data: orders } = await query.graph({ + entity: "order", fields: [ - "fulfillment_sets.*", + "customer.*", ], }) -// stockLocations[0].fulfillment_sets +// orders[0].customer ``` ### useQueryGraphStep @@ -27636,222 +24936,41 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: stockLocations } = useQueryGraphStep({ - entity: "stock_location", +const { data: orders } = useQueryGraphStep({ + entity: "order", fields: [ - "fulfillment_sets.*", + "customer.*", ], }) -// stockLocations[0].fulfillment_sets +// orders[0].customer ``` -### Manage with Link - -To manage the stock location of a fulfillment set, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.STOCK_LOCATION]: { - stock_location_id: "sloc_123", - }, - [Modules.FULFILLMENT]: { - fulfillment_set_id: "fset_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.STOCK_LOCATION]: { - stock_location_id: "sloc_123", - }, - [Modules.FULFILLMENT]: { - fulfillment_set_id: "fset_123", - }, -}) -``` - -*** - -## Inventory Module - -Medusa defines a read-only link between the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md)'s `InventoryLevel` data model and the `StockLocation` data model. Because the link is read-only from the `InventoryLevel`'s side, you can only retrieve the stock location of an inventory level, and not the other way around. - -### Retrieve with Query - -To retrieve the stock locations of an inventory level with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `stock_locations.*` in `fields`: - -### query.graph - -```ts -const { data: inventoryLevels } = await query.graph({ - entity: "inventory_level", - fields: [ - "stock_locations.*", - ], -}) - -// inventoryLevels[0].stock_locations -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: inventoryLevels } = useQueryGraphStep({ - entity: "inventory_level", - fields: [ - "stock_locations.*", - ], -}) - -// inventoryLevels[0].stock_locations -``` - -*** - -## Sales Channel Module - -A stock location is associated with a sales channel. This scopes inventory quantities in a stock location by the associated sales channel. - -Medusa defines a link between the `SalesChannel` and `StockLocation` data models. - -![A diagram showcasing an example of how resources from the Sales Channel and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716796872/Medusa%20Resources/sales-channel-location_cqrih1.jpg) - -### Retrieve with Query - -To retrieve the sales channels of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channels.*` in `fields`: - -### query.graph - -```ts -const { data: stockLocations } = await query.graph({ - entity: "stock_location", - fields: [ - "sales_channels.*", - ], -}) - -// stockLocations[0].sales_channels -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: stockLocations } = useQueryGraphStep({ - entity: "stock_location", - fields: [ - "sales_channels.*", - ], -}) - -// stockLocations[0].sales_channels -``` - -### Manage with Link - -To manage the stock locations of a sales channel, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.SALES_CHANNEL]: { - sales_channel_id: "sc_123", - }, - [Modules.STOCK_LOCATION]: { - sales_channel_id: "sloc_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.SALES_CHANNEL]: { - sales_channel_id: "sc_123", - }, - [Modules.STOCK_LOCATION]: { - sales_channel_id: "sloc_123", - }, -}) -``` - - -# Links between Promotion Module and Other Modules - -This document showcases the module links defined between the Promotion Module and other Commerce Modules. - -## Summary - -The Promotion Module has the following links to other modules: - -Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. - -|First Data Model|Second Data Model|Type|Description| -|---|---|---|---| -| in ||Stored - many-to-many|| -| in ||Read-only - has one|| -| in ||Stored - many-to-many|| - *** ## Cart Module -A promotion can be applied on line items and shipping methods of a cart. Medusa defines a link between the `Cart` and `Promotion` data models. +The [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md) provides cart-management features. -![A diagram showcasing an example of how data models from the Cart and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711538015/Medusa%20Resources/cart-promotion_kuh9vm.jpg) +Medusa defines a link between the `Order` and `Cart` data models. The order is linked to the cart used for the purchased. -Medusa also defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `LineItemAdjustment` data model and the `Promotion` data model. Because the link is read-only from the `LineItemAdjustment`'s side, you can only retrieve the promotion applied on a line item, and not the other way around. +![A diagram showcasing an example of how data models from the Cart and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728375735/Medusa%20Resources/cart-order_ijwmfs.jpg) ### Retrieve with Query -To retrieve the carts that a promotion is applied on with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `carts.*` in `fields`: - -To retrieve the promotion of a line item adjustment, pass `promotion.*` in `fields`. +To retrieve the cart of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `cart.*` in `fields`: ### query.graph ```ts -const { data: promotions } = await query.graph({ - entity: "promotion", +const { data: orders } = await query.graph({ + entity: "order", fields: [ - "carts.*", + "cart.*", ], }) -// promotions[0].carts +// orders[0].cart ``` ### useQueryGraphStep @@ -27861,19 +24980,19 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: promotions } = useQueryGraphStep({ - entity: "promotion", +const { data: orders } = useQueryGraphStep({ + entity: "order", fields: [ - "carts.*", + "cart.*", ], }) -// promotions[0].carts +// orders[0].cart ``` ### Manage with Link -To manage the promotions of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): +To manage the cart of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -27883,12 +25002,12 @@ import { Modules } from "@medusajs/framework/utils" // ... await link.create({ + [Modules.ORDER]: { + order_id: "order_123", + }, [Modules.CART]: { cart_id: "cart_123", }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", - }, }) ``` @@ -27901,18 +25020,233 @@ import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" // ... createRemoteLinkStep({ + [Modules.ORDER]: { + order_id: "order_123", + }, [Modules.CART]: { cart_id: "cart_123", }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", - }, }) ``` *** -## Order Module +## Fulfillment Module + +A fulfillment is created for an orders' items. Medusa defines a link between the `Fulfillment` and `Order` data models. + +![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716549903/Medusa%20Resources/order-fulfillment_h0vlps.jpg) + +A fulfillment is also created for a return's items. So, Medusa defines a link between the `Fulfillment` and `Return` data models. + +![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399052/Medusa%20Resources/Social_Media_Graphics_2024_Order_Return_vetimk.jpg) + +### Retrieve with Query + +To retrieve the fulfillments of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `fulfillments.*` in `fields`: + +To retrieve the fulfillments of a return, pass `fulfillments.*` in `fields`. + +### query.graph + +```ts +const { data: orders } = await query.graph({ + entity: "order", + fields: [ + "fulfillments.*", + ], +}) + +// orders[0].fulfillments +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "fulfillments.*", + ], +}) + +// orders[0].fulfillments +``` + +### Manage with Link + +To manage the fulfillments of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.FULFILLMENT]: { + fulfillment_id: "ful_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.FULFILLMENT]: { + fulfillment_id: "ful_123", + }, +}) +``` + +*** + +## Payment Module + +An order's payment details are stored in a payment collection. This also applies for claims and exchanges. + +So, Medusa defines links between the `PaymentCollection` data model and the `Order`, `OrderClaim`, and `OrderExchange` data models. + +![A diagram showcasing an example of how data models from the Order and Payment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716554726/Medusa%20Resources/order-payment_ubdwok.jpg) + +### Retrieve with Query + +To retrieve the payment collections of an order, order exchange, or order claim with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `payment_collections.*` in `fields`: + +### query.graph + +```ts +const { data: orders } = await query.graph({ + entity: "order", + fields: [ + "payment_collections.*", + ], +}) + +// orders[0].payment_collections +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "payment_collections.*", + ], +}) + +// orders[0].payment_collections +``` + +### Manage with Link + +To manage the payment collections of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PAYMENT]: { + payment_collection_id: "paycol_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PAYMENT]: { + payment_collection_id: "paycol_123", + }, +}) +``` + +*** + +## Product Module + +Medusa defines read-only links between: + +- the `OrderLineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `Product` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `OrderLineItem` data model. +- the `OrderLineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `ProductVariant` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `OrderLineItem` data model. + +### Retrieve with Query + +To retrieve the variant of a line item with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`: + +To retrieve the product, pass `product.*` in `fields`. + +### query.graph + +```ts +const { data: lineItems } = await query.graph({ + entity: "order_line_item", + fields: [ + "variant.*", + ], +}) + +// lineItems.variant +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: lineItems } = useQueryGraphStep({ + entity: "order_line_item", + fields: [ + "variant.*", + ], +}) + +// lineItems.variant +``` + +*** + +## Promotion Module An order is associated with the promotion applied on it. Medusa defines a link between the `Order` and `Promotion` data models. @@ -27920,19 +25254,19 @@ An order is associated with the promotion applied on it. Medusa defines a link b ### Retrieve with Query -To retrieve the orders a promotion is applied on with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `orders.*` in `fields`: +To retrieve the promotion applied on an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `promotion.*` in `fields`: ### query.graph ```ts -const { data: promotions } = await query.graph({ - entity: "promotion", +const { data: orders } = await query.graph({ + entity: "order", fields: [ - "orders.*", + "promotion.*", ], }) -// promotions[0].orders +// orders[0].promotion ``` ### useQueryGraphStep @@ -27942,14 +25276,14 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: promotions } = useQueryGraphStep({ - entity: "promotion", +const { data: orders } = useQueryGraphStep({ + entity: "order", fields: [ - "orders.*", + "promotion.*", ], }) -// promotions[0].orders +// orders[0].promotion ``` ### Manage with Link @@ -27991,6 +25325,1097 @@ createRemoteLinkStep({ }) ``` +*** + +## Region Module + +Medusa defines a read-only link between the `Order` data model and the [Region Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/region/index.html.md)'s `Region` data model. This means you can retrieve the details of an order's region, but you don't manage the links in a pivot table in the database. The region of an order is determined by the `region_id` property of the `Order` data model. + +### Retrieve with Query + +To retrieve the region of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `region.*` in `fields`: + +### query.graph + +```ts +const { data: orders } = await query.graph({ + entity: "order", + fields: [ + "region.*", + ], +}) + +// orders[0].region +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "region.*", + ], +}) + +// orders[0].region +``` + +*** + +## Sales Channel Module + +Medusa defines a read-only link between the `Order` data model and the [Sales Channel Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md)'s `SalesChannel` data model. This means you can retrieve the details of an order's sales channel, but you don't manage the links in a pivot table in the database. The sales channel of an order is determined by the `sales_channel_id` property of the `Order` data model. + +### Retrieve with Query + +To retrieve the sales channel of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channel.*` in `fields`: + +### query.graph + +```ts +const { data: orders } = await query.graph({ + entity: "order", + fields: [ + "sales_channel.*", + ], +}) + +// orders[0].sales_channel +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "sales_channel.*", + ], +}) + +// orders[0].sales_channel +``` + + +# Order Change + +In this document, you'll learn about the Order Change data model and possible actions in it. + +## OrderChange Data Model + +The [OrderChange data model](https://docs.medusajs.com/references/order/models/OrderChange/index.html.md) represents any kind of change to an order, such as a return, exchange, or edit. + +Its `change_type` property indicates what the order change is created for: + +1. `edit`: The order change is making edits to the order, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/edit/index.html.md). +2. `exchange`: The order change is associated with an exchange, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/exchange/index.html.md). +3. `claim`: The order change is associated with a claim, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/claim/index.html.md). +4. `return_request` or `return_receive`: The order change is associated with a return, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). + +Once the order change is confirmed, its changes are applied on the order. + +*** + +## Order Change Actions + +The actions to perform on the original order by a change, such as adding an item, are represented by the [OrderChangeAction data model](https://docs.medusajs.com/references/order/models/OrderChangeAction/index.html.md). + +The `OrderChangeAction` has an `action` property that indicates the type of action to perform on the order, and a `details` property that holds more details related to the action. + +The following table lists the possible `action` values that Medusa uses and what `details` they carry. + +|Action|Description|Details| +|---|---|---|---|---| +|\`ITEM\_ADD\`|Add an item to the order.|\`details\`| +|\`ITEM\_UPDATE\`|Update an item in the order.|\`details\`| +|\`RETURN\_ITEM\`|Set an item to be returned.|\`details\`| +|\`RECEIVE\_RETURN\_ITEM\`|Mark a return item as received.|\`details\`| +|\`RECEIVE\_DAMAGED\_RETURN\_ITEM\`|Mark a return item that's damaged as received.|\`details\`| +|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | +|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | +|\`WRITE\_OFF\_ITEM\`|Remove an item's quantity as part of the claim, without adding the quantity back to the item variant's inventory.|\`details\`| + + +# Order Versioning + +In this document, you’ll learn how an order and its details are versioned. + +## What's Versioning? + +Versioning means assigning a version number to a record, such as an order and its items. This is useful to view the different versions of the order following changes in its lifetime. + +When changes are made on an order, such as an item is added or returned, the order's version changes. + +*** + +## version Property + +The `Order` and `OrderSummary` data models have a `version` property that indicates the current version. By default, its value is `1`. + +Other order-related data models, such as `OrderItem`, also has a `version` property, but it indicates the version it belongs to. + +*** + +## How the Version Changes + +When the order is changed, such as an item is exchanged, this changes the version of the order and its related data: + +1. The version of the order and its summary is incremented. +2. Related order data that have a `version` property, such as the `OrderItem`, are duplicated. The duplicated item has the new version, whereas the original item has the previous version. + +When the order is retrieved, only the related data having the same version is retrieved. + + +# Tax Lines in Order Module + +In this document, you’ll learn about tax lines in an order. + +## What are Tax Lines? + +A tax line indicates the tax rate of a line item or a shipping method. + +The [OrderLineItemTaxLine data model](https://docs.medusajs.com/references/order/models/OrderLineItemTaxLine/index.html.md) represents a line item’s tax line, and the [OrderShippingMethodTaxLine data model](https://docs.medusajs.com/references/order/models/OrderShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line. + +![A diagram showcasing the relation between orders, items and shipping methods, and tax lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307225/Medusa%20Resources/order-tax-lines_sixujd.jpg) + +*** + +## Tax Inclusivity + +By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount and then adding it to the item/method’s subtotal. + +However, line items and shipping methods have an `is_tax_inclusive` property that, when enabled, indicates that the item or method’s price already includes taxes. + +So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal. + +The following diagram is a simplified showcase of how a subtotal is calculated from the tax perspective. + +![A diagram showcasing how a subtotal is calculated from the tax perspective](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307395/Medusa%20Resources/order-tax-inclusive_oebdnm.jpg) + +For example, if a line item's amount is `5000`, the tax rate is `10`, and `is_tax_inclusive` is enabled, the tax amount is 10% of `5000`, which is `500`. The item's unit price becomes `4500`. + + +# Promotions Adjustments in Orders + +In this document, you’ll learn how a promotion is applied to an order’s items and shipping methods using adjustment lines. + +## What are Adjustment Lines? + +An adjustment line indicates a change to a line item or a shipping method’s amount. It’s used to apply promotions or discounts on an order. + +The [OrderLineItemAdjustment data model](https://docs.medusajs.com/references/order/models/OrderLineItemAdjustment/index.html.md) represents changes on a line item, and the [OrderShippingMethodAdjustment data model](https://docs.medusajs.com/references/order/models/OrderShippingMethodAdjustment/index.html.md) represents changes on a shipping method. + +![A diagram showcasing the relation between an order, its items and shipping methods, and their adjustment lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712306017/Medusa%20Resources/order-adjustments_myflir.jpg) + +The `amount` property of the adjustment line indicates the amount to be discounted from the original amount. + +The ID of the applied promotion is stored in the `promotion_id` property of the adjustment line. + +*** + +## Discountable Option + +The `OrderLineItem` data model has an `is_discountable` property that indicates whether promotions can be applied to the line item. It’s enabled by default. + +When disabled, a promotion can’t be applied to a line item. In the context of the Promotion Module, the promotion isn’t applied to the line item even if it matches its rules. + +*** + +## Promotion Actions + +When using the Order and Promotion modules together, use the [computeActions method of the Promotion Module’s main service](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). It retrieves the actions of line items and shipping methods. + +Learn more about actions in the [Promotion Module’s documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md). + +```ts collapsibleLines="1-10" expandButtonLabel="Show Imports" +import { + ComputeActionAdjustmentLine, + ComputeActionItemLine, + ComputeActionShippingLine, + // ... +} from "@medusajs/framework/types" + +// ... + +// retrieve the order +const order = await orderModuleService.retrieveOrder("ord_123", { + relations: [ + "items.item.adjustments", + "shipping_methods.shipping_method.adjustments", + ], +}) +// retrieve the line item adjustments +const lineItemAdjustments: ComputeActionItemLine[] = [] +order.items.forEach((item) => { + const filteredAdjustments = item.adjustments?.filter( + (adjustment) => adjustment.code !== undefined + ) as unknown as ComputeActionAdjustmentLine[] + if (filteredAdjustments.length) { + lineItemAdjustments.push({ + ...item, + ...item.detail, + adjustments: filteredAdjustments, + }) + } +}) + +//retrieve shipping method adjustments +const shippingMethodAdjustments: ComputeActionShippingLine[] = + [] +order.shipping_methods.forEach((shippingMethod) => { + const filteredAdjustments = + shippingMethod.adjustments?.filter( + (adjustment) => adjustment.code !== undefined + ) as unknown as ComputeActionAdjustmentLine[] + if (filteredAdjustments.length) { + shippingMethodAdjustments.push({ + ...shippingMethod, + adjustments: filteredAdjustments, + }) + } +}) + +// compute actions +const actions = await promotionModuleService.computeActions( + ["promo_123"], + { + items: lineItemAdjustments, + shipping_methods: shippingMethodAdjustments, + // TODO infer from cart or region + currency_code: "usd", + } +) +``` + +The `computeActions` method accepts the existing adjustments of line items and shipping methods to compute the actions accurately. + +Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the order’s line items and the shipping method’s adjustments. + +```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" +import { + AddItemAdjustmentAction, + AddShippingMethodAdjustment, + // ... +} from "@medusajs/framework/types" + +// ... + +await orderModuleService.setOrderLineItemAdjustments( + order.id, + actions.filter( + (action) => action.action === "addItemAdjustment" + ) as AddItemAdjustmentAction[] +) + +await orderModuleService.setOrderShippingMethodAdjustments( + order.id, + actions.filter( + (action) => + action.action === "addShippingMethodAdjustment" + ) as AddShippingMethodAdjustment[] +) +``` + + +# Transactions + +In this document, you’ll learn about an order’s transactions and its use. + +## What is a Transaction? + +A transaction represents any order payment process, such as capturing or refunding an amount. It’s represented by the [OrderTransaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). + +The transaction’s main purpose is to ensure a correct balance between paid and outstanding amounts. + +Transactions are also associated with returns, claims, and exchanges if additional payment or refund is required. + +*** + +## Checking Outstanding Amount + +The order’s total amounts are stored in the `OrderSummary`'s `totals` property, which is a JSON object holding the total details of the order. + +```json +{ + "totals": { + "total": 30, + "subtotal": 30, + // ... + } +} +``` + +To check the outstanding amount of the order, its transaction amounts are summed. Then, the following conditions are checked: + +|Condition|Result| +|---|---|---| +|summary’s total - transaction amounts total = 0|There’s no outstanding amount.| +|summary’s total - transaction amounts total > 0|The customer owes additional payment to the merchant.| +|summary’s total - transaction amounts total \< 0|The merchant owes the customer a refund.| + +*** + +## Transaction Reference + +The Order Module doesn’t provide payment processing functionalities, so it doesn’t store payments that can be processed. Payment functionalities are provided by the [Payment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/index.html.md). + +The `OrderTransaction` data model has two properties that determine which data model and record holds the actual payment’s details: + +- `reference`: indicates the table’s name in the database. For example, `payment` from the Payment Module. +- `reference_id`: indicates the ID of the record in the table. For example, `pay_123`. + + +# Order Return + +In this document, you’ll learn about order returns. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/returns/index.html.md) to learn how to manage an order's returns using the dashboard. + +## What is a Return? + +A return is the return of items delivered from the customer back to the merchant. It is represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md). + +A return is requested either by the customer from the storefront, or the merchant from the admin. Medusa supports an automated Return Merchandise Authorization (RMA) flow. + +![Diagram showcasing the automated RMA flow.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719578128/Medusa%20Resources/return-rma_pzprwq.jpg) + +Once the merchant receives the returned items, they mark the return as received. + +*** + +## Returned Items + +The items to be returned are represented by the [ReturnItem data model](references/order/models/ReturnItem). + +The `ReturnItem` model has two properties storing the item's quantity: + +1. `received_quantity`: The quantity of the item that's received and can be added to the item's inventory quantity. +2. `damaged_quantity`: The quantity of the item that's damaged, meaning it can't be sold again or added to the item's inventory quantity. + +*** + +## Return Shipping Methods + +A return has shipping methods used to return the items to the merchant. The shipping methods are represented by the [OrderShippingMethod data model](references/order/models/OrderShippingMethod). + +In the Medusa application, the shipping method for a return is created only from a shipping option, provided by the Fulfillment Module, that has the rule `is_return` enabled. + +*** + +## Refund Payment + +The `refund_amount` property of the `Return` data model holds the amount a merchant must refund the customer. + +The [OrderTransaction data model](references/order/models/OrderTransaction) represents the refunds made for the return. + +*** + +## Returns in Exchanges and Claims + +When a merchant creates an exchange or a claim, it includes returning items from the customer. + +The `Return` data model also represents the return of these items. In this case, the return is associated with the exchange or claim it was created for. + +*** + +## How Returns Impact an Order’s Version + +The order’s version is incremented when: + +1. A return is requested. +2. A return is marked as received. + + +# Pricing Concepts + +In this document, you’ll learn about the main concepts in the Pricing Module. + +## Price Set + +A [PriceSet](https://docs.medusajs.com/references/pricing/models/PriceSet/index.html.md) represents a collection of prices that are linked to a resource (for example, a product or a shipping option). + +Each of these prices are represented by the [Price data module](https://docs.medusajs.com/references/pricing/models/Price/index.html.md). + +![A diagram showcasing the relation between the price set and price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648650/Medusa%20Resources/price-set-money-amount_xeees0.jpg) + +*** + +## Price List + +A [PriceList](https://docs.medusajs.com/references/pricing/models/PriceList/index.html.md) is a group of prices only enabled if their conditions and rules are satisfied. + +A price list has optional `start_date` and `end_date` properties that indicate the date range in which a price list can be applied. + +Its associated prices are represented by the `Price` data model. + + +# Links between Currency Module and Other Modules + +This document showcases the module links defined between the Currency Module and other Commerce Modules. + +## Summary + +The Currency Module has the following links to other modules: + +Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. + +|First Data Model|Second Data Model|Type|Description| +|---|---|---|---| +| in ||Read-only - has one|| + +*** + +## Store Module + +The Store Module has a `Currency` data model that stores the supported currencies of a store. However, these currencies don't hold all the details of a currency, such as its name or symbol. + +Instead, Medusa defines a read-only link between the [Store Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/store/index.html.md)'s `StoreCurrency` data model and the Currency Module's `Currency` data model. Because the link is read-only from the `Store`'s side, you can only retrieve the details of a store's supported currencies, and not the other way around. + +### Retrieve with Query + +To retrieve the details of a store's currencies with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `supported_currencies.currency.*` in `fields`: + +### query.graph + +```ts +const { data: stores } = await query.graph({ + entity: "store", + fields: [ + "supported_currencies.currency.*", + ], +}) + +// stores[0].supported_currencies[0].currency +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: stores } = useQueryGraphStep({ + entity: "store", + fields: [ + "supported_currencies.currency.*", + ], +}) + +// stores[0].supported_currencies[0].currency +``` + + +# Price Rules + +In this Pricing Module guide, you'll learn about price rules for price sets and price lists, and how to add rules to a price. + +## Price Rule + +You can restrict prices by rules. Each rule of a price is represented by the [PriceRule data model](https://docs.medusajs.com/references/pricing/models/PriceRule/index.html.md). + +The `Price` data model has a `rules_count` property, which indicates how many rules, represented by `PriceRule`, are applied to the price. + +For exmaple, you create a price restricted to `10557` zip codes. + +![A diagram showcasing the relation between the PriceRule and Price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg) + +A price can have multiple price rules. + +For example, a price can be restricted by a region and a zip code. + +![A diagram showcasing the relation between the PriceRule and Price with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg) + +*** + +## Price List Rules + +Rules applied to a price list are represented by the [PriceListRule data model](https://docs.medusajs.com/references/pricing/models/PriceListRule/index.html.md). + +The `rules_count` property of a `PriceList` indicates how many rules are applied to it. + +![A diagram showcasing the relation between the PriceSet, PriceList, Price, RuleType, and PriceListRuleValue](https://res.cloudinary.com/dza7lstvk/image/upload/v1709641999/Medusa%20Resources/price-list_zd10yd.jpg) + +*** + +## How to Set Rules on a Price? + +### Using Workflows + +Medusa uses the Pricing Module to store prices of different resources, such as product variants and shipping options. + +When you manage one of these resources using [Medusa's workflows](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/medusa-workflows-reference/index.html.md) or using the API routes that use them, you can set rules on a price using the `rules` property of the price object. + +For example, when creating a shipping option using the [createShippingOptionsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createShippingOptionsWorkflow/index.html.md) to create a shipping option, you can make the shipping price free based on the cart total: + +```ts highlights={workflowHighlights} +const { result } = await createShippingOptionsWorkflow(container) + .run({ + input: [{ + name: "Standard Shipping", + service_zone_id: "serzo_123", + shipping_profile_id: "sp_123", + provider_id: "prov_123", + type: { + label: "Standard", + description: "Standard shipping", + code: "standard", + }, + price_type: "flat", + prices: [ + // default price + { + currency_code: "usd", + amount: 10, + rules: {}, + }, + // price if cart total >= $100 + { + currency_code: "usd", + amount: 0, + rules: { + item_total: { + operator: "gte", + value: 100, + }, + }, + }, + ], + }], + }) +``` + +In this example, you create a shipping option whose default price is `$10`. When the total of the cart or order using this shipping option is greater than `$100`, the shipping option's price becomes free. + +### Using Pricing Module's Service + +For most use cases, it's recommended to use [workflows](#using-workflows) instead of directly using the module's service. + +When adding a price using the [addPrices](https://docs.medusajs.com/resources/references/pricing/addPrices/index.html.md) method of the Pricing Module's service, pass the `rules` property to a price object. + +For example: + +```ts +const priceSet = await pricingModule.addPrices({ + priceSetId: "pset_1", + prices: [ + // default price + { + currency_code: "usd", + amount: 10, + rules: {}, + }, + // price if cart total >= $100 + { + currency_code: "usd", + amount: 0, + rules: { + item_total: { + operator: "gte", + value: 100, + }, + }, + }, + ], +}) +``` + +In this example, you set the default price of a resource (for example, a shipping option), to `$10`. You also add a conditioned price that sets the price to `0` when the cart or order's total is greater than or equal to `$100`. + +### How is the Price Rule Applied? + +The [price calculation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md) mechanism considers a price applicable when the resource that this price is in matches the specified rules. + +For example, a [cart object](https://docs.medusajs.com/api/store#carts_cart_schema) has an `item_total` property. So, if a shipping option has the following price: + +```json +{ + "currency_code": "usd", + "amount": 0, + "rules": { + "item_total": { + "operator": "gte", + "value": 100, + } + } +} +``` + +The shipping option's price is applied when the cart's `item_total` is greater than or equal to `$100`. + +You can also apply the rule on nested relations and properties. For example, to apply a shipping option's price based on the customer's group, you can apply a rule on the `customer.group.id` attribute: + +```json +{ + "currency_code": "usd", + "amount": 0, + "rules": { + "customer.group.id": { + "operator": "eq", + "value": "cusgrp_123" + } + } +} +``` + +In this example, the price is only applied if a cart's customer belongs to the customer group of ID `cusgrp_123`. + +These same rules apply to product variant prices as well, or any other resource that has a price. + + +# Links between Pricing Module and Other Modules + +This document showcases the module links defined between the Pricing Module and other Commerce Modules. + +## Summary + +The Pricing Module has the following links to other modules: + +|First Data Model|Second Data Model|Type|Description| +|---|---|---|---| +| in ||Stored - one-to-one|| +| in ||Stored - one-to-one|| + +*** + +## Fulfillment Module + +The Fulfillment Module provides fulfillment-related functionalities, including shipping options that the customer chooses from when they place their order. However, it doesn't provide pricing-related functionalities for these options. + +Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set. + +![A diagram showcasing an example of how data models from the Pricing and Fulfillment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716561747/Medusa%20Resources/pricing-fulfillment_spywwa.jpg) + +### Retrieve with Query + +To retrieve the shipping option of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `shipping_option.*` in `fields`: + +### query.graph + +```ts +const { data: priceSets } = await query.graph({ + entity: "price_set", + fields: [ + "shipping_option.*", + ], +}) + +// priceSets[0].shipping_option +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: priceSets } = useQueryGraphStep({ + entity: "price_set", + fields: [ + "shipping_option.*", + ], +}) + +// priceSets[0].shipping_option +``` + +### Manage with Link + +To manage the price set of a shipping option, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.FULFILLMENT]: { + shipping_option_id: "so_123", + }, + [Modules.PRICING]: { + price_set_id: "pset_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.FULFILLMENT]: { + shipping_option_id: "so_123", + }, + [Modules.PRICING]: { + price_set_id: "pset_123", + }, +}) +``` + +*** + +## Product Module + +The Product Module doesn't store or manage the prices of product variants. + +Medusa defines a link between the `ProductVariant` and the `PriceSet`. A product variant’s prices are stored as prices belonging to a price set. + +![A diagram showcasing an example of how data models from the Pricing and Product Module are linked. The PriceSet is linked to the ProductVariant of the Product Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651039/Medusa%20Resources/pricing-product_m4xaut.jpg) + +So, when you want to add prices for a product variant, you create a price set and add the prices to it. + +You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context. + +### Retrieve with Query + +To retrieve the variant of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`: + +### query.graph + +```ts +const { data: priceSets } = await query.graph({ + entity: "price_set", + fields: [ + "variant.*", + ], +}) + +// priceSets[0].variant +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: priceSets } = useQueryGraphStep({ + entity: "price_set", + fields: [ + "variant.*", + ], +}) + +// priceSets[0].variant +``` + +### Manage with Link + +To manage the price set of a variant, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.PRODUCT]: { + variant_id: "variant_123", + }, + [Modules.PRICING]: { + price_set_id: "pset_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.PRODUCT]: { + variant_id: "variant_123", + }, + [Modules.PRICING]: { + price_set_id: "pset_123", + }, +}) +``` + + +# Prices Calculation + +In this document, you'll learn how prices are calculated when you use the [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) of the Pricing Module's main service. + +## calculatePrices Method + +The [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) accepts as parameters the ID of one or more price sets and a context. + +It returns a price object with the best matching price for each price set. + +### Calculation Context + +The calculation context is an optional object passed as a second parameter to the `calculatePrices` method. It accepts rules to restrict the selected prices in the price set. + +For example: + +```ts +const price = await pricingModuleService.calculatePrices( + { id: [priceSetId] }, + { + context: { + currency_code: currencyCode, + region_id: "reg_123", + }, + } +) +``` + +In this example, you retrieve the prices in a price set for the specified currency code and region ID. + +### Returned Price Object + +For each price set, the `calculatePrices` method selects two prices: + +- A calculated price: Either a price that belongs to a price list and best matches the specified context, or the same as the original price. +- An original price, which is either: + - The same price as the calculated price if the price list it belongs to is of type `override`; + - Or a price that doesn't belong to a price list and best matches the specified context. + +Both prices are returned in an object that has the following properties: + +- id: (\`string\`) The ID of the price set from which the price was selected. +- is\_calculated\_price\_price\_list: (\`boolean\`) Whether the calculated price belongs to a price list. +- calculated\_amount: (\`number\`) The amount of the calculated price, or \`null\` if there isn't a calculated price. This is the amount shown to the customer. +- is\_original\_price\_price\_list: (\`boolean\`) Whether the original price belongs to a price list. +- original\_amount: (\`number\`) The amount of the original price, or \`null\` if there isn't an original price. This amount is useful to compare with the \`calculated\_amount\`, such as to check for discounted value. +- currency\_code: (\`string\`) The currency code of the calculated price, or \`null\` if there isn't a calculated price. +- is\_calculated\_price\_tax\_inclusive: (\`boolean\`) Whether the calculated price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx) +- is\_original\_price\_tax\_inclusive: (\`boolean\`) Whether the original price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx) +- calculated\_price: (\`object\`) The calculated price's price details. + + - id: (\`string\`) The ID of the price. + + - price\_list\_id: (\`string\`) The ID of the associated price list. + + - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`. + + - min\_quantity: (\`number\`) The price's min quantity condition. + + - max\_quantity: (\`number\`) The price's max quantity condition. +- original\_price: (\`object\`) The original price's price details. + + - id: (\`string\`) The ID of the price. + + - price\_list\_id: (\`string\`) The ID of the associated price list. + + - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`. + + - min\_quantity: (\`number\`) The price's min quantity condition. + + - max\_quantity: (\`number\`) The price's max quantity condition. + +*** + +## Examples + +Consider the following price set: + +```ts +const priceSet = await pricingModuleService.createPriceSets({ + prices: [ + // default price + { + amount: 500, + currency_code: "EUR", + rules: {}, + }, + // prices with rules + { + amount: 400, + currency_code: "EUR", + rules: { + region_id: "reg_123", + }, + }, + { + amount: 450, + currency_code: "EUR", + rules: { + city: "krakow", + }, + }, + { + amount: 500, + currency_code: "EUR", + rules: { + city: "warsaw", + region_id: "reg_123", + }, + }, + ], +}) +``` + +### Default Price Selection + +### Code + +```ts +const price = await pricingModuleService.calculatePrices( + { id: [priceSet.id] }, + { + context: { + currency_code: "EUR" + } + } +) +``` + +### Result + +### Calculate Prices with Rules + +### Code + +```ts +const price = await pricingModuleService.calculatePrices( + { id: [priceSet.id] }, + { + context: { + currency_code: "EUR", + region_id: "reg_123", + city: "krakow" + } + } +) +``` + +### Result + +### Price Selection with Price List + +### Code + +```ts +const priceList = pricingModuleService.createPriceLists([{ + title: "Summer Price List", + description: "Price list for summer sale", + starts_at: Date.parse("01/10/2023").toString(), + ends_at: Date.parse("31/10/2023").toString(), + rules: { + region_id: ['PL'] + }, + type: "sale", + prices: [ + { + amount: 400, + currency_code: "EUR", + price_set_id: priceSet.id, + }, + { + amount: 450, + currency_code: "EUR", + price_set_id: priceSet.id, + }, + ], +}]); + +const price = await pricingModuleService.calculatePrices( + { id: [priceSet.id] }, + { + context: { + currency_code: "EUR", + region_id: "PL", + city: "krakow" + } + } +) +``` + +### Result + + +# Tax-Inclusive Pricing + +In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices. + +## What is Tax-Inclusive Pricing? + +A tax-inclusive price is a price of a resource that includes taxes. Medusa calculates the tax amount from the price rather than adds the amount to it. + +For example, if a product’s price is $50, the tax rate is 2%, and tax-inclusive pricing is enabled, then the product's price is $49, and the applied tax amount is $1. + +*** + +## How is Tax-Inclusive Pricing Set? + +The [PricePreference data model](https://docs.medusajs.com/references/pricing/models/PricePreference/index.html.md) holds the tax-inclusive setting for a context. It has two properties that indicate the context: + +- `attribute`: The name of the attribute to compare against. For example, `region_id` or `currency_code`. +- `value`: The attribute’s value. For example, `reg_123` or `usd`. + +Only `region_id` and `currency_code` are supported as an `attribute` at the moment. + +The `is_tax_inclusive` property indicates whether tax-inclusivity is enabled in the specified context. + +For example: + +```json +{ + "attribute": "currency_code", + "value": "USD", + "is_tax_inclusive": true, +} +``` + +In this example, tax-inclusivity is enabled for the `USD` currency code. + +*** + +## Tax-Inclusive Pricing in Price Calculation + +### Tax Context + +As mentioned in the [Price Calculation documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md), The `calculatePrices` method accepts as a parameter a calculation context. + +To get accurate tax results, pass the `region_id` and / or `currency_code` in the calculation context. + +### Returned Tax Properties + +The `calculatePrices` method returns two properties related to tax-inclusivity: + +Learn more about the returned properties in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md). + +- `is_calculated_price_tax_inclusive`: Whether the selected `calculated_price` is tax-inclusive. +- `is_original_price_tax_inclusive` : Whether the selected `original_price` is tax-inclusive. + +A price is considered tax-inclusive if: + +1. It belongs to the region or currency code specified in the calculation context; +2. and the region or currency code has a price preference with `is_tax_inclusive` enabled. + +### Tax Context Precedence + +A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if: + +- both the `region_id` and `currency_code` are provided in the calculation context; +- the selected price belongs to the region; +- and the region has a price preference + # Configure Selling Products @@ -28556,6 +26981,815 @@ The following guides provide more details on inventory management in the Medusa - [Storefront guide: how to retrieve a product variant's inventory details](https://docs.medusajs.com/resources/storefront-development/products/inventory/index.html.md). +# Account Holders and Saved Payment Methods + +In this documentation, you'll learn about account holders, and how they're used to save payment methods in third-party payment providers. + +Account holders are available starting from Medusa `v2.5.0`. + +## What's an Account Holder? + +An account holder represents a customer that can have saved payment methods in a third-party service. It's represented by the `AccountHolder` data model. + +It holds fields retrieved from the third-party provider, such as: + +- `external_id`: The ID of the equivalent customer or account holder in the third-party provider. +- `data`: Data returned by the payment provider when the account holder is created. + +A payment provider that supports saving payment methods for customers would create the equivalent of an account holder in the third-party provider. Then, whenever a payment method is saved, it would be saved under the account holder in the third-party provider. + +### Relation between Account Holder and Customer + +The Medusa application creates a link between the [Customer](https://docs.medusajs.com/references/customer/models/Customer/index.html.md) data model of the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md) and the `AccountHolder` data model of the Payment Module. + +This link indicates that a customer can have more than one account holder, each representing saved payment methods in different payment providers. + +Learn more about this link in the [Link to Other Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/index.html.md) guide. + +*** + +## Save Payment Methods + +If a payment provider supports saving payment methods for a customer, they must implement the following methods: + +- `createAccountHolder`: Creates an account holder in the payment provider. The Payment Module uses this method before creating the account holder in Medusa, and uses the returned data to set fields like `external_id` and `data` in the created `AccountHolder` record. +- `deleteAccountHolder`: Deletes an account holder in the payment provider. The Payment Module uses this method when an account holder is deleted in Medusa. +- `savePaymentMethod`: Saves a payment method for an account holder in the payment provider. +- `listPaymentMethods`: Lists saved payment methods in the third-party service for an account holder. This is useful when displaying the customer's saved payment methods in the storefront. + +Learn more about implementing these methods in the [Create Payment Provider guide](https://docs.medusajs.com/references/payment/provider/index.html.md). + +*** + +## Account Holder in Medusa Payment Flows + +In the Medusa application, when a payment session is created for a registered customer, the Medusa application uses the Payment Module to create an account holder for the customer. + +Consequently, the Payment Module uses the payment provider to create an account holder in the third-party service, then creates the account holder in Medusa. + +This flow is only supported if the chosen payment provider has implemented the necessary [save payment methods](#save-payment-methods). + + +# Payment Module Options + +In this document, you'll learn about the options of the Payment Module. + +## All Module Options + +|Option|Description|Required|Default| +|---|---|---|---|---|---|---| +|\`webhook\_delay\`|A number indicating the delay in milliseconds before processing a webhook event.|No|\`5000\`| +|\`webhook\_retries\`|The number of times to retry the webhook event processing in case of an error.|No|\`3\`| +|\`providers\`|An array of payment providers to install and register. Learn more |No|-| + +*** + +## providers Option + +The `providers` option is an array of payment module providers. + +When the Medusa application starts, these providers are registered and can be used to process payments. + +For example: + +```ts title="medusa-config.ts" +import { Modules } from "@medusajs/framework/utils" + +// ... + +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/medusa/payment", + options: { + providers: [ + { + resolve: "@medusajs/medusa/payment-stripe", + id: "stripe", + options: { + // ... + }, + }, + ], + }, + }, + ], +}) +``` + +The `providers` option is an array of objects that accept the following properties: + +- `resolve`: A string indicating the package name of the module provider or the path to it relative to the `src` directory. +- `id`: A string indicating the provider's unique name or ID. +- `options`: An optional object of the module provider's options. + + +# Links between Payment Module and Other Modules + +This document showcases the module links defined between the Payment Module and other Commerce Modules. + +## Summary + +The Payment Module has the following links to other modules: + +|First Data Model|Second Data Model|Type|Description| +|---|---|---|---| +| in ||Stored - one-to-one|| +| in ||Stored - many-to-many|| +| in ||Stored - one-to-many|| +| in ||Stored - one-to-many|| +| in ||Stored - one-to-many|| +| in ||Stored - many-to-many|| + +*** + +## Cart Module + +The Cart Module provides cart-related features, but not payment processing. + +Medusa defines a link between the `Cart` and `PaymentCollection` data models. A cart has a payment collection which holds all the authorized payment sessions and payments made related to the cart. + +Learn more about this relation in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-collection#usage-with-the-cart-module/index.html.md). + +### Retrieve with Query + +To retrieve the cart associated with the payment collection with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `cart.*` in `fields`: + +### query.graph + +```ts +const { data: paymentCollections } = await query.graph({ + entity: "payment_collection", + fields: [ + "cart.*", + ], +}) + +// paymentCollections[0].cart +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: paymentCollections } = useQueryGraphStep({ + entity: "payment_collection", + fields: [ + "cart.*", + ], +}) + +// paymentCollections[0].cart +``` + +### Manage with Link + +To manage the payment collection of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.CART]: { + cart_id: "cart_123", + }, + [Modules.PAYMENT]: { + payment_collection_id: "paycol_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.CART]: { + cart_id: "cart_123", + }, + [Modules.PAYMENT]: { + payment_collection_id: "paycol_123", + }, +}) +``` + +*** + +## Customer Module + +Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it. + +This link is available starting from Medusa `v2.5.0`. + +### Retrieve with Query + +To retrieve the customer associated with an account holder with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: + +### query.graph + +```ts +const { data: accountHolders } = await query.graph({ + entity: "account_holder", + fields: [ + "customer.*", + ], +}) + +// accountHolders[0].customer +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: accountHolders } = useQueryGraphStep({ + entity: "account_holder", + fields: [ + "customer.*", + ], +}) + +// accountHolders[0].customer +``` + +### Manage with Link + +To manage the account holders of a customer, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + +*** + +## Order Module + +An order's payment details are stored in a payment collection. This also applies for claims and exchanges. + +So, Medusa defines links between the `PaymentCollection` data model and the `Order`, `OrderClaim`, and `OrderExchange` data models. + +![A diagram showcasing an example of how data models from the Order and Payment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716554726/Medusa%20Resources/order-payment_ubdwok.jpg) + +### Retrieve with Query + +To retrieve the order of a payment collection with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `order.*` in `fields`: + +### query.graph + +```ts +const { data: paymentCollections } = await query.graph({ + entity: "payment_collection", + fields: [ + "order.*", + ], +}) + +// paymentCollections[0].order +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: paymentCollections } = useQueryGraphStep({ + entity: "payment_collection", + fields: [ + "order.*", + ], +}) + +// paymentCollections[0].order +``` + +### Manage with Link + +To manage the payment collections of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PAYMENT]: { + payment_collection_id: "paycol_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PAYMENT]: { + payment_collection_id: "paycol_123", + }, +}) +``` + +*** + +## Region Module + +You can specify for each region which payment providers are available. The Medusa application defines a link between the `PaymentProvider` and the `Region` data models. + +![A diagram showcasing an example of how resources from the Payment and Region modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711569520/Medusa%20Resources/payment-region_jyo2dz.jpg) + +This increases the flexibility of your store. For example, you only show during checkout the payment providers associated with the cart's region. + +### Retrieve with Query + +To retrieve the regions of a payment provider with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `regions.*` in `fields`: + +### query.graph + +```ts +const { data: paymentProviders } = await query.graph({ + entity: "payment_provider", + fields: [ + "regions.*", + ], +}) + +// paymentProviders[0].regions +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: paymentProviders } = useQueryGraphStep({ + entity: "payment_provider", + fields: [ + "regions.*", + ], +}) + +// paymentProviders[0].regions +``` + +### Manage with Link + +To manage the payment providers in a region, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.REGION]: { + region_id: "reg_123", + }, + [Modules.PAYMENT]: { + payment_provider_id: "pp_stripe_stripe", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.REGION]: { + region_id: "reg_123", + }, + [Modules.PAYMENT]: { + payment_provider_id: "pp_stripe_stripe", + }, +}) +``` + + +# Payment + +In this document, you’ll learn what a payment is and how it's created, captured, and refunded. + +## What's a Payment? + +When a payment session is authorized, a payment, represented by the [Payment data model](https://docs.medusajs.com/references/payment/models/Payment/index.html.md), is created. This payment can later be captured or refunded. + +A payment carries many of the data and relations of a payment session: + +- It belongs to the same payment collection. +- It’s associated with the same payment provider, which handles further payment processing. +- It stores the payment session’s `data` property in its `data` property, as it’s still useful for the payment provider’s processing. + +*** + +## Capture Payments + +When a payment is captured, a capture, represented by the [Capture data model](https://docs.medusajs.com/references/payment/models/Capture/index.html.md), is created. It holds details related to the capture, such as the amount, the capture date, and more. + +The payment can also be captured incrementally, each time a capture record is created for that amount. + +![A diagram showcasing how a payment's multiple captures are stored](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565445/Medusa%20Resources/payment-capture_f5fve1.jpg) + +*** + +## Refund Payments + +When a payment is refunded, a refund, represented by the [Refund data model](https://docs.medusajs.com/references/payment/models/Refund/index.html.md), is created. It holds details related to the refund, such as the amount, refund date, and more. + +A payment can be refunded multiple times, and each time a refund record is created. + +![A diagram showcasing how a payment's multiple refunds are stored](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565555/Medusa%20Resources/payment-refund_lgfvyy.jpg) + + +# Payment Collection + +In this document, you’ll learn what a payment collection is and how the Medusa application uses it with the Cart Module. + +## What's a Payment Collection? + +A payment collection stores payment details related to a resource, such as a cart or an order. It’s represented by the [PaymentCollection data model](https://docs.medusajs.com/references/payment/models/PaymentCollection/index.html.md). + +Every purchase or request for payment starts with a payment collection. The collection holds details necessary to complete the payment, including: + +- The [payment sessions](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-session/index.html.md) that represents the payment amount to authorize. +- The [payments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment/index.html.md) that are created when a payment session is authorized. They can be captured and refunded. +- The [payment providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/index.html.md) that handle the processing of each payment session, including the authorization, capture, and refund. + +*** + +## Multiple Payments + +The payment collection supports multiple payment sessions and payments. + +You can use this to accept payments in increments or split payments across payment providers. + +![Diagram showcasing how a payment collection can have multiple payment sessions and payments](https://res.cloudinary.com/dza7lstvk/image/upload/v1711554695/Medusa%20Resources/payment-collection-multiple-payments_oi3z3n.jpg) + +*** + +## Usage with the Cart Module + +The Cart Module provides cart management features. However, it doesn’t provide any features related to accepting payment. + +During checkout, the Medusa application links a cart to a payment collection, which will be used for further payment processing. + +It also implements the payment flow during checkout as explained in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-flow/index.html.md). + +![Diagram showcasing the relation between the Payment and Cart modules](https://res.cloudinary.com/dza7lstvk/image/upload/v1711537849/Medusa%20Resources/cart-payment_ixziqm.jpg) + + +# Accept Payment Flow + +In this document, you’ll learn how to implement an accept-payment flow using workflows or the Payment Module's main service. + +It's highly recommended to use Medusa's workflows to implement this flow. Use the Payment Module's main service for more complex cases. + +For a guide on how to implement this flow in the storefront, check out [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/checkout/payment/index.html.md). + +## Flow Overview + +![A diagram showcasing the payment flow's steps](https://res.cloudinary.com/dza7lstvk/image/upload/v1711566781/Medusa%20Resources/payment-flow_jblrvw.jpg) + +*** + +## 1. Create a Payment Collection + +A payment collection holds all details related to a resource’s payment operations. So, you start off by creating a payment collection. + +For example: + +### Using Workflow + +```ts +import { createPaymentCollectionForCartWorkflow } from "@medusajs/medusa/core-flows" + +// ... + +await createPaymentCollectionForCartWorkflow(req.scope) + .run({ + input: { + cart_id: "cart_123", + }, + }) +``` + +### Using Service + +```ts +const paymentCollection = + await paymentModuleService.createPaymentCollections({ + currency_code: "usd", + amount: 5000, + }) +``` + +*** + +## 2. Create Payment Sessions + +The payment collection has one or more payment sessions, each being a payment amount to be authorized by a payment provider. + +So, after creating the payment collection, create at least one payment session for a provider. + +For example: + +### Using Workflow + +```ts +import { createPaymentSessionsWorkflow } from "@medusajs/medusa/core-flows" + +// ... + +const { result: paymentSesion } = await createPaymentSessionsWorkflow(req.scope) + .run({ + input: { + payment_collection_id: "paycol_123", + provider_id: "stripe", + }, + }) +``` + +### Using Service + +```ts +const paymentSession = + await paymentModuleService.createPaymentSession( + paymentCollection.id, + { + provider_id: "stripe", + currency_code: "usd", + amount: 5000, + data: { + // any necessary data for the + // payment provider + }, + } + ) +``` + +*** + +## 3. Authorize Payment Session + +Once the customer chooses a payment session, start the authorization process. This may involve some action performed by the third-party payment provider, such as entering a 3DS code. + +For example: + +### Using Step + +```ts +import { authorizePaymentSessionStep } from "@medusajs/medusa/core-flows" + +// ... + +authorizePaymentSessionStep({ + id: "payses_123", + context: {}, +}) +``` + +### Using Service + +```ts +const payment = authorizePaymentSessionStep({ + id: "payses_123", + context: {}, +}) +``` + +When the payment authorization is successful, a payment is created and returned. + +### Handling Additional Action + +If you used the `authorizePaymentSessionStep`, you don't need to implement this logic as it's implemented in the step. + +If the payment authorization isn’t successful, whether because it requires additional action or for another reason, the method updates the payment session with the new status and throws an error. + +In that case, you can catch that error and, if the session's `status` property is `requires_more`, handle the additional action, then retry the authorization. + +For example: + +```ts +try { + const payment = + await paymentModuleService.authorizePaymentSession( + paymentSession.id, + {} + ) +} catch (e) { + // retrieve the payment session again + const updatedPaymentSession = ( + await paymentModuleService.listPaymentSessions({ + id: [paymentSession.id], + }) + )[0] + + if (updatedPaymentSession.status === "requires_more") { + // TODO perform required action + // TODO authorize payment again. + } +} +``` + +*** + +## 4. Payment Flow Complete + +The payment flow is complete once the payment session is authorized and the payment is created. + +You can then: + +- Capture the payment either using the [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/index.html.md) or [capturePayment method](https://docs.medusajs.com/references/payment/capturePayment/index.html.md). +- Refund captured amounts using the [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md) or [refundPayment method](https://docs.medusajs.com/references/payment/refundPayment/index.html.md). + +Some payment providers allow capturing the payment automatically once it’s authorized. In that case, you don’t need to do it manually. + + +# Webhook Events + +In this document, you’ll learn how the Payment Module supports listening to webhook events. + +## What's a Webhook Event? + +A webhook event is sent from a third-party payment provider to your application. It indicates a change in a payment’s status. + +This is useful in many cases such as when a payment is being processed asynchronously or when a request is interrupted and the payment provider is sending details on the process later. + +*** + +## getWebhookActionAndData Method + +The Payment Module’s main service has a [getWebhookActionAndData method](https://docs.medusajs.com/references/payment/getWebhookActionAndData/index.html.md) used to handle incoming webhook events from third-party payment services. The method delegates the handling to the associated payment provider, which returns the event's details. + +Medusa implements a webhook listener route at the `/hooks/payment/[identifier]_[provider]` API route, where: + +- `[identifier]` is the `identifier` static property defined in the payment provider. For example, `stripe`. +- `[provider]` is the ID of the provider. For example, `stripe`. + +For example, when integrating basic Stripe payments with the [Stripe Module Provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/stripe/index.html.md), the webhook listener route is `/hooks/payment/stripe_stripe`. If you're integrating Stripe's Bancontact payments, the webhook listener route is `/hooks/payment/stripe-bancontact_stripe`. + +Use that webhook listener in your third-party payment provider's configurations. + +![A diagram showcasing the steps of how the getWebhookActionAndData method words](https://res.cloudinary.com/dza7lstvk/image/upload/v1711567415/Medusa%20Resources/payment-webhook_seaocg.jpg) + +If the event's details indicate that the payment should be authorized, then the [authorizePaymentSession method of the main service](https://docs.medusajs.com/references/payment/authorizePaymentSession/index.html.md) is executed on the specified payment session. + +If the event's details indicate that the payment should be captured, then the [capturePayment method of the main service](https://docs.medusajs.com/references/payment/capturePayment/index.html.md) is executed on the payment of the specified payment session. + +### Actions After Webhook Payment Processing + +After the payment webhook actions are processed and the payment is authorized or captured, the Medusa application completes the cart associated with the payment's collection if it's not completed yet. + + +# Payment Session + +In this document, you’ll learn what a payment session is. + +## What's a Payment Session? + +A payment session, represented by the [PaymentSession data model](https://docs.medusajs.com/references/payment/models/PaymentSession/index.html.md), is a payment amount to be authorized. It’s associated with a payment provider that handles authorizing it. + +A payment collection can have multiple payment sessions. Using this feature, you can implement payment in installments or payments using multiple providers. + +![Diagram showcasing how every payment session has a different payment provider](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565056/Medusa%20Resources/payment-session-provider_guxzqt.jpg) + +*** + +## data Property + +Payment providers may need additional data to process the payment later. The `PaymentSession` data model has a `data` property used to store that data. + +For example, the customer's ID in Stripe is stored in the `data` property. + +*** + +## Payment Session Status + +The `status` property of a payment session indicates its current status. Its value can be: + +- `pending`: The payment session is awaiting authorization. +- `requires_more`: The payment session requires an action before it’s authorized. For example, to enter a 3DS code. +- `authorized`: The payment session is authorized. +- `error`: An error occurred while authorizing the payment. +- `canceled`: The authorization of the payment session has been canceled. + + +# Payment Module Provider + +In this document, you’ll learn what a payment module provider is. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage the payment providers available in a region using the dashboard. + +## What's a Payment Module Provider? + +A payment module provider registers a payment provider that handles payment processing in the Medusa application. It integrates third-party payment providers, such as Stripe. + +To authorize a payment amount with a payment provider, a payment session is created and associated with that payment provider. The payment provider is then used to handle the authorization. + +After the payment session is authorized, the payment provider is associated with the resulting payment and handles its payment processing, such as to capture or refund payment. + +### List of Payment Module Providers + +- [Stripe](https://docs.medusajs.com/commerce-modules/payment/payment-provider/stripe/index.html.md) + +*** + +## System Payment Provider + +The Payment Module provides a `system` payment provider that acts as a placeholder payment provider. + +It doesn’t handle payment processing and delegates that to the merchant. It acts similarly to a cash-on-delivery (COD) payment method. + +*** + +## How are Payment Providers Created? + +A payment provider is a module whose main service extends the `AbstractPaymentProvider` imported from `@medusajs/framework/utils`. + +Refer to [this guide](https://docs.medusajs.com/references/payment/provider/index.html.md) on how to create a payment provider for the Payment Module. + +*** + +## Configure Payment Providers + +The Payment Module accepts a `providers` option that allows you to register providers in your application. + +Learn more about this option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/module-options#providers/index.html.md). + +*** + +## PaymentProvider Data Model + +When the Medusa application starts and registers the payment providers, it also creates a record of the `PaymentProvider` data model if none exists. + +This data model is used to reference a payment provider and determine whether it’s installed in the application. + + # Links between Region Module and Other Modules This document showcases the module links defined between the Region Module and other Commerce Modules. @@ -28736,6 +27970,253 @@ createRemoteLinkStep({ ``` +# Stock Location Concepts + +In this document, you’ll learn about the main concepts in the Stock Location Module. + +## Stock Location + +A stock location, represented by the `StockLocation` data model, represents a location where stock items are kept. For example, a warehouse. + +Medusa uses stock locations to provide inventory details, from the Inventory Module, per location. + +*** + +## StockLocationAddress + +The `StockLocationAddress` data model belongs to the `StockLocation` data model. It provides more detailed information of the location, such as country code or street address. + + +# Links between Stock Location Module and Other Modules + +This document showcases the module links defined between the Stock Location Module and other Commerce Modules. + +## Summary + +The Stock Location Module has the following links to other modules: + +Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. + +|First Data Model|Second Data Model|Type|Description| +|---|---|---|---| +| in ||Stored - many-to-one|| +| in ||Stored - many-to-many|| +| in ||Read-only - has many|| +| in ||Stored - many-to-many|| + +*** + +## Fulfillment Module + +A fulfillment set can be conditioned to a specific stock location. + +Medusa defines a link between the `FulfillmentSet` and `StockLocation` data models. + +![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1712567101/Medusa%20Resources/fulfillment-stock-location_nlkf7e.jpg) + +Medusa also defines a link between the `FulfillmentProvider` and `StockLocation` data models to indicate the providers that can be used in a location. + +![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399492/Medusa%20Resources/fulfillment-provider-stock-location_b0mulo.jpg) + +### Retrieve with Query + +To retrieve the fulfillment sets of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `fulfillment_sets.*` in `fields`: + +To retrieve the fulfillment providers, pass `fulfillment_providers.*` in `fields`. + +### query.graph + +```ts +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: [ + "fulfillment_sets.*", + ], +}) + +// stockLocations[0].fulfillment_sets +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: stockLocations } = useQueryGraphStep({ + entity: "stock_location", + fields: [ + "fulfillment_sets.*", + ], +}) + +// stockLocations[0].fulfillment_sets +``` + +### Manage with Link + +To manage the stock location of a fulfillment set, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.STOCK_LOCATION]: { + stock_location_id: "sloc_123", + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: "fset_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.STOCK_LOCATION]: { + stock_location_id: "sloc_123", + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: "fset_123", + }, +}) +``` + +*** + +## Inventory Module + +Medusa defines a read-only link between the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md)'s `InventoryLevel` data model and the `StockLocation` data model. Because the link is read-only from the `InventoryLevel`'s side, you can only retrieve the stock location of an inventory level, and not the other way around. + +### Retrieve with Query + +To retrieve the stock locations of an inventory level with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `stock_locations.*` in `fields`: + +### query.graph + +```ts +const { data: inventoryLevels } = await query.graph({ + entity: "inventory_level", + fields: [ + "stock_locations.*", + ], +}) + +// inventoryLevels[0].stock_locations +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: inventoryLevels } = useQueryGraphStep({ + entity: "inventory_level", + fields: [ + "stock_locations.*", + ], +}) + +// inventoryLevels[0].stock_locations +``` + +*** + +## Sales Channel Module + +A stock location is associated with a sales channel. This scopes inventory quantities in a stock location by the associated sales channel. + +Medusa defines a link between the `SalesChannel` and `StockLocation` data models. + +![A diagram showcasing an example of how resources from the Sales Channel and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716796872/Medusa%20Resources/sales-channel-location_cqrih1.jpg) + +### Retrieve with Query + +To retrieve the sales channels of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channels.*` in `fields`: + +### query.graph + +```ts +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: [ + "sales_channels.*", + ], +}) + +// stockLocations[0].sales_channels +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: stockLocations } = useQueryGraphStep({ + entity: "stock_location", + fields: [ + "sales_channels.*", + ], +}) + +// stockLocations[0].sales_channels +``` + +### Manage with Link + +To manage the stock locations of a sales channel, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.SALES_CHANNEL]: { + sales_channel_id: "sc_123", + }, + [Modules.STOCK_LOCATION]: { + sales_channel_id: "sloc_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.SALES_CHANNEL]: { + sales_channel_id: "sc_123", + }, + [Modules.STOCK_LOCATION]: { + sales_channel_id: "sloc_123", + }, +}) +``` + + # Links between Sales Channel Module and Other Modules This document showcases the module links defined between the Sales Channel Module and other Commerce Modules. @@ -29196,6 +28677,123 @@ const { data: stores } = useQueryGraphStep({ ``` +# User Module Options + +In this document, you'll learn about the options of the User Module. + +## Module Options + +```ts title="medusa-config.ts" +import { Modules } from "@medusajs/framework/utils" + +// ... + +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/user", + options: { + jwt_secret: process.env.JWT_SECRET, + }, + }, + ], +}) +``` + +|Option|Description|Required| +|---|---|---|---|---| +|\`jwt\_secret\`|A string indicating the secret used to sign the invite tokens.|Yes| + +### Environment Variables + +Make sure to add the necessary environment variables for the above options in `.env`: + +```bash +JWT_SECRET=supersecret +``` + + +# User Creation Flows + +In this document, learn the different ways to create a user using the User Module. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/users/index.html.md) to learn how to manage users using the dashboard. + +## Straightforward User Creation + +To create a user, use the [create method of the User Module’s main service](https://docs.medusajs.com/references/user/create/index.html.md): + +```ts +const user = await userModuleService.createUsers({ + email: "user@example.com", +}) +``` + +You can pair this with the Auth Module to allow the user to authenticate, as explained in a [later section](#create-identity-with-the-auth-module). + +*** + +## Invite Users + +To create a user, you can create an invite for them using the [createInvites method](https://docs.medusajs.com/references/user/createInvites/index.html.md) of the User Module's main service: + +```ts +const invite = await userModuleService.createInvites({ + email: "user@example.com", +}) +``` + +Later, you can accept the invite and create a new user for them: + +```ts +const invite = + await userModuleService.validateInviteToken("secret_123") + +await userModuleService.updateInvites({ + id: invite.id, + accepted: true, +}) + +const user = await userModuleService.createUsers({ + email: invite.email, +}) +``` + +### Invite Expiry + +An invite has an expiry date. You can renew the expiry date and refresh the token using the [refreshInviteTokens method](https://docs.medusajs.com/references/user/refreshInviteTokens/index.html.md): + +```ts +await userModuleService.refreshInviteTokens(["invite_123"]) +``` + +*** + +## Create Identity with the Auth Module + +By combining the User and Auth Modules, you can use the Auth Module for authenticating users, and the User Module to manage those users. + +So, when a user is authenticated, and you receive the `AuthIdentity` object, you can use it to create a user if it doesn’t exist: + +```ts +const { success, authIdentity } = + await authModuleService.authenticate("emailpass", { + // ... + }) + +const [, count] = await userModuleService.listAndCountUsers({ + email: authIdentity.entity_id, +}) + +if (!count) { + const user = await userModuleService.createUsers({ + email: authIdentity.entity_id, + }) +} +``` + + # Tax Module Options In this document, you'll learn about the options of the Tax Module. @@ -29316,21 +28914,6 @@ TODO add once tax provider guide is updated + add module providers match other m Refer to [this guide](/modules/tax/provider) to learn more about creating a tax provider. */} -# Tax Region - -In this document, you’ll learn about tax regions and how to use them with the Region Module. - -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions/index.html.md) to learn how to manage tax regions using the dashboard. - -## What is a Tax Region? - -A tax region, represented by the [TaxRegion data model](https://docs.medusajs.com/references/tax/models/TaxRegion/index.html.md), stores tax settings related to a region that your store serves. - -Tax regions can inherit settings and rules from a parent tax region. - -Each tax region has tax rules and a tax provider. - - # Tax Rates and Rules In this document, you’ll learn about tax rates and rules. @@ -29369,122 +28952,356 @@ These two properties of the data model identify the rule’s target: So, to override the default tax rate for product types “Shirt”, you create a tax rate and associate with it a tax rule whose `reference` is `product_type` and `reference_id` the ID of the “Shirt” product type. -# User Module Options +# Tax Region -In this document, you'll learn about the options of the User Module. +In this document, you’ll learn about tax regions and how to use them with the Region Module. -## Module Options +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions/index.html.md) to learn how to manage tax regions using the dashboard. -```ts title="medusa-config.ts" -import { Modules } from "@medusajs/framework/utils" +## What is a Tax Region? -// ... +A tax region, represented by the [TaxRegion data model](https://docs.medusajs.com/references/tax/models/TaxRegion/index.html.md), stores tax settings related to a region that your store serves. -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "@medusajs/user", - options: { - jwt_secret: process.env.JWT_SECRET, - }, - }, - ], -}) -``` +Tax regions can inherit settings and rules from a parent tax region. -|Option|Description|Required| -|---|---|---|---|---| -|\`jwt\_secret\`|A string indicating the secret used to sign the invite tokens.|Yes| - -### Environment Variables - -Make sure to add the necessary environment variables for the above options in `.env`: - -```bash -JWT_SECRET=supersecret -``` +Each tax region has tax rules and a tax provider. -# User Creation Flows +# Promotion Actions -In this document, learn the different ways to create a user using the User Module. +In this document, you’ll learn about promotion actions and how they’re computed using the [computeActions method](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). -Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/users/index.html.md) to learn how to manage users using the dashboard. +## computeActions Method -## Straightforward User Creation +The Promotion Module's main service has a [computeActions method](https://docs.medusajs.com/references/promotion/computeActions/index.html.md) that returns an array of actions to perform on a cart when one or more promotions are applied. -To create a user, use the [create method of the User Module’s main service](https://docs.medusajs.com/references/user/create/index.html.md): - -```ts -const user = await userModuleService.createUsers({ - email: "user@example.com", -}) -``` - -You can pair this with the Auth Module to allow the user to authenticate, as explained in a [later section](#create-identity-with-the-auth-module). +Actions inform you what adjustment must be made to a cart item or shipping method. Each action is an object having the `action` property indicating the type of action. *** -## Invite Users +## Action Types -To create a user, you can create an invite for them using the [createInvites method](https://docs.medusajs.com/references/user/createInvites/index.html.md) of the User Module's main service: +### `addItemAdjustment` Action + +The `addItemAdjustment` action indicates that an adjustment must be made to an item. For example, removing $5 off its amount. + +This action has the following format: ```ts -const invite = await userModuleService.createInvites({ - email: "user@example.com", -}) -``` - -Later, you can accept the invite and create a new user for them: - -```ts -const invite = - await userModuleService.validateInviteToken("secret_123") - -await userModuleService.updateInvites({ - id: invite.id, - accepted: true, -}) - -const user = await userModuleService.createUsers({ - email: invite.email, -}) -``` - -### Invite Expiry - -An invite has an expiry date. You can renew the expiry date and refresh the token using the [refreshInviteTokens method](https://docs.medusajs.com/references/user/refreshInviteTokens/index.html.md): - -```ts -await userModuleService.refreshInviteTokens(["invite_123"]) -``` - -*** - -## Create Identity with the Auth Module - -By combining the User and Auth Modules, you can use the Auth Module for authenticating users, and the User Module to manage those users. - -So, when a user is authenticated, and you receive the `AuthIdentity` object, you can use it to create a user if it doesn’t exist: - -```ts -const { success, authIdentity } = - await authModuleService.authenticate("emailpass", { - // ... - }) - -const [, count] = await userModuleService.listAndCountUsers({ - email: authIdentity.entity_id, -}) - -if (!count) { - const user = await userModuleService.createUsers({ - email: authIdentity.entity_id, - }) +export interface AddItemAdjustmentAction { + action: "addItemAdjustment" + item_id: string + amount: number + code: string + description?: string } ``` +This action means that a new record should be created of the `LineItemAdjustment` data model in the Cart Module, or `OrderLineItemAdjustment` data model in the Order Module. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.AddItemAdjustmentAction/index.html.md) for details on the object’s properties. + +### `removeItemAdjustment` Action + +The `removeItemAdjustment` action indicates that an adjustment must be removed from a line item. For example, remove the $5 discount. + +The `computeActions` method accepts any previous item adjustments in the `items` property of the second parameter. + +This action has the following format: + +```ts +export interface RemoveItemAdjustmentAction { + action: "removeItemAdjustment" + adjustment_id: string + description?: string + code: string +} +``` + +This action means that a new record should be removed of the `LineItemAdjustment` (or `OrderLineItemAdjustment`) with the specified ID in the `adjustment_id` property. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.RemoveItemAdjustmentAction/index.html.md) for details on the object’s properties. + +### `addShippingMethodAdjustment` Action + +The `addShippingMethodAdjustment` action indicates that an adjustment must be made on a shipping method. For example, make the shipping method free. + +This action has the following format: + +```ts +export interface AddShippingMethodAdjustment { + action: "addShippingMethodAdjustment" + shipping_method_id: string + amount: number + code: string + description?: string +} +``` + +This action means that a new record should be created of the `ShippingMethodAdjustment` data model in the Cart Module, or `OrderShippingMethodAdjustment` data model in the Order Module. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.AddShippingMethodAdjustment/index.html.md) for details on the object’s properties. + +### `removeShippingMethodAdjustment` Action + +The `removeShippingMethodAdjustment` action indicates that an adjustment must be removed from a shipping method. For example, remove the free shipping discount. + +The `computeActions` method accepts any previous shipping method adjustments in the `shipping_methods` property of the second parameter. + +This action has the following format: + +```ts +export interface RemoveShippingMethodAdjustment { + action: "removeShippingMethodAdjustment" + adjustment_id: string + code: string +} +``` + +When the Medusa application receives this action type, it removes the `ShippingMethodAdjustment` (or `OrderShippingMethodAdjustment`) with the specified ID in the `adjustment_id` property. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.RemoveShippingMethodAdjustment/index.html.md) for details on the object’s properties. + +### `campaignBudgetExceeded` Action + +When the `campaignBudgetExceeded` action is returned, the promotions within a campaign can no longer be used as the campaign budget has been exceeded. + +This action has the following format: + +```ts +export interface CampaignBudgetExceededAction { + action: "campaignBudgetExceeded" + code: string +} +``` + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.CampaignBudgetExceededAction/index.html.md) for details on the object’s properties. + + +# Application Method + +In this document, you'll learn what an application method is. + +## What is an Application Method? + +The [ApplicationMethod data model](https://docs.medusajs.com/references/promotion/models/ApplicationMethod/index.html.md) defines how a promotion is applied: + +|Property|Purpose| +|---|---| +|\`type\`|Does the promotion discount a fixed amount or a percentage?| +|\`target\_type\`|Is the promotion applied on a cart item, shipping method, or the entire order?| +|\`allocation\`|Is the discounted amount applied on each item or split between the applicable items?| + +## Target Promotion Rules + +When the promotion is applied to a cart item or a shipping method, you can restrict which items/shipping methods the promotion is applied to. + +The `ApplicationMethod` data model has a collection of `PromotionRule` records to restrict which items or shipping methods the promotion applies to. The `target_rules` property represents this relation. + +![A diagram showcasing the target\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898273/Medusa%20Resources/application-method-target-rules_hqaymz.jpg) + +In this example, the promotion is only applied on products in the cart having the SKU `SHIRT`. + +*** + +## Buy Promotion Rules + +When the promotion’s type is `buyget`, you must specify the “buy X” condition. For example, a cart must have two shirts before the promotion can be applied. + +The application method has a collection of `PromotionRule` items to define the “buy X” rule. The `buy_rules` property represents this relation. + +![A diagram showcasing the buy\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898453/Medusa%20Resources/application-method-buy-rules_djjuhw.jpg) + +In this example, the cart must have two products with the SKU `SHIRT` for the promotion to be applied. + + +# Promotion Concepts + +In this guide, you’ll learn about the main promotion and rule concepts in the Promotion Module. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/index.html.md) to learn how to manage promotions using the dashboard. + +## What is a Promotion? + +A promotion, represented by the [Promotion data model](https://docs.medusajs.com/references/promotion/models/Promotion/index.html.md), is a discount that can be applied on cart items, shipping methods, or entire orders. + +A promotion has two types: + +- `standard`: A standard promotion with rules. +- `buyget`: “A buy X get Y” promotion with rules. + +|\`standard\`|\`buyget\`| +|---|---| +|A coupon code that gives customers 10% off their entire order.|Buy two shirts and get another for free.| +|A coupon code that gives customers $15 off any shirt in their order.|Buy two shirts and get 10% off the entire order.| +|A discount applied automatically for VIP customers that removes 10% off their shipping method’s amount.|Spend $100 and get free shipping.| + +The Medusa Admin UI may not provide a way to create each of these promotion examples. However, they are supported by the Promotion Module and Medusa's workflows and API routes. + +*** + +## Promotion Rules + +A promotion can be restricted by a set of rules, each rule is represented by the [PromotionRule data model](https://docs.medusajs.com/references/promotion/models/PromotionRule/index.html.md). + +For example, you can create a promotion that only customers of the `VIP` customer group can use. + +![A diagram showcasing the relation between Promotion and PromotionRule](https://res.cloudinary.com/dza7lstvk/image/upload/v1709833196/Medusa%20Resources/promotion-promotion-rule_msbx0w.jpg) + +A `PromotionRule`'s `attribute` property indicates the property's name to which this rule is applied. For example, `customer_group_id`. + +The expected value for the attribute is stored in the `PromotionRuleValue` data model. So, a rule can have multiple values. + +When testing whether a promotion can be applied to a cart, the rule's `attribute` property and its values are tested on the cart itself. + +For example, the cart's customer must be part of the customer group(s) indicated in the promotion rule's value. + +### Flexible Rules + +The `PromotionRule`'s `operator` property adds more flexibility to the rule’s condition rather than simple equality (`eq`). + +For example, to restrict the promotion to only `VIP` and `B2B` customer groups: + +- Add a `PromotionRule` record with its `attribute` property set to `customer_group_id` and `operator` property to `in`. +- Add two `PromotionRuleValue` records associated with the rule: one with the value `VIP` and the other `B2B`. + +![A diagram showcasing the relation between PromotionRule and PromotionRuleValue when a rule has multiple values](https://res.cloudinary.com/dza7lstvk/image/upload/v1709897383/Medusa%20Resources/promotion-promotion-rule-multiple_hctpmt.jpg) + +In this case, a customer’s group must be in the `VIP` and `B2B` set of values to use the promotion. + +*** + +## How to Apply Rules on a Promotion? + +### Using Workflows + +If you're managing promotions using [Medusa's workflows](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/medusa-workflows-reference/index.html.md) or the API routes that use them, you can specify rules for the promotion or its [application method](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/application-method/index.html.md). + +For example, if you're creating a promotion using the [createPromotionsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createPromotionsWorkflow/index.html.md): + +```ts +const { result } = await createPromotionsWorkflow(container) + .run({ + input: { + promotionsData: [{ + code: "10OFF", + type: "standard", + status: "active", + application_method: { + type: "percentage", + target_type: "items", + allocation: "across", + value: 10, + currency_code: "usd", + }, + rules: [ + { + attribute: "customer.group.id", + operator: "eq", + values: [ + "cusgrp_123", + ], + }, + ], + }], + }, + }) +``` + +In this example, the promotion is restricted to customers with the `cusgrp_123` customer group. + +### Using Promotion Module's Service + +For most use cases, it's recommended to use [workflows](#using-workflows) instead of directly using the module's service. + +If you're managing promotions using the Promotion Module's service, you can specify rules for the promotion or its [application method](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/application-method/index.html.md) in its methods. + +For example, if you're creating a promotion with the [createPromotions](https://docs.medusajs.com/resources/references/promotion/createPromotions/index.html.md) method: + +```ts +const promotions = await promotionModuleService.createPromotions([ + { + code: "50OFF", + type: "standard", + status: "active", + application_method: { + type: "percentage", + target_type: "items", + value: 50, + }, + rules: [ + { + attribute: "customer.group.id", + operator: "eq", + values: [ + "cusgrp_123", + ], + }, + ], + }, +]) +``` + +In this example, the promotion is restricted to customers with the `cusgrp_123` customer group. + +### How is the Promotion Rule Applied? + +A promotion is applied on a resource if its attributes match the promotion's rules. + +For example, consider you have the following promotion with a rule that restricts the promotion to a specific customer: + +```json +{ + "code": "10OFF", + "type": "standard", + "status": "active", + "application_method": { + "type": "percentage", + "target_type": "items", + "allocation": "across", + "value": 10, + "currency_code": "usd" + }, + "rules": [ + { + "attribute": "customer_id", + "operator": "eq", + "values": [ + "cus_123" + ] + } + ] +} +``` + +When you try to apply this promotion on a cart, the cart's `customer_id` is compared to the promotion rule's value based on the specified operator. So, the promotion will only be applied if the cart's `customer_id` is equal to `cus_123`. + + +# Campaign + +In this document, you'll learn about campaigns. + +Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/campaigns/index.html.md) to learn how to manage campaigns using the dashboard. + +## What is a Campaign? + +A [Campaign](https://docs.medusajs.com/references/promotion/models/Campaign/index.html.md) combines promotions under the same conditions, such as start and end dates. + +![A diagram showcasing the relation between the Campaign and Promotion data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899225/Medusa%20Resources/campagin-promotion_hh3qsi.jpg) + +*** + +## Campaign Limits + +Each campaign has a budget represented by the [CampaignBudget data model](https://docs.medusajs.com/references/promotion/models/CampaignBudget/index.html.md). The budget limits how many times the promotion can be used. + +There are two types of budgets: + +- `spend`: An amount that, when crossed, the promotion becomes unusable. For example, if the amount limit is set to `$100`, and the total amount of usage of this promotion crosses that threshold, the promotion can no longer be applied. +- `usage`: The number of times that a promotion can be used. For example, if the usage limit is set to `10`, the promotion can be used only 10 times by customers. After that, it can no longer be applied. + +![A diagram showcasing the relation between the Campaign and CampaignBudget data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899463/Medusa%20Resources/campagin-budget_rvqlmi.jpg) + # Emailpass Auth Module Provider @@ -29548,6 +29365,189 @@ const hashConfig = \{ - [How to register a customer using email and password](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/register/index.html.md) +# Links between Promotion Module and Other Modules + +This document showcases the module links defined between the Promotion Module and other Commerce Modules. + +## Summary + +The Promotion Module has the following links to other modules: + +Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. + +|First Data Model|Second Data Model|Type|Description| +|---|---|---|---| +| in ||Stored - many-to-many|| +| in ||Read-only - has one|| +| in ||Stored - many-to-many|| + +*** + +## Cart Module + +A promotion can be applied on line items and shipping methods of a cart. Medusa defines a link between the `Cart` and `Promotion` data models. + +![A diagram showcasing an example of how data models from the Cart and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711538015/Medusa%20Resources/cart-promotion_kuh9vm.jpg) + +Medusa also defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `LineItemAdjustment` data model and the `Promotion` data model. Because the link is read-only from the `LineItemAdjustment`'s side, you can only retrieve the promotion applied on a line item, and not the other way around. + +### Retrieve with Query + +To retrieve the carts that a promotion is applied on with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `carts.*` in `fields`: + +To retrieve the promotion of a line item adjustment, pass `promotion.*` in `fields`. + +### query.graph + +```ts +const { data: promotions } = await query.graph({ + entity: "promotion", + fields: [ + "carts.*", + ], +}) + +// promotions[0].carts +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: promotions } = useQueryGraphStep({ + entity: "promotion", + fields: [ + "carts.*", + ], +}) + +// promotions[0].carts +``` + +### Manage with Link + +To manage the promotions of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.CART]: { + cart_id: "cart_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.CART]: { + cart_id: "cart_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` + +*** + +## Order Module + +An order is associated with the promotion applied on it. Medusa defines a link between the `Order` and `Promotion` data models. + +![A diagram showcasing an example of how data models from the Order and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716555015/Medusa%20Resources/order-promotion_dgjzzd.jpg) + +### Retrieve with Query + +To retrieve the orders a promotion is applied on with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `orders.*` in `fields`: + +### query.graph + +```ts +const { data: promotions } = await query.graph({ + entity: "promotion", + fields: [ + "orders.*", + ], +}) + +// promotions[0].orders +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: promotions } = useQueryGraphStep({ + entity: "promotion", + fields: [ + "orders.*", + ], +}) + +// promotions[0].orders +``` + +### Manage with Link + +To manage the promotion of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): + +### link.create + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` + + # GitHub Auth Module Provider In this document, you’ll learn about the GitHub Auth Module Provider and how to install and use it in the Auth Module. @@ -29717,139 +29717,6 @@ The [Authenticate or Login API Route](https://docs.medusajs.com/Users/shahednass - [How to implement Google social login in the storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/third-party-login/index.html.md). -# Stripe Module Provider - -In this document, you’ll learn about the Stripe Module Provider and how to configure it in the Payment Module. - -Your technical team must install the Stripe Module Provider in your Medusa application first. Then, refer to [this user guide](https://docs.medusajs.com/user-guide/settings/regions#edit-region-details/index.html.md) to learn how to enable the Stripe payment provider in a region using the Medusa Admin dashboard. - -## Register the Stripe Module Provider - -### Prerequisites - -- [Stripe account](https://stripe.com/) -- [Stripe Secret API Key](https://support.stripe.com/questions/locate-api-keys-in-the-dashboard) -- [For deployed Medusa applications, a Stripe webhook secret. Refer to the end of this guide for details on the URL and events.](https://docs.stripe.com/webhooks#add-a-webhook-endpoint) - -The Stripe Module Provider is installed by default in your application. To use it, add it to the array of providers passed to the Payment Module in `medusa-config.ts`: - -```ts title="medusa-config.ts" -import { Modules } from "@medusajs/framework/utils" - -// ... - -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "@medusajs/medusa/payment", - options: { - providers: [ - { - resolve: "@medusajs/medusa/payment-stripe", - id: "stripe", - options: { - apiKey: process.env.STRIPE_API_KEY, - }, - }, - ], - }, - }, - ], -}) -``` - -### Environment Variables - -Make sure to add the necessary environment variables for the above options in `.env`: - -```bash -STRIPE_API_KEY= -``` - -### Module Options - -|Option|Description|Required|Default| -|---|---|---|---|---|---|---| -|\`apiKey\`|A string indicating the Stripe Secret API key.|Yes|-| -|\`webhookSecret\`|A string indicating the Stripe webhook secret. This is only useful for deployed Medusa applications.|Yes|-| -|\`capture\`|Whether to automatically capture payment after authorization.|No|\`false\`| -|\`automatic\_payment\_methods\`|A boolean value indicating whether to enable Stripe's automatic payment methods. This is useful if you integrate services like Apple pay or Google pay.|No|\`false\`| -|\`payment\_description\`|A string used as the default description of a payment if none is available in cart.context.payment\_description.|No|-| - -*** - -## Enable Stripe Providers in a Region - -Before customers can use Stripe to complete their purchases, you must enable the Stripe payment provider(s) in the region where you want to offer this payment method. - -Refer to the [user guide](https://docs.medusajs.com/user-guide/settings/regions#edit-region-details/index.html.md) to learn how to edit a region and enable the Stripe payment provider. - -*** - -## Stripe Payment Provider IDs - -When you register the Stripe Module Provider, it registers different providers, such as basic Stripe payment, Bancontact, and more. - -Each provider is registered and referenced by a unique ID made up of the format `pp_{identifier}_{id}`, where: - -- `{identifier}` is the ID of the payment provider as defined in the Stripe Module Provider. -- `{id}` is the ID of the Stripe Module Provider as set in the `medusa-config.ts` file. For example, `stripe`. - -Assuming you set the ID of the Stripe Module Provider to `stripe` in `medusa-config.ts`, the Medusa application will register the following payment providers: - -|Provider Name|Provider ID| -|---|---|---| -|Basic Stripe Payment|\`pp\_stripe\_stripe\`| -|Bancontact Payments|\`pp\_stripe-bancontact\_stripe\`| -|BLIK Payments|\`pp\_stripe-blik\_stripe\`| -|giropay Payments|\`pp\_stripe-giropay\_stripe\`| -|iDEAL Payments|\`pp\_stripe-ideal\_stripe\`| -|Przelewy24 Payments|\`pp\_stripe-przelewy24\_stripe\`| -|PromptPay Payments|\`pp\_stripe-promptpay\_stripe\`| - -*** - -## Setup Stripe Webhooks - -For production applications, you must set up webhooks in Stripe that inform Medusa of changes and updates to payments. Refer to [Stripe's documentation](https://docs.stripe.com/webhooks#add-a-webhook-endpoint) on how to setup webhooks. - -### Webhook URL - -Medusa has a `{server_url}/hooks/payment/{provider_id}` API route that you can use to register webhooks in Stripe, where: - -- `{server_url}` is the URL to your deployed Medusa application in server mode. -- `{provider_id}` is the ID of the provider as explained in the [Stripe Payment Provider IDs](#stripe-payment-provider-ids) section, without the `pp_` prefix. - -The Stripe Module Provider supports the following payment types, and the webhook endpoint URL is different for each: - -|Stripe Payment Type|Webhook Endpoint URL| -|---|---|---| -|Basic Stripe Payment|\`\{server\_url}/hooks/payment/stripe\_stripe\`| -|Bancontact Payments|\`\{server\_url}/hooks/payment/stripe-bancontact\_stripe\`| -|BLIK Payments|\`\{server\_url}/hooks/payment/stripe-blik\_stripe\`| -|giropay Payments|\`\{server\_url}/hooks/payment/stripe-giropay\_stripe\`| -|iDEAL Payments|\`\{server\_url}/hooks/payment/stripe-ideal\_stripe\`| -|Przelewy24 Payments|\`\{server\_url}/hooks/payment/stripe-przelewy24\_stripe\`| -|PromptPay Payments|\`\{server\_url}/hooks/payment/stripe-promptpay\_stripe\`| - -### Webhook Events - -When you set up the webhook in Stripe, choose the following events to listen to: - -- `payment_intent.amount_capturable_updated` -- `payment_intent.succeeded` -- `payment_intent.payment_failed` - -*** - -## Useful Guides - -- [Storefront guide: Add Stripe payment method during checkout](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/checkout/payment/stripe/index.html.md). -- [Integrate in Next.js Starter](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter#stripe-integration/index.html.md). -- [Customize Stripe Integration in Next.js Starter](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/guides/customize-stripe/index.html.md). - - # Get Product Variant Prices using Query In this document, you'll learn how to retrieve product variant prices in the Medusa application using [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). @@ -29930,6 +29797,159 @@ For the context of the product variant's calculated price, you pass an object to Each variant in the retrieved products has a `calculated_price` object. Learn more about its properties in [this Pricing Module guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md). +# Get Product Variant Inventory Quantity + +In this guide, you'll learn how to retrieve the available inventory quantity of a product variant in your Medusa application customizations. That includes API routes, workflows, subscribers, scheduled jobs, and any resource that can access the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md). + +Refer to the [Retrieve Product Variant Inventory](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/products/inventory/index.html.md) storefront guide. + +## Understanding Product Variant Inventory Availability + +Product variants have a `manage_inventory` boolean field that indicates whether the Medusa application manages the inventory of the product variant. + +When `manage_inventory` is disabled, the Medusa application always considers the product variant to be in stock. So, you can't retrieve the inventory quantity for those products. + +When `manage_inventory` is enabled, the Medusa application tracks the inventory of the product variant using the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md). For example, when a customer purchases a product variant, the Medusa application decrements the stocked quantity of the product variant. + +This guide explains how to retrieve the inventory quantity of a product variant when `manage_inventory` is enabled. + +*** + +## Retrieve Product Variant Inventory + +To retrieve the inventory quantity of a product variant, use the `getVariantAvailability` utility function imported from `@medusajs/framework/utils`. It returns the available quantity of the product variant. + +For example: + +```ts highlights={variantAvailabilityHighlights} +import { getVariantAvailability } from "@medusajs/framework/utils" + +// ... + +// use req.scope instead of container in API routes +const query = container.resolve("query") + +const availability = await getVariantAvailability(query, { + variant_ids: ["variant_123"], + sales_channel_id: "sc_123", +}) +``` + +A product variant's inventory quantity is set per [stock location](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/index.html.md). This stock location is linked to a [sales channel](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md). + +So, to retrieve the inventory quantity of a product variant using `getVariantAvailability`, you need to also provide the ID of the sales channel to retrieve the inventory quantity in. + +Refer to the [Retrieve Sales Channel to Use](#retrieve-sales-channel-to-use) section to learn how to retrieve the sales channel ID to use in the `getVariantAvailability` function. + +### Parameters + +The `getVariantAvailability` function accepts the following parameters: + +- query: (Query) Instance of Query to retrieve the necessary data. +- options: (\`object\`) The options to retrieve the variant availability. + + - variant\_ids: (\`string\[]\`) The IDs of the product variants to retrieve their inventory availability. + + - sales\_channel\_id: (\`string\`) The ID of the sales channel to retrieve the variant availability in. + +### Returns + +The `getVariantAvailability` function resolves to an object whose keys are the IDs of each product variant passed in the `variant_ids` parameter. + +The value of each key is an object with the following properties: + +- availability: (\`number\`) The available quantity of the product variant in the stock location linked to the sales channel. If \`manage\_inventory\` is disabled, this value is \`0\`. +- sales\_channel\_id: (\`string\`) The ID of the sales channel that the availability is scoped to. + +For example, the object may look like this: + +```json title="Example result" +{ + "variant_123": { + "availability": 10, + "sales_channel_id": "sc_123" + } +} +``` + +*** + +## Retrieve Sales Channel to Use + +To retrieve the sales channel ID to use in the `getVariantAvailability` function, you can either: + +- Use the sales channel of the request's scope. +- Use the sales channel that the variant's product is available in. + +### Method 1: Use Sales Channel Scope in Store Routes + +Requests sent to API routes starting with `/store` must include a [publishable API key in the request header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/publishable-api-keys/index.html.md). This scopes the request to one or more sales channels associated with the publishable API key. + +So, if you're retrieving the variant inventory availability in an API route starting with `/store`, you can access the sales channel using the `publishable_key_context.sales_channel_ids` property of the request object: + +```ts highlights={salesChannelScopeHighlights} +import { MedusaStoreRequest, MedusaResponse } from "@medusajs/framework/http" +import { getVariantAvailability } from "@medusajs/framework/utils" + +export async function GET( + req: MedusaStoreRequest, + res: MedusaResponse +) { + const query = req.scope.resolve("query") + const sales_channel_ids = req.publishable_key_context.sales_channel_ids + + const availability = await getVariantAvailability(query, { + variant_ids: ["variant_123"], + sales_channel_id: sales_channel_ids[0], + }) + + res.json({ + availability, + }) +} +``` + +In this example, you retrieve the scope's sales channel IDs using `req.publishable_key_context.sales_channel_ids`, whose value is an array of IDs. + +Then, you pass the first sales channel ID to the `getVariantAvailability` function to retrieve the inventory availability of the product variant in that sales channel. + +Notice that the request object's type is `MedusaStoreRequest` instead of `MedusaRequest` to ensure the availability of the `publishable_key_context` property. + +### Method 2: Use Product's Sales Channel + +A product is linked to the sales channels it's available in. So, you can retrieve the details of the variant's product, including its sales channels. + +For example: + +```ts highlights={productSalesChannelHighlights} +import { getVariantAvailability } from "@medusajs/framework/utils" + +// ... + +// use req.scope instead of container in API routes +const query = container.resolve("query") + +const { data: variants } = await query.graph({ + entity: "variant", + fields: ["id", "product.sales_channels.*"], + filters: { + id: "variant_123", + }, +}) + +const availability = await getVariantAvailability(query, { + variant_ids: ["variant_123"], + sales_channel_id: variants[0].product!.sales_channels![0]!.id, +}) +``` + +In this example, you retrieve the sales channels of the variant's product using [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). + +You pass the ID of the variant as a filter, and you specify `product.sales_channels.*` as the fields to retrieve. This retrieves the sales channels linked to the variant's product. + +Then, you pass the first sales channel ID to the `getVariantAvailability` function to retrieve the inventory availability of the product variant in that sales channel. + + # Calculate Product Variant Price with Taxes In this document, you'll learn how to calculate a product variant's price with taxes. @@ -30115,780 +30135,760 @@ For each product variant, you: - `priceWithoutTax`: The variant's price without taxes applied. -# Get Product Variant Inventory Quantity +# Stripe Module Provider -In this guide, you'll learn how to retrieve the available inventory quantity of a product variant in your Medusa application customizations. That includes API routes, workflows, subscribers, scheduled jobs, and any resource that can access the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md). +In this document, you’ll learn about the Stripe Module Provider and how to configure it in the Payment Module. -Refer to the [Retrieve Product Variant Inventory](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/products/inventory/index.html.md) storefront guide. +Your technical team must install the Stripe Module Provider in your Medusa application first. Then, refer to [this user guide](https://docs.medusajs.com/user-guide/settings/regions#edit-region-details/index.html.md) to learn how to enable the Stripe payment provider in a region using the Medusa Admin dashboard. -## Understanding Product Variant Inventory Availability +## Register the Stripe Module Provider -Product variants have a `manage_inventory` boolean field that indicates whether the Medusa application manages the inventory of the product variant. +### Prerequisites -When `manage_inventory` is disabled, the Medusa application always considers the product variant to be in stock. So, you can't retrieve the inventory quantity for those products. +- [Stripe account](https://stripe.com/) +- [Stripe Secret API Key](https://support.stripe.com/questions/locate-api-keys-in-the-dashboard) +- [For deployed Medusa applications, a Stripe webhook secret. Refer to the end of this guide for details on the URL and events.](https://docs.stripe.com/webhooks#add-a-webhook-endpoint) -When `manage_inventory` is enabled, the Medusa application tracks the inventory of the product variant using the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md). For example, when a customer purchases a product variant, the Medusa application decrements the stocked quantity of the product variant. +The Stripe Module Provider is installed by default in your application. To use it, add it to the array of providers passed to the Payment Module in `medusa-config.ts`: -This guide explains how to retrieve the inventory quantity of a product variant when `manage_inventory` is enabled. - -*** - -## Retrieve Product Variant Inventory - -To retrieve the inventory quantity of a product variant, use the `getVariantAvailability` utility function imported from `@medusajs/framework/utils`. It returns the available quantity of the product variant. - -For example: - -```ts highlights={variantAvailabilityHighlights} -import { getVariantAvailability } from "@medusajs/framework/utils" +```ts title="medusa-config.ts" +import { Modules } from "@medusajs/framework/utils" // ... -// use req.scope instead of container in API routes -const query = container.resolve("query") - -const availability = await getVariantAvailability(query, { - variant_ids: ["variant_123"], - sales_channel_id: "sc_123", +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/medusa/payment", + options: { + providers: [ + { + resolve: "@medusajs/medusa/payment-stripe", + id: "stripe", + options: { + apiKey: process.env.STRIPE_API_KEY, + }, + }, + ], + }, + }, + ], }) ``` -A product variant's inventory quantity is set per [stock location](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/index.html.md). This stock location is linked to a [sales channel](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md). +### Environment Variables -So, to retrieve the inventory quantity of a product variant using `getVariantAvailability`, you need to also provide the ID of the sales channel to retrieve the inventory quantity in. +Make sure to add the necessary environment variables for the above options in `.env`: -Refer to the [Retrieve Sales Channel to Use](#retrieve-sales-channel-to-use) section to learn how to retrieve the sales channel ID to use in the `getVariantAvailability` function. - -### Parameters - -The `getVariantAvailability` function accepts the following parameters: - -- query: (Query) Instance of Query to retrieve the necessary data. -- options: (\`object\`) The options to retrieve the variant availability. - - - variant\_ids: (\`string\[]\`) The IDs of the product variants to retrieve their inventory availability. - - - sales\_channel\_id: (\`string\`) The ID of the sales channel to retrieve the variant availability in. - -### Returns - -The `getVariantAvailability` function resolves to an object whose keys are the IDs of each product variant passed in the `variant_ids` parameter. - -The value of each key is an object with the following properties: - -- availability: (\`number\`) The available quantity of the product variant in the stock location linked to the sales channel. If \`manage\_inventory\` is disabled, this value is \`0\`. -- sales\_channel\_id: (\`string\`) The ID of the sales channel that the availability is scoped to. - -For example, the object may look like this: - -```json title="Example result" -{ - "variant_123": { - "availability": 10, - "sales_channel_id": "sc_123" - } -} +```bash +STRIPE_API_KEY= ``` +### Module Options + +|Option|Description|Required|Default| +|---|---|---|---|---|---|---| +|\`apiKey\`|A string indicating the Stripe Secret API key.|Yes|-| +|\`webhookSecret\`|A string indicating the Stripe webhook secret. This is only useful for deployed Medusa applications.|Yes|-| +|\`capture\`|Whether to automatically capture payment after authorization.|No|\`false\`| +|\`automatic\_payment\_methods\`|A boolean value indicating whether to enable Stripe's automatic payment methods. This is useful if you integrate services like Apple pay or Google pay.|No|\`false\`| +|\`payment\_description\`|A string used as the default description of a payment if none is available in cart.context.payment\_description.|No|-| + *** -## Retrieve Sales Channel to Use +## Enable Stripe Providers in a Region -To retrieve the sales channel ID to use in the `getVariantAvailability` function, you can either: +Before customers can use Stripe to complete their purchases, you must enable the Stripe payment provider(s) in the region where you want to offer this payment method. -- Use the sales channel of the request's scope. -- Use the sales channel that the variant's product is available in. +Refer to the [user guide](https://docs.medusajs.com/user-guide/settings/regions#edit-region-details/index.html.md) to learn how to edit a region and enable the Stripe payment provider. -### Method 1: Use Sales Channel Scope in Store Routes +*** -Requests sent to API routes starting with `/store` must include a [publishable API key in the request header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/publishable-api-keys/index.html.md). This scopes the request to one or more sales channels associated with the publishable API key. +## Stripe Payment Provider IDs -So, if you're retrieving the variant inventory availability in an API route starting with `/store`, you can access the sales channel using the `publishable_key_context.sales_channel_ids` property of the request object: +When you register the Stripe Module Provider, it registers different providers, such as basic Stripe payment, Bancontact, and more. -```ts highlights={salesChannelScopeHighlights} -import { MedusaStoreRequest, MedusaResponse } from "@medusajs/framework/http" -import { getVariantAvailability } from "@medusajs/framework/utils" +Each provider is registered and referenced by a unique ID made up of the format `pp_{identifier}_{id}`, where: -export async function GET( - req: MedusaStoreRequest, - res: MedusaResponse -) { - const query = req.scope.resolve("query") - const sales_channel_ids = req.publishable_key_context.sales_channel_ids +- `{identifier}` is the ID of the payment provider as defined in the Stripe Module Provider. +- `{id}` is the ID of the Stripe Module Provider as set in the `medusa-config.ts` file. For example, `stripe`. - const availability = await getVariantAvailability(query, { - variant_ids: ["variant_123"], - sales_channel_id: sales_channel_ids[0], - }) +Assuming you set the ID of the Stripe Module Provider to `stripe` in `medusa-config.ts`, the Medusa application will register the following payment providers: - res.json({ - availability, - }) -} -``` +|Provider Name|Provider ID| +|---|---|---| +|Basic Stripe Payment|\`pp\_stripe\_stripe\`| +|Bancontact Payments|\`pp\_stripe-bancontact\_stripe\`| +|BLIK Payments|\`pp\_stripe-blik\_stripe\`| +|giropay Payments|\`pp\_stripe-giropay\_stripe\`| +|iDEAL Payments|\`pp\_stripe-ideal\_stripe\`| +|Przelewy24 Payments|\`pp\_stripe-przelewy24\_stripe\`| +|PromptPay Payments|\`pp\_stripe-promptpay\_stripe\`| -In this example, you retrieve the scope's sales channel IDs using `req.publishable_key_context.sales_channel_ids`, whose value is an array of IDs. +*** -Then, you pass the first sales channel ID to the `getVariantAvailability` function to retrieve the inventory availability of the product variant in that sales channel. +## Setup Stripe Webhooks -Notice that the request object's type is `MedusaStoreRequest` instead of `MedusaRequest` to ensure the availability of the `publishable_key_context` property. +For production applications, you must set up webhooks in Stripe that inform Medusa of changes and updates to payments. Refer to [Stripe's documentation](https://docs.stripe.com/webhooks#add-a-webhook-endpoint) on how to setup webhooks. -### Method 2: Use Product's Sales Channel +### Webhook URL -A product is linked to the sales channels it's available in. So, you can retrieve the details of the variant's product, including its sales channels. +Medusa has a `{server_url}/hooks/payment/{provider_id}` API route that you can use to register webhooks in Stripe, where: -For example: +- `{server_url}` is the URL to your deployed Medusa application in server mode. +- `{provider_id}` is the ID of the provider as explained in the [Stripe Payment Provider IDs](#stripe-payment-provider-ids) section, without the `pp_` prefix. -```ts highlights={productSalesChannelHighlights} -import { getVariantAvailability } from "@medusajs/framework/utils" +The Stripe Module Provider supports the following payment types, and the webhook endpoint URL is different for each: -// ... +|Stripe Payment Type|Webhook Endpoint URL| +|---|---|---| +|Basic Stripe Payment|\`\{server\_url}/hooks/payment/stripe\_stripe\`| +|Bancontact Payments|\`\{server\_url}/hooks/payment/stripe-bancontact\_stripe\`| +|BLIK Payments|\`\{server\_url}/hooks/payment/stripe-blik\_stripe\`| +|giropay Payments|\`\{server\_url}/hooks/payment/stripe-giropay\_stripe\`| +|iDEAL Payments|\`\{server\_url}/hooks/payment/stripe-ideal\_stripe\`| +|Przelewy24 Payments|\`\{server\_url}/hooks/payment/stripe-przelewy24\_stripe\`| +|PromptPay Payments|\`\{server\_url}/hooks/payment/stripe-promptpay\_stripe\`| -// use req.scope instead of container in API routes -const query = container.resolve("query") +### Webhook Events -const { data: variants } = await query.graph({ - entity: "variant", - fields: ["id", "product.sales_channels.*"], - filters: { - id: "variant_123", - }, -}) +When you set up the webhook in Stripe, choose the following events to listen to: -const availability = await getVariantAvailability(query, { - variant_ids: ["variant_123"], - sales_channel_id: variants[0].product!.sales_channels![0]!.id, -}) -``` +- `payment_intent.amount_capturable_updated` +- `payment_intent.succeeded` +- `payment_intent.payment_failed` -In this example, you retrieve the sales channels of the variant's product using [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). +*** -You pass the ID of the variant as a filter, and you specify `product.sales_channels.*` as the fields to retrieve. This retrieves the sales channels linked to the variant's product. +## Useful Guides -Then, you pass the first sales channel ID to the `getVariantAvailability` function to retrieve the inventory availability of the product variant in that sales channel. +- [Storefront guide: Add Stripe payment method during checkout](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/checkout/payment/stripe/index.html.md). +- [Integrate in Next.js Starter](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter#stripe-integration/index.html.md). +- [Customize Stripe Integration in Next.js Starter](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/guides/customize-stripe/index.html.md). ## Workflows - [deleteApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteApiKeysWorkflow/index.html.md) +- [linkSalesChannelsToApiKeyWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToApiKeyWorkflow/index.html.md) - [updateApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateApiKeysWorkflow/index.html.md) - [revokeApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/revokeApiKeysWorkflow/index.html.md) - [createApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/createApiKeysWorkflow/index.html.md) -- [linkSalesChannelsToApiKeyWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToApiKeyWorkflow/index.html.md) -- [createCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAddressesWorkflow/index.html.md) -- [createCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAccountWorkflow/index.html.md) -- [deleteCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerAddressesWorkflow/index.html.md) -- [removeCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeCustomerAccountWorkflow/index.html.md) -- [createCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomersWorkflow/index.html.md) -- [updateCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerAddressesWorkflow/index.html.md) -- [deleteCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomersWorkflow/index.html.md) -- [updateCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomersWorkflow/index.html.md) -- [batchLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinksWorkflow/index.html.md) -- [createLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/createLinksWorkflow/index.html.md) -- [dismissLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissLinksWorkflow/index.html.md) -- [updateLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLinksWorkflow/index.html.md) - [generateResetPasswordTokenWorkflow](https://docs.medusajs.com/references/medusa-workflows/generateResetPasswordTokenWorkflow/index.html.md) - [completeCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/completeCartWorkflow/index.html.md) - [confirmVariantInventoryWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmVariantInventoryWorkflow/index.html.md) -- [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md) -- [addShippingMethodToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addShippingMethodToCartWorkflow/index.html.md) -- [createPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentCollectionForCartWorkflow/index.html.md) -- [createCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartWorkflow/index.html.md) -- [listShippingOptionsForCartWithPricingWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWithPricingWorkflow/index.html.md) -- [deleteCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCartCreditLinesWorkflow/index.html.md) - [createCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartCreditLinesWorkflow/index.html.md) +- [addShippingMethodToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addShippingMethodToCartWorkflow/index.html.md) +- [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md) +- [createCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartWorkflow/index.html.md) +- [createPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentCollectionForCartWorkflow/index.html.md) - [listShippingOptionsForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWorkflow/index.html.md) - [refreshCartItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshCartItemsWorkflow/index.html.md) +- [listShippingOptionsForCartWithPricingWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWithPricingWorkflow/index.html.md) +- [deleteCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCartCreditLinesWorkflow/index.html.md) - [refreshCartShippingMethodsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshCartShippingMethodsWorkflow/index.html.md) -- [transferCartCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/transferCartCustomerWorkflow/index.html.md) - [refreshPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshPaymentCollectionForCartWorkflow/index.html.md) +- [updateCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartWorkflow/index.html.md) - [updateCartPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartPromotionsWorkflow/index.html.md) - [updateLineItemInCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLineItemInCartWorkflow/index.html.md) - [updateTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxLinesWorkflow/index.html.md) -- [createDefaultsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createDefaultsWorkflow/index.html.md) -- [updateCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartWorkflow/index.html.md) +- [transferCartCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/transferCartCustomerWorkflow/index.html.md) - [validateExistingPaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/validateExistingPaymentCollectionStep/index.html.md) -- [deleteFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFilesWorkflow/index.html.md) +- [createDefaultsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createDefaultsWorkflow/index.html.md) +- [createCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAccountWorkflow/index.html.md) +- [createCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomersWorkflow/index.html.md) +- [deleteCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomersWorkflow/index.html.md) +- [deleteCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerAddressesWorkflow/index.html.md) +- [removeCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeCustomerAccountWorkflow/index.html.md) +- [createCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAddressesWorkflow/index.html.md) +- [updateCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomersWorkflow/index.html.md) +- [updateCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerAddressesWorkflow/index.html.md) +- [createCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerGroupsWorkflow/index.html.md) +- [deleteCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerGroupsWorkflow/index.html.md) +- [linkCustomersToCustomerGroupWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomersToCustomerGroupWorkflow/index.html.md) +- [updateCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerGroupsWorkflow/index.html.md) - [uploadFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/uploadFilesWorkflow/index.html.md) -- [batchShippingOptionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchShippingOptionRulesWorkflow/index.html.md) -- [calculateShippingOptionsPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/calculateShippingOptionsPricesWorkflow/index.html.md) -- [cancelFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelFulfillmentWorkflow/index.html.md) -- [createReturnFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnFulfillmentWorkflow/index.html.md) -- [createFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentWorkflow/index.html.md) -- [createServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createServiceZonesWorkflow/index.html.md) -- [createShipmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShipmentWorkflow/index.html.md) -- [createShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingProfilesWorkflow/index.html.md) -- [deleteFulfillmentSetsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFulfillmentSetsWorkflow/index.html.md) -- [createShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingOptionsWorkflow/index.html.md) -- [deleteServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteServiceZonesWorkflow/index.html.md) -- [updateFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateFulfillmentWorkflow/index.html.md) -- [markFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markFulfillmentAsDeliveredWorkflow/index.html.md) -- [deleteShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingOptionsWorkflow/index.html.md) -- [updateServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateServiceZonesWorkflow/index.html.md) -- [updateShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateShippingOptionsWorkflow/index.html.md) -- [validateFulfillmentDeliverabilityStep](https://docs.medusajs.com/references/medusa-workflows/validateFulfillmentDeliverabilityStep/index.html.md) -- [updateShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateShippingProfilesWorkflow/index.html.md) +- [deleteFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFilesWorkflow/index.html.md) +- [linkCustomerGroupsToCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomerGroupsToCustomerWorkflow/index.html.md) +- [addDraftOrderPromotionWorkflow](https://docs.medusajs.com/references/medusa-workflows/addDraftOrderPromotionWorkflow/index.html.md) - [addDraftOrderItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addDraftOrderItemsWorkflow/index.html.md) - [addDraftOrderShippingMethodsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addDraftOrderShippingMethodsWorkflow/index.html.md) -- [addDraftOrderPromotionWorkflow](https://docs.medusajs.com/references/medusa-workflows/addDraftOrderPromotionWorkflow/index.html.md) - [beginDraftOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginDraftOrderEditWorkflow/index.html.md) - [cancelDraftOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelDraftOrderEditWorkflow/index.html.md) -- [confirmDraftOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmDraftOrderEditWorkflow/index.html.md) - [convertDraftOrderStep](https://docs.medusajs.com/references/medusa-workflows/convertDraftOrderStep/index.html.md) -- [convertDraftOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/convertDraftOrderWorkflow/index.html.md) +- [confirmDraftOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmDraftOrderEditWorkflow/index.html.md) - [removeDraftOrderActionItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeDraftOrderActionItemWorkflow/index.html.md) -- [removeDraftOrderShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeDraftOrderShippingMethodWorkflow/index.html.md) +- [convertDraftOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/convertDraftOrderWorkflow/index.html.md) - [removeDraftOrderActionShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeDraftOrderActionShippingMethodWorkflow/index.html.md) - [removeDraftOrderPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeDraftOrderPromotionsWorkflow/index.html.md) +- [removeDraftOrderShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeDraftOrderShippingMethodWorkflow/index.html.md) - [requestDraftOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestDraftOrderEditWorkflow/index.html.md) -- [updateDraftOrderActionItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderActionItemWorkflow/index.html.md) - [updateDraftOrderItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderItemWorkflow/index.html.md) - [updateDraftOrderActionShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderActionShippingMethodWorkflow/index.html.md) +- [updateDraftOrderActionItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderActionItemWorkflow/index.html.md) - [updateDraftOrderShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderShippingMethodWorkflow/index.html.md) - [updateDraftOrderStep](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderStep/index.html.md) - [updateDraftOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateDraftOrderWorkflow/index.html.md) +- [batchShippingOptionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchShippingOptionRulesWorkflow/index.html.md) +- [cancelFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelFulfillmentWorkflow/index.html.md) +- [calculateShippingOptionsPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/calculateShippingOptionsPricesWorkflow/index.html.md) +- [createFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentWorkflow/index.html.md) +- [createReturnFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnFulfillmentWorkflow/index.html.md) +- [createServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createServiceZonesWorkflow/index.html.md) +- [createShipmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShipmentWorkflow/index.html.md) +- [createShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingOptionsWorkflow/index.html.md) +- [createShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingProfilesWorkflow/index.html.md) +- [deleteServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteServiceZonesWorkflow/index.html.md) +- [deleteFulfillmentSetsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFulfillmentSetsWorkflow/index.html.md) +- [deleteShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingOptionsWorkflow/index.html.md) +- [markFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markFulfillmentAsDeliveredWorkflow/index.html.md) +- [updateServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateServiceZonesWorkflow/index.html.md) +- [updateFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateFulfillmentWorkflow/index.html.md) +- [updateShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateShippingOptionsWorkflow/index.html.md) +- [updateShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateShippingProfilesWorkflow/index.html.md) +- [validateFulfillmentDeliverabilityStep](https://docs.medusajs.com/references/medusa-workflows/validateFulfillmentDeliverabilityStep/index.html.md) - [batchInventoryItemLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchInventoryItemLevelsWorkflow/index.html.md) -- [bulkCreateDeleteLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/bulkCreateDeleteLevelsWorkflow/index.html.md) - [createInventoryItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInventoryItemsWorkflow/index.html.md) -- [deleteInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryLevelsWorkflow/index.html.md) +- [bulkCreateDeleteLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/bulkCreateDeleteLevelsWorkflow/index.html.md) - [deleteInventoryItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryItemWorkflow/index.html.md) - [createInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInventoryLevelsWorkflow/index.html.md) +- [deleteInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryLevelsWorkflow/index.html.md) +- [validateInventoryLevelsDelete](https://docs.medusajs.com/references/medusa-workflows/validateInventoryLevelsDelete/index.html.md) - [updateInventoryItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateInventoryItemsWorkflow/index.html.md) - [updateInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateInventoryLevelsWorkflow/index.html.md) -- [validateInventoryLevelsDelete](https://docs.medusajs.com/references/medusa-workflows/validateInventoryLevelsDelete/index.html.md) -- [deleteCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerGroupsWorkflow/index.html.md) -- [createCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerGroupsWorkflow/index.html.md) -- [updateCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerGroupsWorkflow/index.html.md) -- [linkCustomersToCustomerGroupWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomersToCustomerGroupWorkflow/index.html.md) -- [linkCustomerGroupsToCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomerGroupsToCustomerWorkflow/index.html.md) -- [deleteLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteLineItemsWorkflow/index.html.md) - [acceptInviteWorkflow](https://docs.medusajs.com/references/medusa-workflows/acceptInviteWorkflow/index.html.md) - [createInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInvitesWorkflow/index.html.md) - [deleteInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInvitesWorkflow/index.html.md) - [refreshInviteTokensWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshInviteTokensWorkflow/index.html.md) +- [dismissLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissLinksWorkflow/index.html.md) +- [batchLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinksWorkflow/index.html.md) +- [updateLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLinksWorkflow/index.html.md) +- [createLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/createLinksWorkflow/index.html.md) +- [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md) - [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/index.html.md) - [processPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/processPaymentWorkflow/index.html.md) -- [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md) -- [refundPaymentsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentsWorkflow/index.html.md) - [validateRefundStep](https://docs.medusajs.com/references/medusa-workflows/validateRefundStep/index.html.md) - [validatePaymentsRefundStep](https://docs.medusajs.com/references/medusa-workflows/validatePaymentsRefundStep/index.html.md) -- [createRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRefundReasonsWorkflow/index.html.md) -- [createPaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentSessionsWorkflow/index.html.md) -- [deleteRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRefundReasonsWorkflow/index.html.md) -- [deletePaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePaymentSessionsWorkflow/index.html.md) -- [updateRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRefundReasonsWorkflow/index.html.md) -- [createPricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPricePreferencesWorkflow/index.html.md) -- [deletePricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePricePreferencesWorkflow/index.html.md) -- [updatePricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePricePreferencesWorkflow/index.html.md) -- [createPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListPricesWorkflow/index.html.md) -- [createPriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListsWorkflow/index.html.md) -- [deletePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePriceListsWorkflow/index.html.md) -- [removePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/removePriceListPricesWorkflow/index.html.md) -- [batchPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPriceListPricesWorkflow/index.html.md) -- [updatePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListsWorkflow/index.html.md) -- [updatePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListPricesWorkflow/index.html.md) -- [addOrRemoveCampaignPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrRemoveCampaignPromotionsWorkflow/index.html.md) -- [createCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCampaignsWorkflow/index.html.md) -- [batchPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPromotionRulesWorkflow/index.html.md) -- [deleteCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCampaignsWorkflow/index.html.md) -- [createPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPromotionRulesWorkflow/index.html.md) -- [createPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPromotionsWorkflow/index.html.md) -- [deletePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionRulesWorkflow/index.html.md) -- [updateCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCampaignsWorkflow/index.html.md) -- [deletePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionsWorkflow/index.html.md) -- [updatePromotionsStatusWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsStatusWorkflow/index.html.md) -- [updatePromotionsValidationStep](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsValidationStep/index.html.md) -- [updatePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsWorkflow/index.html.md) -- [updatePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionRulesWorkflow/index.html.md) -- [updateRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRegionsWorkflow/index.html.md) -- [createRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRegionsWorkflow/index.html.md) -- [acceptOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferValidationStep/index.html.md) -- [deleteRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRegionsWorkflow/index.html.md) +- [refundPaymentsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentsWorkflow/index.html.md) - [acceptOrderTransferWorkflow](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferWorkflow/index.html.md) -- [addOrderLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrderLineItemsWorkflow/index.html.md) -- [beginClaimOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderWorkflow/index.html.md) -- [beginExchangeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginExchangeOrderWorkflow/index.html.md) -- [beginOrderEditOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditOrderWorkflow/index.html.md) -- [beginClaimOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderValidationStep/index.html.md) -- [beginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditValidationStep/index.html.md) -- [beginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderExchangeValidationStep/index.html.md) +- [acceptOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferValidationStep/index.html.md) - [archiveOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/archiveOrderWorkflow/index.html.md) +- [addOrderLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrderLineItemsWorkflow/index.html.md) +- [beginClaimOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderValidationStep/index.html.md) +- [beginClaimOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderWorkflow/index.html.md) +- [beginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditValidationStep/index.html.md) +- [beginOrderEditOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditOrderWorkflow/index.html.md) +- [beginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderExchangeValidationStep/index.html.md) +- [beginReceiveReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnWorkflow/index.html.md) +- [beginExchangeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginExchangeOrderWorkflow/index.html.md) - [beginReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnValidationStep/index.html.md) - [beginReturnOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderValidationStep/index.html.md) -- [beginReceiveReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnWorkflow/index.html.md) -- [beginReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderWorkflow/index.html.md) - [cancelBeginOrderClaimValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimValidationStep/index.html.md) -- [cancelBeginOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimWorkflow/index.html.md) - [cancelBeginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderEditValidationStep/index.html.md) -- [cancelBeginOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeWorkflow/index.html.md) -- [cancelBeginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeValidationStep/index.html.md) -- [cancelClaimValidateOrderStep](https://docs.medusajs.com/references/medusa-workflows/cancelClaimValidateOrderStep/index.html.md) +- [cancelBeginOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimWorkflow/index.html.md) +- [beginReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderWorkflow/index.html.md) - [cancelBeginOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderEditWorkflow/index.html.md) +- [cancelBeginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeValidationStep/index.html.md) +- [cancelBeginOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeWorkflow/index.html.md) - [cancelExchangeValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelExchangeValidateOrder/index.html.md) -- [cancelOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderChangeWorkflow/index.html.md) - [cancelOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderClaimWorkflow/index.html.md) -- [cancelOrderFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentValidateOrder/index.html.md) -- [cancelOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderTransferRequestWorkflow/index.html.md) -- [cancelOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentWorkflow/index.html.md) -- [cancelOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderWorkflow/index.html.md) -- [cancelReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelReceiveReturnValidationStep/index.html.md) +- [cancelOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderChangeWorkflow/index.html.md) - [cancelOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderExchangeWorkflow/index.html.md) +- [cancelOrderFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentValidateOrder/index.html.md) +- [cancelOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentWorkflow/index.html.md) +- [cancelOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderTransferRequestWorkflow/index.html.md) +- [cancelReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelReceiveReturnValidationStep/index.html.md) +- [cancelOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderWorkflow/index.html.md) - [cancelRequestReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelRequestReturnValidationStep/index.html.md) -- [cancelReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnRequestWorkflow/index.html.md) - [cancelReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnReceiveWorkflow/index.html.md) +- [cancelReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnRequestWorkflow/index.html.md) - [cancelReturnValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelReturnValidateOrder/index.html.md) - [cancelReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnWorkflow/index.html.md) - [cancelTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelTransferOrderRequestValidationStep/index.html.md) - [cancelValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelValidateOrder/index.html.md) - [completeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/completeOrderWorkflow/index.html.md) +- [confirmClaimRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmClaimRequestWorkflow/index.html.md) - [confirmClaimRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmClaimRequestValidationStep/index.html.md) - [confirmExchangeRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmExchangeRequestValidationStep/index.html.md) -- [confirmClaimRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmClaimRequestWorkflow/index.html.md) - [confirmExchangeRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmExchangeRequestWorkflow/index.html.md) -- [confirmReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmReceiveReturnValidationStep/index.html.md) - [confirmOrderEditRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmOrderEditRequestValidationStep/index.html.md) - [confirmOrderEditRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmOrderEditRequestWorkflow/index.html.md) - [confirmReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnReceiveWorkflow/index.html.md) -- [confirmReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnRequestWorkflow/index.html.md) +- [confirmReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmReceiveReturnValidationStep/index.html.md) - [confirmReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmReturnRequestValidationStep/index.html.md) -- [createClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodValidationStep/index.html.md) -- [createClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodWorkflow/index.html.md) +- [confirmReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnRequestWorkflow/index.html.md) - [createAndCompleteReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/createAndCompleteReturnOrderWorkflow/index.html.md) +- [createClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodWorkflow/index.html.md) +- [createClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodValidationStep/index.html.md) - [createCompleteReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/createCompleteReturnValidationStep/index.html.md) - [createExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createExchangeShippingMethodValidationStep/index.html.md) +- [createExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createExchangeShippingMethodWorkflow/index.html.md) - [createFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentValidateOrder/index.html.md) - [createOrUpdateOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrUpdateOrderPaymentCollectionWorkflow/index.html.md) -- [createExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createExchangeShippingMethodWorkflow/index.html.md) -- [createOrderCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderCreditLinesWorkflow/index.html.md) -- [createOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeWorkflow/index.html.md) - [createOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeActionsWorkflow/index.html.md) -- [createOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderPaymentCollectionWorkflow/index.html.md) +- [createOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeWorkflow/index.html.md) +- [createOrderCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderCreditLinesWorkflow/index.html.md) - [createOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodWorkflow/index.html.md) - [createOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodValidationStep/index.html.md) - [createOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderFulfillmentWorkflow/index.html.md) +- [createOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderPaymentCollectionWorkflow/index.html.md) - [createOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderWorkflow/index.html.md) - [createOrderShipmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderShipmentWorkflow/index.html.md) -- [createReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createReturnShippingMethodValidationStep/index.html.md) -- [createShipmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createShipmentValidateOrder/index.html.md) - [createOrdersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrdersWorkflow/index.html.md) +- [createReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createReturnShippingMethodValidationStep/index.html.md) - [createReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnShippingMethodWorkflow/index.html.md) -- [declineOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderTransferRequestWorkflow/index.html.md) -- [declineTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/declineTransferOrderRequestValidationStep/index.html.md) -- [deleteOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteOrderChangeActionsWorkflow/index.html.md) +- [createShipmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createShipmentValidateOrder/index.html.md) - [declineOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderChangeWorkflow/index.html.md) +- [declineOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderTransferRequestWorkflow/index.html.md) +- [deleteOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteOrderChangeActionsWorkflow/index.html.md) - [deleteOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteOrderChangeWorkflow/index.html.md) +- [declineTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/declineTransferOrderRequestValidationStep/index.html.md) +- [deleteOrderPaymentCollections](https://docs.medusajs.com/references/medusa-workflows/deleteOrderPaymentCollections/index.html.md) - [dismissItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestWorkflow/index.html.md) - [dismissItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestValidationStep/index.html.md) -- [deleteOrderPaymentCollections](https://docs.medusajs.com/references/medusa-workflows/deleteOrderPaymentCollections/index.html.md) - [exchangeAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/exchangeAddNewItemValidationStep/index.html.md) +- [cancelClaimValidateOrderStep](https://docs.medusajs.com/references/medusa-workflows/cancelClaimValidateOrderStep/index.html.md) - [exchangeRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/exchangeRequestItemReturnValidationStep/index.html.md) - [getOrderDetailWorkflow](https://docs.medusajs.com/references/medusa-workflows/getOrderDetailWorkflow/index.html.md) -- [markPaymentCollectionAsPaid](https://docs.medusajs.com/references/medusa-workflows/markPaymentCollectionAsPaid/index.html.md) -- [markOrderFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow/index.html.md) - [getOrdersListWorkflow](https://docs.medusajs.com/references/medusa-workflows/getOrdersListWorkflow/index.html.md) - [orderClaimAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimAddNewItemValidationStep/index.html.md) - [orderClaimAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimAddNewItemWorkflow/index.html.md) -- [orderClaimRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnValidationStep/index.html.md) -- [orderClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemWorkflow/index.html.md) -- [orderClaimRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnWorkflow/index.html.md) - [orderClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemValidationStep/index.html.md) -- [orderEditAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderEditAddNewItemWorkflow/index.html.md) +- [orderClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemWorkflow/index.html.md) +- [orderClaimRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnValidationStep/index.html.md) +- [orderClaimRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnWorkflow/index.html.md) +- [markPaymentCollectionAsPaid](https://docs.medusajs.com/references/medusa-workflows/markPaymentCollectionAsPaid/index.html.md) - [orderEditAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditAddNewItemValidationStep/index.html.md) -- [orderEditUpdateItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityValidationStep/index.html.md) +- [orderEditAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderEditAddNewItemWorkflow/index.html.md) - [orderEditUpdateItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityWorkflow/index.html.md) - [orderExchangeAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderExchangeAddNewItemWorkflow/index.html.md) -- [orderFulfillmentDeliverablilityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderFulfillmentDeliverablilityValidationStep/index.html.md) - [orderExchangeRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderExchangeRequestItemReturnWorkflow/index.html.md) +- [orderFulfillmentDeliverablilityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderFulfillmentDeliverablilityValidationStep/index.html.md) +- [markOrderFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow/index.html.md) - [receiveAndCompleteReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveAndCompleteReturnOrderWorkflow/index.html.md) - [receiveCompleteReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveCompleteReturnValidationStep/index.html.md) -- [receiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestWorkflow/index.html.md) +- [orderEditUpdateItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityValidationStep/index.html.md) - [receiveItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestValidationStep/index.html.md) -- [removeAddItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeAddItemClaimActionWorkflow/index.html.md) -- [removeClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodValidationStep/index.html.md) +- [receiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestWorkflow/index.html.md) - [removeClaimItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimItemActionValidationStep/index.html.md) -- [removeClaimAddItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimAddItemActionValidationStep/index.html.md) -- [removeClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodWorkflow/index.html.md) -- [removeExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodValidationStep/index.html.md) -- [removeExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodWorkflow/index.html.md) +- [removeClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodValidationStep/index.html.md) - [removeExchangeItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeItemActionValidationStep/index.html.md) +- [removeClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodWorkflow/index.html.md) +- [removeAddItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeAddItemClaimActionWorkflow/index.html.md) +- [removeExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodWorkflow/index.html.md) +- [removeClaimAddItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimAddItemActionValidationStep/index.html.md) +- [removeExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodValidationStep/index.html.md) - [removeItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemClaimActionWorkflow/index.html.md) -- [removeItemReceiveReturnActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionValidationStep/index.html.md) - [removeItemExchangeActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemExchangeActionWorkflow/index.html.md) -- [removeItemOrderEditActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemOrderEditActionWorkflow/index.html.md) - [removeItemReceiveReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionWorkflow/index.html.md) +- [removeItemOrderEditActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemOrderEditActionWorkflow/index.html.md) - [removeItemReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReturnActionWorkflow/index.html.md) +- [removeItemReceiveReturnActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionValidationStep/index.html.md) - [removeOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditShippingMethodValidationStep/index.html.md) -- [removeOrderEditItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditItemActionValidationStep/index.html.md) - [removeOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditShippingMethodWorkflow/index.html.md) +- [removeOrderEditItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditItemActionValidationStep/index.html.md) - [removeReturnItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeReturnItemActionValidationStep/index.html.md) -- [requestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnValidationStep/index.html.md) -- [removeReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeReturnShippingMethodValidationStep/index.html.md) - [removeReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeReturnShippingMethodWorkflow/index.html.md) +- [requestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnValidationStep/index.html.md) - [requestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnWorkflow/index.html.md) +- [removeReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeReturnShippingMethodValidationStep/index.html.md) - [requestOrderEditRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderEditRequestValidationStep/index.html.md) - [requestOrderTransferWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferWorkflow/index.html.md) -- [throwUnlessPaymentCollectionNotPaid](https://docs.medusajs.com/references/medusa-workflows/throwUnlessPaymentCollectionNotPaid/index.html.md) -- [requestOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferValidationStep/index.html.md) - [requestOrderEditRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestOrderEditRequestWorkflow/index.html.md) -- [updateClaimAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemValidationStep/index.html.md) +- [requestOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferValidationStep/index.html.md) +- [throwUnlessPaymentCollectionNotPaid](https://docs.medusajs.com/references/medusa-workflows/throwUnlessPaymentCollectionNotPaid/index.html.md) - [throwUnlessStatusIsNotPaid](https://docs.medusajs.com/references/medusa-workflows/throwUnlessStatusIsNotPaid/index.html.md) +- [updateClaimAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemValidationStep/index.html.md) - [updateClaimAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemWorkflow/index.html.md) - [updateClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemValidationStep/index.html.md) -- [updateClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemWorkflow/index.html.md) - [updateClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodValidationStep/index.html.md) -- [updateExchangeAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemValidationStep/index.html.md) - [updateClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodWorkflow/index.html.md) +- [updateExchangeAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemValidationStep/index.html.md) +- [updateClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemWorkflow/index.html.md) - [updateExchangeAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemWorkflow/index.html.md) +- [updateExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodWorkflow/index.html.md) - [updateExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodValidationStep/index.html.md) +- [updateOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangeActionsWorkflow/index.html.md) - [updateOrderChangesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangesWorkflow/index.html.md) - [updateOrderEditAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditAddItemValidationStep/index.html.md) -- [updateExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodWorkflow/index.html.md) -- [updateOrderEditAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditAddItemWorkflow/index.html.md) - [updateOrderEditItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityValidationStep/index.html.md) - [updateOrderEditItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityWorkflow/index.html.md) -- [updateOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodWorkflow/index.html.md) +- [updateOrderEditAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditAddItemWorkflow/index.html.md) - [updateOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodValidationStep/index.html.md) -- [updateOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderWorkflow/index.html.md) -- [updateOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderValidationStep/index.html.md) -- [updateOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangeActionsWorkflow/index.html.md) +- [updateOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodWorkflow/index.html.md) - [updateOrderTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderTaxLinesWorkflow/index.html.md) -- [updateReceiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReceiveItemReturnRequestWorkflow/index.html.md) +- [updateOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderValidationStep/index.html.md) +- [updateOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderWorkflow/index.html.md) - [updateReceiveItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReceiveItemReturnRequestValidationStep/index.html.md) +- [updateReceiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReceiveItemReturnRequestWorkflow/index.html.md) - [updateRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnValidationStep/index.html.md) -- [updateReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodWorkflow/index.html.md) +- [updateRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnWorkflow/index.html.md) - [updateReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReturnValidationStep/index.html.md) - [updateReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodValidationStep/index.html.md) +- [updateReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodWorkflow/index.html.md) - [updateReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnWorkflow/index.html.md) - [validateOrderCreditLinesStep](https://docs.medusajs.com/references/medusa-workflows/validateOrderCreditLinesStep/index.html.md) -- [updateRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnWorkflow/index.html.md) -- [createProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductCategoriesWorkflow/index.html.md) -- [updateProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductCategoriesWorkflow/index.html.md) -- [deleteProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductCategoriesWorkflow/index.html.md) -- [createReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReservationsWorkflow/index.html.md) -- [deleteReservationsByLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsByLineItemsWorkflow/index.html.md) -- [deleteReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsWorkflow/index.html.md) -- [updateReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReservationsWorkflow/index.html.md) +- [batchPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPriceListPricesWorkflow/index.html.md) +- [createPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListPricesWorkflow/index.html.md) +- [deletePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePriceListsWorkflow/index.html.md) +- [createPriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListsWorkflow/index.html.md) +- [updatePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListPricesWorkflow/index.html.md) +- [removePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/removePriceListPricesWorkflow/index.html.md) +- [updatePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListsWorkflow/index.html.md) +- [createPricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPricePreferencesWorkflow/index.html.md) +- [updatePricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePricePreferencesWorkflow/index.html.md) +- [deletePricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePricePreferencesWorkflow/index.html.md) - [batchLinkProductsToCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCollectionWorkflow/index.html.md) -- [batchProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductVariantsWorkflow/index.html.md) - [batchLinkProductsToCategoryWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCategoryWorkflow/index.html.md) +- [batchProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductVariantsWorkflow/index.html.md) - [batchProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductsWorkflow/index.html.md) -- [createProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductOptionsWorkflow/index.html.md) - [createCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCollectionsWorkflow/index.html.md) -- [createProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTypesWorkflow/index.html.md) -- [createProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductsWorkflow/index.html.md) +- [createProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductOptionsWorkflow/index.html.md) - [createProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTagsWorkflow/index.html.md) - [createProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductVariantsWorkflow/index.html.md) +- [createProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductsWorkflow/index.html.md) +- [createProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTypesWorkflow/index.html.md) - [deleteCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCollectionsWorkflow/index.html.md) -- [deleteProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductOptionsWorkflow/index.html.md) - [deleteProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTypesWorkflow/index.html.md) -- [deleteProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductVariantsWorkflow/index.html.md) -- [deleteProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductsWorkflow/index.html.md) +- [deleteProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductOptionsWorkflow/index.html.md) - [deleteProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTagsWorkflow/index.html.md) +- [deleteProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductVariantsWorkflow/index.html.md) - [exportProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/exportProductsWorkflow/index.html.md) -- [updateCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCollectionsWorkflow/index.html.md) +- [deleteProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductsWorkflow/index.html.md) - [importProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/importProductsWorkflow/index.html.md) +- [updateCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCollectionsWorkflow/index.html.md) - [updateProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductOptionsWorkflow/index.html.md) - [updateProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductTypesWorkflow/index.html.md) -- [updateProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductVariantsWorkflow/index.html.md) - [updateProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductTagsWorkflow/index.html.md) - [updateProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductsWorkflow/index.html.md) -- [validateProductInputStep](https://docs.medusajs.com/references/medusa-workflows/validateProductInputStep/index.html.md) +- [updateProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductVariantsWorkflow/index.html.md) - [upsertVariantPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/upsertVariantPricesWorkflow/index.html.md) +- [validateProductInputStep](https://docs.medusajs.com/references/medusa-workflows/validateProductInputStep/index.html.md) +- [createProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductCategoriesWorkflow/index.html.md) +- [deleteProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductCategoriesWorkflow/index.html.md) +- [updateProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductCategoriesWorkflow/index.html.md) +- [deleteLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteLineItemsWorkflow/index.html.md) +- [addOrRemoveCampaignPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrRemoveCampaignPromotionsWorkflow/index.html.md) +- [batchPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPromotionRulesWorkflow/index.html.md) +- [createPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPromotionRulesWorkflow/index.html.md) +- [createCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCampaignsWorkflow/index.html.md) +- [createPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPromotionsWorkflow/index.html.md) +- [deletePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionRulesWorkflow/index.html.md) +- [deleteCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCampaignsWorkflow/index.html.md) +- [updateCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCampaignsWorkflow/index.html.md) +- [deletePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionsWorkflow/index.html.md) +- [updatePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionRulesWorkflow/index.html.md) +- [updatePromotionsValidationStep](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsValidationStep/index.html.md) +- [updatePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsWorkflow/index.html.md) +- [updatePromotionsStatusWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsStatusWorkflow/index.html.md) +- [deleteRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRegionsWorkflow/index.html.md) +- [createRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRegionsWorkflow/index.html.md) +- [updateRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRegionsWorkflow/index.html.md) - [deleteReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReturnReasonsWorkflow/index.html.md) - [createReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnReasonsWorkflow/index.html.md) - [updateReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnReasonsWorkflow/index.html.md) +- [createReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReservationsWorkflow/index.html.md) +- [deleteReservationsByLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsByLineItemsWorkflow/index.html.md) +- [updateReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReservationsWorkflow/index.html.md) +- [deleteReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsWorkflow/index.html.md) +- [deleteSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteSalesChannelsWorkflow/index.html.md) +- [createSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createSalesChannelsWorkflow/index.html.md) +- [updateSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateSalesChannelsWorkflow/index.html.md) +- [linkProductsToSalesChannelWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkProductsToSalesChannelWorkflow/index.html.md) +- [createPaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentSessionsWorkflow/index.html.md) +- [createRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRefundReasonsWorkflow/index.html.md) +- [deleteRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRefundReasonsWorkflow/index.html.md) +- [deletePaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePaymentSessionsWorkflow/index.html.md) +- [updateRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRefundReasonsWorkflow/index.html.md) +- [validateStepShippingProfileDelete](https://docs.medusajs.com/references/medusa-workflows/validateStepShippingProfileDelete/index.html.md) +- [deleteShippingProfileWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingProfileWorkflow/index.html.md) - [createLocationFulfillmentSetWorkflow](https://docs.medusajs.com/references/medusa-workflows/createLocationFulfillmentSetWorkflow/index.html.md) -- [deleteStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteStockLocationsWorkflow/index.html.md) - [createStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createStockLocationsWorkflow/index.html.md) - [linkSalesChannelsToStockLocationWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToStockLocationWorkflow/index.html.md) +- [deleteStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteStockLocationsWorkflow/index.html.md) - [updateStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateStockLocationsWorkflow/index.html.md) +- [createTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRateRulesWorkflow/index.html.md) +- [createTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRatesWorkflow/index.html.md) +- [deleteTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRatesWorkflow/index.html.md) +- [createTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRegionsWorkflow/index.html.md) +- [deleteTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRegionsWorkflow/index.html.md) +- [deleteTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRateRulesWorkflow/index.html.md) +- [maybeListTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/maybeListTaxRateRuleIdsStep/index.html.md) +- [setTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/setTaxRateRulesWorkflow/index.html.md) +- [updateTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRatesWorkflow/index.html.md) +- [updateTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRegionsWorkflow/index.html.md) +- [createUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUsersWorkflow/index.html.md) +- [createUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUserAccountWorkflow/index.html.md) +- [updateUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateUsersWorkflow/index.html.md) +- [deleteUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteUsersWorkflow/index.html.md) +- [removeUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeUserAccountWorkflow/index.html.md) - [createStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/createStoresWorkflow/index.html.md) - [updateStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateStoresWorkflow/index.html.md) - [deleteStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteStoresWorkflow/index.html.md) -- [validateStepShippingProfileDelete](https://docs.medusajs.com/references/medusa-workflows/validateStepShippingProfileDelete/index.html.md) -- [deleteShippingProfileWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingProfileWorkflow/index.html.md) -- [deleteSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteSalesChannelsWorkflow/index.html.md) -- [createSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createSalesChannelsWorkflow/index.html.md) -- [linkProductsToSalesChannelWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkProductsToSalesChannelWorkflow/index.html.md) -- [updateSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateSalesChannelsWorkflow/index.html.md) -- [createTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRateRulesWorkflow/index.html.md) -- [createTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRatesWorkflow/index.html.md) -- [createTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRegionsWorkflow/index.html.md) -- [deleteTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRatesWorkflow/index.html.md) -- [deleteTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRateRulesWorkflow/index.html.md) -- [setTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/setTaxRateRulesWorkflow/index.html.md) -- [updateTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRatesWorkflow/index.html.md) -- [maybeListTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/maybeListTaxRateRuleIdsStep/index.html.md) -- [updateTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRegionsWorkflow/index.html.md) -- [deleteTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRegionsWorkflow/index.html.md) -- [createUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUserAccountWorkflow/index.html.md) -- [createUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUsersWorkflow/index.html.md) -- [updateUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateUsersWorkflow/index.html.md) -- [removeUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeUserAccountWorkflow/index.html.md) -- [deleteUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteUsersWorkflow/index.html.md) ## Steps -- [setAuthAppMetadataStep](https://docs.medusajs.com/references/medusa-workflows/steps/setAuthAppMetadataStep/index.html.md) - [createApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/createApiKeysStep/index.html.md) -- [deleteApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteApiKeysStep/index.html.md) - [linkSalesChannelsToApiKeyStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkSalesChannelsToApiKeyStep/index.html.md) +- [deleteApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteApiKeysStep/index.html.md) - [revokeApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/revokeApiKeysStep/index.html.md) - [updateApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateApiKeysStep/index.html.md) - [validateSalesChannelsExistStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateSalesChannelsExistStep/index.html.md) +- [addShippingMethodToCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/addShippingMethodToCartStep/index.html.md) +- [confirmInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/confirmInventoryStep/index.html.md) +- [setAuthAppMetadataStep](https://docs.medusajs.com/references/medusa-workflows/steps/setAuthAppMetadataStep/index.html.md) +- [createCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCartsStep/index.html.md) +- [createLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemAdjustmentsStep/index.html.md) +- [createPaymentCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentCollectionsStep/index.html.md) +- [createShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingMethodAdjustmentsStep/index.html.md) +- [createLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemsStep/index.html.md) +- [findOrCreateCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOrCreateCustomerStep/index.html.md) +- [findOneOrAnyRegionStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOneOrAnyRegionStep/index.html.md) +- [getActionsToComputeFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getActionsToComputeFromPromotionsStep/index.html.md) +- [findSalesChannelStep](https://docs.medusajs.com/references/medusa-workflows/steps/findSalesChannelStep/index.html.md) +- [getLineItemActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getLineItemActionsStep/index.html.md) +- [getPromotionCodesToApply](https://docs.medusajs.com/references/medusa-workflows/steps/getPromotionCodesToApply/index.html.md) +- [prepareAdjustmentsFromPromotionActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/prepareAdjustmentsFromPromotionActionsStep/index.html.md) +- [getVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantsStep/index.html.md) +- [getVariantPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantPriceSetsStep/index.html.md) +- [removeLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeLineItemAdjustmentsStep/index.html.md) +- [removeShippingMethodFromCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodFromCartStep/index.html.md) +- [reserveInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/reserveInventoryStep/index.html.md) +- [removeShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodAdjustmentsStep/index.html.md) +- [retrieveCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/retrieveCartStep/index.html.md) +- [setTaxLinesForItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/setTaxLinesForItemsStep/index.html.md) +- [updateCartPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartPromotionsStep/index.html.md) +- [updateCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartsStep/index.html.md) +- [updateLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateLineItemsStep/index.html.md) +- [updateShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingMethodsStep/index.html.md) +- [validateCartPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartPaymentsStep/index.html.md) +- [validateAndReturnShippingMethodsDataStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateAndReturnShippingMethodsDataStep/index.html.md) +- [validateCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartStep/index.html.md) +- [validateLineItemPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateLineItemPricesStep/index.html.md) +- [validateCartShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsStep/index.html.md) +- [validateCartShippingOptionsPriceStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsPriceStep/index.html.md) +- [validateVariantPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateVariantPricesStep/index.html.md) +- [validateShippingStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingStep/index.html.md) +- [createCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerAddressesStep/index.html.md) +- [createCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomersStep/index.html.md) +- [deleteCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerAddressesStep/index.html.md) +- [maybeUnsetDefaultBillingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultBillingAddressesStep/index.html.md) +- [deleteCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomersStep/index.html.md) +- [maybeUnsetDefaultShippingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultShippingAddressesStep/index.html.md) +- [validateCustomerAccountCreation](https://docs.medusajs.com/references/medusa-workflows/steps/validateCustomerAccountCreation/index.html.md) +- [updateCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomersStep/index.html.md) +- [updateCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerAddressesStep/index.html.md) +- [createDefaultStoreStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultStoreStep/index.html.md) +- [validateDraftOrderStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateDraftOrderStep/index.html.md) - [createCustomerGroupsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerGroupsStep/index.html.md) - [deleteCustomerGroupStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerGroupStep/index.html.md) - [linkCustomerGroupsToCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkCustomerGroupsToCustomerStep/index.html.md) - [updateCustomerGroupsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerGroupsStep/index.html.md) - [linkCustomersToCustomerGroupStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkCustomersToCustomerGroupStep/index.html.md) -- [createCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerAddressesStep/index.html.md) -- [deleteCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomersStep/index.html.md) -- [createCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomersStep/index.html.md) -- [maybeUnsetDefaultBillingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultBillingAddressesStep/index.html.md) -- [maybeUnsetDefaultShippingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultShippingAddressesStep/index.html.md) -- [updateCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerAddressesStep/index.html.md) -- [updateCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomersStep/index.html.md) -- [validateCustomerAccountCreation](https://docs.medusajs.com/references/medusa-workflows/steps/validateCustomerAccountCreation/index.html.md) -- [createDefaultStoreStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultStoreStep/index.html.md) -- [deleteCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerAddressesStep/index.html.md) -- [deleteFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFilesStep/index.html.md) -- [uploadFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/uploadFilesStep/index.html.md) -- [validateDraftOrderStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateDraftOrderStep/index.html.md) -- [addShippingMethodToCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/addShippingMethodToCartStep/index.html.md) -- [confirmInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/confirmInventoryStep/index.html.md) -- [createCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCartsStep/index.html.md) -- [createLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemsStep/index.html.md) -- [createLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemAdjustmentsStep/index.html.md) -- [createPaymentCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentCollectionsStep/index.html.md) -- [createShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingMethodAdjustmentsStep/index.html.md) -- [findOneOrAnyRegionStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOneOrAnyRegionStep/index.html.md) -- [findSalesChannelStep](https://docs.medusajs.com/references/medusa-workflows/steps/findSalesChannelStep/index.html.md) -- [getActionsToComputeFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getActionsToComputeFromPromotionsStep/index.html.md) -- [getLineItemActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getLineItemActionsStep/index.html.md) -- [getPromotionCodesToApply](https://docs.medusajs.com/references/medusa-workflows/steps/getPromotionCodesToApply/index.html.md) -- [getVariantPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantPriceSetsStep/index.html.md) -- [getVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantsStep/index.html.md) -- [prepareAdjustmentsFromPromotionActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/prepareAdjustmentsFromPromotionActionsStep/index.html.md) -- [removeLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeLineItemAdjustmentsStep/index.html.md) -- [removeShippingMethodFromCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodFromCartStep/index.html.md) -- [findOrCreateCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOrCreateCustomerStep/index.html.md) -- [removeShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodAdjustmentsStep/index.html.md) -- [setTaxLinesForItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/setTaxLinesForItemsStep/index.html.md) -- [retrieveCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/retrieveCartStep/index.html.md) -- [reserveInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/reserveInventoryStep/index.html.md) -- [updateLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateLineItemsStep/index.html.md) -- [updateCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartsStep/index.html.md) -- [updateShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingMethodsStep/index.html.md) -- [updateCartPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartPromotionsStep/index.html.md) -- [validateCartShippingOptionsPriceStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsPriceStep/index.html.md) -- [validateCartShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsStep/index.html.md) -- [validateCartPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartPaymentsStep/index.html.md) -- [validateAndReturnShippingMethodsDataStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateAndReturnShippingMethodsDataStep/index.html.md) -- [validateCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartStep/index.html.md) -- [validateLineItemPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateLineItemPricesStep/index.html.md) -- [validateShippingStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingStep/index.html.md) -- [validateVariantPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateVariantPricesStep/index.html.md) -- [createRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRemoteLinkStep/index.html.md) -- [dismissRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/dismissRemoteLinkStep/index.html.md) -- [emitEventStep](https://docs.medusajs.com/references/medusa-workflows/steps/emitEventStep/index.html.md) -- [useQueryGraphStep](https://docs.medusajs.com/references/medusa-workflows/steps/useQueryGraphStep/index.html.md) -- [updateRemoteLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRemoteLinksStep/index.html.md) -- [removeRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRemoteLinkStep/index.html.md) - [buildPriceSet](https://docs.medusajs.com/references/medusa-workflows/steps/buildPriceSet/index.html.md) -- [useRemoteQueryStep](https://docs.medusajs.com/references/medusa-workflows/steps/useRemoteQueryStep/index.html.md) - [calculateShippingOptionsPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/calculateShippingOptionsPricesStep/index.html.md) -- [validatePresenceOfStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePresenceOfStep/index.html.md) -- [cancelFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelFulfillmentStep/index.html.md) - [createFulfillmentSets](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentSets/index.html.md) +- [cancelFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelFulfillmentStep/index.html.md) +- [createFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentStep/index.html.md) - [createReturnFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnFulfillmentStep/index.html.md) - [createServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createServiceZonesStep/index.html.md) -- [createFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentStep/index.html.md) +- [createShippingOptionsPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionsPriceSetsStep/index.html.md) - [createShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingProfilesStep/index.html.md) - [createShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionRulesStep/index.html.md) -- [createShippingOptionsPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionsPriceSetsStep/index.html.md) - [deleteFulfillmentSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFulfillmentSetsStep/index.html.md) - [deleteServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteServiceZonesStep/index.html.md) - [deleteShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingOptionRulesStep/index.html.md) - [deleteShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingOptionsStep/index.html.md) +- [updateServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateServiceZonesStep/index.html.md) - [setShippingOptionsPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/setShippingOptionsPricesStep/index.html.md) - [updateFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateFulfillmentStep/index.html.md) - [updateShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingOptionRulesStep/index.html.md) -- [updateServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateServiceZonesStep/index.html.md) - [updateShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingProfilesStep/index.html.md) - [upsertShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/upsertShippingOptionsStep/index.html.md) -- [validateShippingOptionPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingOptionPricesStep/index.html.md) - [validateShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShipmentStep/index.html.md) +- [validateShippingOptionPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingOptionPricesStep/index.html.md) +- [createInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryItemsStep/index.html.md) - [adjustInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/adjustInventoryLevelsStep/index.html.md) - [attachInventoryItemToVariants](https://docs.medusajs.com/references/medusa-workflows/steps/attachInventoryItemToVariants/index.html.md) - [createInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryLevelsStep/index.html.md) -- [createInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryItemsStep/index.html.md) - [deleteInventoryItemStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryItemStep/index.html.md) - [deleteInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryLevelsStep/index.html.md) - [updateInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryLevelsStep/index.html.md) -- [validateInventoryDeleteStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryDeleteStep/index.html.md) - [updateInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryItemsStep/index.html.md) +- [validateInventoryDeleteStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryDeleteStep/index.html.md) - [validateInventoryItemsForCreate](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryItemsForCreate/index.html.md) - [validateInventoryLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryLocationsStep/index.html.md) +- [deleteFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFilesStep/index.html.md) +- [uploadFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/uploadFilesStep/index.html.md) +- [createInviteStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInviteStep/index.html.md) +- [deleteInvitesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInvitesStep/index.html.md) +- [refreshInviteTokensStep](https://docs.medusajs.com/references/medusa-workflows/steps/refreshInviteTokensStep/index.html.md) +- [validateTokenStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateTokenStep/index.html.md) - [deleteLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteLineItemsStep/index.html.md) - [updateLineItemsStepWithSelector](https://docs.medusajs.com/references/medusa-workflows/steps/updateLineItemsStepWithSelector/index.html.md) - [listLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listLineItemsStep/index.html.md) +- [notifyOnFailureStep](https://docs.medusajs.com/references/medusa-workflows/steps/notifyOnFailureStep/index.html.md) +- [sendNotificationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/sendNotificationsStep/index.html.md) - [addOrderTransactionStep](https://docs.medusajs.com/references/medusa-workflows/steps/addOrderTransactionStep/index.html.md) -- [cancelOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderChangeStep/index.html.md) - [archiveOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/archiveOrdersStep/index.html.md) +- [cancelOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderChangeStep/index.html.md) - [cancelOrderExchangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderExchangeStep/index.html.md) - [cancelOrderFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderFulfillmentStep/index.html.md) - [cancelOrderClaimStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderClaimStep/index.html.md) - [cancelOrderReturnStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderReturnStep/index.html.md) - [cancelOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrdersStep/index.html.md) -- [completeOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/completeOrdersStep/index.html.md) - [createOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderChangeStep/index.html.md) +- [completeOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/completeOrdersStep/index.html.md) +- [createCompleteReturnStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCompleteReturnStep/index.html.md) +- [createOrderExchangeItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangeItemsFromActionsStep/index.html.md) - [createOrderClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderClaimsStep/index.html.md) - [createOrderClaimItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderClaimItemsFromActionsStep/index.html.md) -- [createOrderExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangesStep/index.html.md) -- [createOrderExchangeItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangeItemsFromActionsStep/index.html.md) -- [createCompleteReturnStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCompleteReturnStep/index.html.md) - [createOrderLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderLineItemsStep/index.html.md) +- [createOrderExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangesStep/index.html.md) - [createReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnsStep/index.html.md) - [declineOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/declineOrderChangeStep/index.html.md) - [createOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrdersStep/index.html.md) +- [deleteOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangeActionsStep/index.html.md) +- [deleteExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteExchangesStep/index.html.md) - [deleteClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteClaimsStep/index.html.md) - [deleteOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangesStep/index.html.md) -- [deleteExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteExchangesStep/index.html.md) -- [deleteOrderLineItems](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderLineItems/index.html.md) -- [deleteOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangeActionsStep/index.html.md) -- [deleteReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnsStep/index.html.md) - [deleteOrderShippingMethods](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderShippingMethods/index.html.md) -- [previewOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/previewOrderChangeStep/index.html.md) -- [registerOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderChangesStep/index.html.md) -- [registerOrderFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderFulfillmentStep/index.html.md) +- [deleteOrderLineItems](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderLineItems/index.html.md) - [registerOrderDeliveryStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderDeliveryStep/index.html.md) +- [registerOrderFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderFulfillmentStep/index.html.md) +- [previewOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/previewOrderChangeStep/index.html.md) - [registerOrderShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderShipmentStep/index.html.md) - [setOrderTaxLinesForItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/setOrderTaxLinesForItemsStep/index.html.md) -- [updateOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangesStep/index.html.md) -- [updateOrderShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderShippingMethodsStep/index.html.md) +- [registerOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderChangesStep/index.html.md) - [updateOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangeActionsStep/index.html.md) -- [updateReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnsStep/index.html.md) +- [updateOrderShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderShippingMethodsStep/index.html.md) - [updateOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrdersStep/index.html.md) +- [updateOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangesStep/index.html.md) +- [deleteReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnsStep/index.html.md) +- [updateReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnsStep/index.html.md) - [updateReturnItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnItemsStep/index.html.md) -- [authorizePaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/authorizePaymentSessionStep/index.html.md) - [cancelPaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelPaymentStep/index.html.md) +- [authorizePaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/authorizePaymentSessionStep/index.html.md) - [capturePaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/capturePaymentStep/index.html.md) - [refundPaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/refundPaymentStep/index.html.md) - [refundPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/refundPaymentsStep/index.html.md) -- [sendNotificationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/sendNotificationsStep/index.html.md) -- [notifyOnFailureStep](https://docs.medusajs.com/references/medusa-workflows/steps/notifyOnFailureStep/index.html.md) -- [createInviteStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInviteStep/index.html.md) -- [validateTokenStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateTokenStep/index.html.md) -- [refreshInviteTokensStep](https://docs.medusajs.com/references/medusa-workflows/steps/refreshInviteTokensStep/index.html.md) -- [deleteInvitesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInvitesStep/index.html.md) - [createPricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPricePreferencesStep/index.html.md) - [createPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceSetsStep/index.html.md) +- [deletePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePricePreferencesStep/index.html.md) - [updatePricePreferencesAsArrayStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesAsArrayStep/index.html.md) - [updatePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesStep/index.html.md) - [updatePriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceSetsStep/index.html.md) -- [deletePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePricePreferencesStep/index.html.md) -- [createRefundReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRefundReasonStep/index.html.md) -- [createPaymentAccountHolderStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentAccountHolderStep/index.html.md) -- [createPaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentSessionStep/index.html.md) -- [deletePaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePaymentSessionsStep/index.html.md) -- [deleteRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRefundReasonsStep/index.html.md) -- [updatePaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePaymentCollectionStep/index.html.md) -- [updateRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRefundReasonsStep/index.html.md) - [createPriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceListPricesStep/index.html.md) - [createPriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceListsStep/index.html.md) -- [validateDeletedPaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateDeletedPaymentSessionsStep/index.html.md) - [deletePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePriceListsStep/index.html.md) - [getExistingPriceListsPriceIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getExistingPriceListsPriceIdsStep/index.html.md) - [removePriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/removePriceListPricesStep/index.html.md) -- [updatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceListsStep/index.html.md) -- [validatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePriceListsStep/index.html.md) -- [validateVariantPriceLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateVariantPriceLinksStep/index.html.md) -- [createProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductCategoriesStep/index.html.md) - [updatePriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceListPricesStep/index.html.md) -- [deleteProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductCategoriesStep/index.html.md) -- [updateProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductCategoriesStep/index.html.md) -- [createRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRegionsStep/index.html.md) -- [deleteRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRegionsStep/index.html.md) -- [setRegionsPaymentProvidersStep](https://docs.medusajs.com/references/medusa-workflows/steps/setRegionsPaymentProvidersStep/index.html.md) -- [updateRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRegionsStep/index.html.md) -- [deleteReservationsByLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsByLineItemsStep/index.html.md) -- [deleteReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsStep/index.html.md) -- [createReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReservationsStep/index.html.md) -- [updateReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReservationsStep/index.html.md) -- [addCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addCampaignPromotionsStep/index.html.md) -- [createPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPromotionsStep/index.html.md) -- [deleteCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCampaignsStep/index.html.md) -- [removeCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeCampaignPromotionsStep/index.html.md) -- [deletePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePromotionsStep/index.html.md) -- [updateCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCampaignsStep/index.html.md) -- [createCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCampaignsStep/index.html.md) -- [removeRulesFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRulesFromPromotionsStep/index.html.md) -- [addRulesToPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addRulesToPromotionsStep/index.html.md) -- [updatePromotionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionRulesStep/index.html.md) -- [createReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnReasonsStep/index.html.md) -- [updateReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnReasonsStep/index.html.md) -- [deleteReturnReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnReasonStep/index.html.md) -- [updatePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionsStep/index.html.md) +- [updatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceListsStep/index.html.md) +- [validateVariantPriceLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateVariantPriceLinksStep/index.html.md) +- [dismissRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/dismissRemoteLinkStep/index.html.md) +- [createRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRemoteLinkStep/index.html.md) +- [validatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePriceListsStep/index.html.md) +- [emitEventStep](https://docs.medusajs.com/references/medusa-workflows/steps/emitEventStep/index.html.md) +- [updateRemoteLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRemoteLinksStep/index.html.md) +- [removeRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRemoteLinkStep/index.html.md) +- [useQueryGraphStep](https://docs.medusajs.com/references/medusa-workflows/steps/useQueryGraphStep/index.html.md) +- [useRemoteQueryStep](https://docs.medusajs.com/references/medusa-workflows/steps/useRemoteQueryStep/index.html.md) +- [validatePresenceOfStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePresenceOfStep/index.html.md) +- [createPaymentAccountHolderStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentAccountHolderStep/index.html.md) +- [createPaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentSessionStep/index.html.md) +- [deletePaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePaymentSessionsStep/index.html.md) +- [updatePaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePaymentCollectionStep/index.html.md) +- [createRefundReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRefundReasonStep/index.html.md) +- [deleteRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRefundReasonsStep/index.html.md) +- [updateRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRefundReasonsStep/index.html.md) +- [validateDeletedPaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateDeletedPaymentSessionsStep/index.html.md) - [batchLinkProductsToCategoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/batchLinkProductsToCategoryStep/index.html.md) +- [batchLinkProductsToCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/batchLinkProductsToCollectionStep/index.html.md) - [createProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductOptionsStep/index.html.md) - [createCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCollectionsStep/index.html.md) -- [batchLinkProductsToCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/batchLinkProductsToCollectionStep/index.html.md) -- [createProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductVariantsStep/index.html.md) -- [createProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTypesStep/index.html.md) -- [createProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductsStep/index.html.md) -- [createVariantPricingLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createVariantPricingLinkStep/index.html.md) -- [deleteCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCollectionsStep/index.html.md) - [createProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTagsStep/index.html.md) +- [createProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTypesStep/index.html.md) +- [createVariantPricingLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createVariantPricingLinkStep/index.html.md) +- [createProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductsStep/index.html.md) +- [createProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductVariantsStep/index.html.md) +- [deleteCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCollectionsStep/index.html.md) +- [deleteProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductOptionsStep/index.html.md) - [deleteProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductTagsStep/index.html.md) - [deleteProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductTypesStep/index.html.md) -- [deleteProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductOptionsStep/index.html.md) +- [deleteProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductVariantsStep/index.html.md) - [deleteProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductsStep/index.html.md) +- [getAllProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getAllProductsStep/index.html.md) - [generateProductCsvStep](https://docs.medusajs.com/references/medusa-workflows/steps/generateProductCsvStep/index.html.md) - [getProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getProductsStep/index.html.md) +- [groupProductsForBatchStep](https://docs.medusajs.com/references/medusa-workflows/steps/groupProductsForBatchStep/index.html.md) +- [updateCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCollectionsStep/index.html.md) - [getVariantAvailabilityStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantAvailabilityStep/index.html.md) -- [getAllProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getAllProductsStep/index.html.md) - [parseProductCsvStep](https://docs.medusajs.com/references/medusa-workflows/steps/parseProductCsvStep/index.html.md) -- [deleteProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductVariantsStep/index.html.md) - [updateProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductOptionsStep/index.html.md) - [updateProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductTagsStep/index.html.md) -- [groupProductsForBatchStep](https://docs.medusajs.com/references/medusa-workflows/steps/groupProductsForBatchStep/index.html.md) -- [updateProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductVariantsStep/index.html.md) - [updateProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductTypesStep/index.html.md) -- [waitConfirmationProductImportStep](https://docs.medusajs.com/references/medusa-workflows/steps/waitConfirmationProductImportStep/index.html.md) -- [updateCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCollectionsStep/index.html.md) +- [updateProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductVariantsStep/index.html.md) - [updateProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductsStep/index.html.md) -- [listShippingOptionsForContextStep](https://docs.medusajs.com/references/medusa-workflows/steps/listShippingOptionsForContextStep/index.html.md) +- [waitConfirmationProductImportStep](https://docs.medusajs.com/references/medusa-workflows/steps/waitConfirmationProductImportStep/index.html.md) +- [createProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductCategoriesStep/index.html.md) +- [updateProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductCategoriesStep/index.html.md) +- [deleteProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductCategoriesStep/index.html.md) +- [addCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addCampaignPromotionsStep/index.html.md) +- [addRulesToPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addRulesToPromotionsStep/index.html.md) +- [createPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPromotionsStep/index.html.md) +- [deleteCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCampaignsStep/index.html.md) +- [deletePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePromotionsStep/index.html.md) +- [createCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCampaignsStep/index.html.md) +- [removeCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeCampaignPromotionsStep/index.html.md) +- [removeRulesFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRulesFromPromotionsStep/index.html.md) +- [updateCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCampaignsStep/index.html.md) +- [updatePromotionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionRulesStep/index.html.md) +- [updatePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionsStep/index.html.md) +- [createReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReservationsStep/index.html.md) +- [deleteReservationsByLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsByLineItemsStep/index.html.md) +- [deleteReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsStep/index.html.md) +- [updateReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReservationsStep/index.html.md) +- [createReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnReasonsStep/index.html.md) +- [deleteReturnReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnReasonStep/index.html.md) +- [updateReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnReasonsStep/index.html.md) - [associateLocationsWithSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/associateLocationsWithSalesChannelsStep/index.html.md) - [associateProductsWithSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/associateProductsWithSalesChannelsStep/index.html.md) -- [canDeleteSalesChannelsOrThrowStep](https://docs.medusajs.com/references/medusa-workflows/steps/canDeleteSalesChannelsOrThrowStep/index.html.md) - [createDefaultSalesChannelStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultSalesChannelStep/index.html.md) -- [deleteSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteSalesChannelsStep/index.html.md) +- [canDeleteSalesChannelsOrThrowStep](https://docs.medusajs.com/references/medusa-workflows/steps/canDeleteSalesChannelsOrThrowStep/index.html.md) - [createSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createSalesChannelsStep/index.html.md) - [detachLocationsFromSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/detachLocationsFromSalesChannelsStep/index.html.md) -- [detachProductsFromSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/detachProductsFromSalesChannelsStep/index.html.md) +- [deleteSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteSalesChannelsStep/index.html.md) - [updateSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateSalesChannelsStep/index.html.md) +- [detachProductsFromSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/detachProductsFromSalesChannelsStep/index.html.md) +- [listShippingOptionsForContextStep](https://docs.medusajs.com/references/medusa-workflows/steps/listShippingOptionsForContextStep/index.html.md) - [deleteShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingProfilesStep/index.html.md) -- [updateStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStoresStep/index.html.md) -- [deleteStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStoresStep/index.html.md) - [createStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/createStoresStep/index.html.md) -- [createTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRateRulesStep/index.html.md) -- [createTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRatesStep/index.html.md) -- [deleteTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRateRulesStep/index.html.md) +- [deleteStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStoresStep/index.html.md) +- [updateStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStoresStep/index.html.md) - [createTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRegionsStep/index.html.md) -- [deleteTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRatesStep/index.html.md) +- [createTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRatesStep/index.html.md) +- [createTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRateRulesStep/index.html.md) +- [deleteTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRateRulesStep/index.html.md) - [getItemTaxLinesStep](https://docs.medusajs.com/references/medusa-workflows/steps/getItemTaxLinesStep/index.html.md) - [deleteTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRegionsStep/index.html.md) -- [updateTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRatesStep/index.html.md) -- [listTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateRuleIdsStep/index.html.md) - [listTaxRateIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateIdsStep/index.html.md) +- [deleteTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRatesStep/index.html.md) +- [listTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateRuleIdsStep/index.html.md) +- [updateTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRatesStep/index.html.md) - [updateTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRegionsStep/index.html.md) -- [createUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createUsersStep/index.html.md) -- [deleteUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteUsersStep/index.html.md) -- [updateUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateUsersStep/index.html.md) -- [deleteStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStockLocationsStep/index.html.md) -- [updateStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStockLocationsStep/index.html.md) - [createStockLocations](https://docs.medusajs.com/references/medusa-workflows/steps/createStockLocations/index.html.md) +- [updateStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStockLocationsStep/index.html.md) +- [deleteStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStockLocationsStep/index.html.md) +- [deleteUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteUsersStep/index.html.md) +- [createUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createUsersStep/index.html.md) +- [updateUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateUsersStep/index.html.md) +- [createRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRegionsStep/index.html.md) +- [deleteRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRegionsStep/index.html.md) +- [updateRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRegionsStep/index.html.md) +- [setRegionsPaymentProvidersStep](https://docs.medusajs.com/references/medusa-workflows/steps/setRegionsPaymentProvidersStep/index.html.md) # Medusa CLI Reference @@ -30914,6 +30914,22 @@ npx medusa --help *** +# exec Command - Medusa CLI Reference + +Run a custom CLI script. Learn more about it in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/custom-cli-scripts/index.html.md). + +```bash +npx medusa exec [file] [args...] +``` + +## Arguments + +|Argument|Description|Required| +|---|---|---|---|---| +|\`file\`|The path to the TypeScript or JavaScript file holding the function to execute.|Yes| +|\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No| + + # db Commands - Medusa CLI Reference Commands starting with `db:` perform actions on the database. @@ -31096,22 +31112,6 @@ By default, the Medusa Admin is built to the `.medusa/server/public/admin` direc If you want a separate build to host the admin standalone, such as on Vercel, pass the `--admin-only` option as explained in the [Options](#options) section. This outputs the admin to the `.medusa/admin` directory instead. -# develop Command - Medusa CLI Reference - -Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application. - -```bash -npx medusa develop -``` - -## Options - -|Option|Description|Default| -|---|---|---|---|---| -|\`-H \\`|Set host of the Medusa server.|\`localhost\`| -|\`-p \\`|Set port of the Medusa server.|\`9000\`| - - # new Command - Medusa CLI Reference Create a new Medusa application. Unlike the `create-medusa-app` CLI tool, this command provides more flexibility for experienced Medusa developers in creating and configuring their project. @@ -31202,20 +31202,37 @@ npx medusa plugin:build ``` -# exec Command - Medusa CLI Reference +# develop Command - Medusa CLI Reference -Run a custom CLI script. Learn more about it in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/custom-cli-scripts/index.html.md). +Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application. ```bash -npx medusa exec [file] [args...] +npx medusa develop ``` -## Arguments +## Options -|Argument|Description|Required| +|Option|Description|Default| |---|---|---|---|---| -|\`file\`|The path to the TypeScript or JavaScript file holding the function to execute.|Yes| -|\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No| +|\`-H \\`|Set host of the Medusa server.|\`localhost\`| +|\`-p \\`|Set port of the Medusa server.|\`9000\`| + + +# start Command - Medusa CLI Reference + +Start the Medusa application in production. + +```bash +npx medusa start +``` + +## Options + +|Option|Description|Default| +|---|---|---|---|---| +|\`-H \\`|Set host of the Medusa server.|\`localhost\`| +|\`-p \\`|Set port of the Medusa server.|\`9000\`| +|\`--cluster \\`|Start Medusa's Node.js server in |Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.| # user Command - Medusa CLI Reference @@ -31253,23 +31270,6 @@ npx medusa telemetry |\`--disable\`|Disable telemetry.| -# start Command - Medusa CLI Reference - -Start the Medusa application in production. - -```bash -npx medusa start -``` - -## Options - -|Option|Description|Default| -|---|---|---|---|---| -|\`-H \\`|Set host of the Medusa server.|\`localhost\`| -|\`-p \\`|Set port of the Medusa server.|\`9000\`| -|\`--cluster \\`|Start Medusa's Node.js server in |Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.| - - # Medusa CLI Reference The Medusa CLI tool provides commands that facilitate your development. @@ -31355,6 +31355,22 @@ By default, the Medusa Admin is built to the `.medusa/server/public/admin` direc If you want a separate build to host the admin standalone, such as on Vercel, pass the `--admin-only` option as explained in the [Options](#options) section. This outputs the admin to the `.medusa/admin` directory instead. +# exec Command - Medusa CLI Reference + +Run a custom CLI script. Learn more about it in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/custom-cli-scripts/index.html.md). + +```bash +npx medusa exec [file] [args...] +``` + +## Arguments + +|Argument|Description|Required| +|---|---|---|---|---| +|\`file\`|The path to the TypeScript or JavaScript file holding the function to execute.|Yes| +|\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No| + + # db Commands - Medusa CLI Reference Commands starting with `db:` perform actions on the database. @@ -31475,22 +31491,6 @@ npx medusa db:sync-links |\`--execute-all\`|Skip prompts when syncing links and execute all (including unsafe) actions.|No|Prompts are shown for unsafe actions, by default.| -# exec Command - Medusa CLI Reference - -Run a custom CLI script. Learn more about it in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/custom-cli-scripts/index.html.md). - -```bash -npx medusa exec [file] [args...] -``` - -## Arguments - -|Argument|Description|Required| -|---|---|---|---|---| -|\`file\`|The path to the TypeScript or JavaScript file holding the function to execute.|Yes| -|\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No| - - # develop Command - Medusa CLI Reference Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application. @@ -31597,23 +31597,6 @@ npx medusa plugin:build ``` -# start Command - Medusa CLI Reference - -Start the Medusa application in production. - -```bash -npx medusa start -``` - -## Options - -|Option|Description|Default| -|---|---|---|---|---| -|\`-H \\`|Set host of the Medusa server.|\`localhost\`| -|\`-p \\`|Set port of the Medusa server.|\`9000\`| -|\`--cluster \\`|Start Medusa's Node.js server in |Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.| - - # telemetry Command - Medusa CLI Reference Enable or disable the collection of anonymous data usage. If no option is provided, the command enables the collection of anonymous data usage. @@ -31649,6 +31632,23 @@ npx medusa user --email [--password ] If ran successfully, you'll receive the invite token in the output.|No|\`false\`| +# start Command - Medusa CLI Reference + +Start the Medusa application in production. + +```bash +npx medusa start +``` + +## Options + +|Option|Description|Default| +|---|---|---|---|---| +|\`-H \\`|Set host of the Medusa server.|\`localhost\`| +|\`-p \\`|Set port of the Medusa server.|\`9000\`| +|\`--cluster \\`|Start Medusa's Node.js server in |Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.| + + # Medusa JS SDK In this documentation, you'll learn how to install and use Medusa's JS SDK. @@ -45071,7 +45071,7 @@ You can confirm that the loyalty points were deducted either by sending a reques You've now implement a loyalty points system in Medusa. There's still more that you can implement based on your use case: -- Add loyalty points on registration or other events. Refer to the [Events Reference](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/events-reference/index.html.md) for a full list of available events you can listen to. +- Add loyalty points on registration or other events. Refer to the [Events Reference](https://docs.medusajs.com/references/events/index.html.md) for a full list of available events you can listen to. - Show the customer their loyalty point usage history. This will require adding another data model in the Loyalty Module that records the usage history. You can create records of that data model when an order that has a loyalty promotion is placed, then customize the storefront to show a new page for loyalty points history. - Customize the Medusa Admin to show a new page or [UI Route](https://docs.medusajs.com/docs/learn/fundamentals/admin/ui-routes/index.html.md) for loyalty points information and analytics. @@ -45166,11 +45166,1227 @@ Integrate a search engine to index and search products or other types of data in - [Algolia](https://docs.medusajs.com/integrations/guides/algolia/index.html.md) +# Integrate Algolia (Search) with Medusa + +In this tutorial, you'll learn how to integrate Medusa with Algolia. + +When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. Medusa's architecture supports integrating third-party services, such as a search engine, allowing you to build your unique requirements around core commerce flows. + +[Algolia](https://www.algolia.com/doc/) is a search engine that enables you to build and manage an intuitive search experience for your customers. By integrating Algolia with Medusa, you can index e-commerce data, such as products, and allow clients to search through them. + +You can follow this guide whether you're new to Medusa or an advanced Medusa developer. + +## Summary + +By following this tutorial, you'll learn how to: + +- Install and set up Medusa. +- Integrate Algolia into Medusa. +- Trigger Algolia reindexing when a product is created, updated, deleted, or when the admin manually triggers a reindex. +- Customize the Next.js Starter Storefront to allow searching for products through Algolia. + +![Diagram illustrating the integration of Algolia with Medusa](https://res.cloudinary.com/dza7lstvk/image/upload/v1742889842/Medusa%20Resources/algolia-summary_lhegrr.jpg) + +- [Algolia Integration Repository](https://github.com/medusajs/examples/tree/main/algolia-integration): Find the full code for this guide in this repository. +- [OpenApi Specs for Postman](https://res.cloudinary.com/dza7lstvk/raw/upload/v1742829748/OpenApi/Algolia-Search_t1zlkd.yaml): Import this OpenApi Specs file into tools like Postman. + +*** + +## Step 1: Install a Medusa Application + +### Prerequisites + +- [Node.js v20+](https://nodejs.org/en/download) +- [Git CLI tool](https://git-scm.com/downloads) +- [PostgreSQL](https://www.postgresql.org/download/) + +Start by installing the Medusa application on your machine with the following command: + +```bash +npx create-medusa-app@latest +``` + +You'll first be asked for the project's name. Then, when asked whether you want to install the [Next.js starter storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md), choose "Yes." + +Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name and the Next.js Starter Storefront in a separate directory named `{project-name}-storefront`. + +The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more in [Medusa's Architecture documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md). + +Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard. + +Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help. + +*** + +## Step 2: Create Algolia Module + +To integrate third-party services into Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup. + +In this step, you'll create a custom module that provides the necessary functionalities to integrate Algolia with Medusa. + +Refer to the [Modules documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) to learn more. + +Before building the module, you need to install Algolia's JavaScript client. Run the following command in your Medusa application's root directory: + +```bash npm2yarn +npm install algoliasearch +``` + +### Create Module Directory + +A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/algolia`. + +### Create Service + +You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to the database, which is useful if your module defines tables in the database, or connect to a third-party service. + +In this section, you'll create the Algolia Module's service and the methods necessary to manage indexed products in Algolia and search through them. + +To create the Algolia Module's service, create the file `src/modules/algolia/service.ts` with the following content: + +```ts title="src/modules/algolia/service.ts" +import { algoliasearch, SearchClient } from "algoliasearch" + +type AlgoliaOptions = { + apiKey: string; + appId: string; + productIndexName: string; +} + +export type AlgoliaIndexType = "product" + +export default class AlgoliaModuleService { + private client: SearchClient + private options: AlgoliaOptions + + constructor({}, options: AlgoliaOptions) { + this.client = algoliasearch(options.appId, options.apiKey) + this.options = options + } + + // TODO add methods +} +``` + +You export a class that will be the Algolia Module's main service. In the service, you define two properties: + +- `client`: An instance of the Algolia Search Client, which you'll use to perform actions with Algolia's API. +- `options`: An object of options that the Module receives when it's registered, which you'll learn about later. The options contain: + - `apiKey`: The Algolia API key. + - `appId`: The Algolia App ID. + - `productIndexName`: The name of the index where products are stored. + +If you want to index other types of data, such as product categories, you can add new properties for their index names in the `AlgoliaOptions` type. + +A module's service receives the module's options as a second parameter in its constructor. In the constructor, you initialize the Algolia client using the module's options. + +A module has a container that holds all resources registered in that module, and you can access those resources in the first parameter of the constructor. Learn more about it in the [Module Container documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md). + +#### Index Data Method + +The first method you need to add to the servie is a method that receives an array of data to add or update in Algolia's index. + +Add the following methods to the `AlgoliaModuleService` class: + +```ts title="src/modules/algolia/service.ts" +export default class AlgoliaModuleService { + // ... + async getIndexName(type: AlgoliaIndexType) { + switch (type) { + case "product": + return this.options.productIndexName + default: + throw new Error(`Invalid index type: ${type}`) + } + } + + async indexData(data: Record[], type: AlgoliaIndexType = "product") { + const indexName = await this.getIndexName(type) + this.client.saveObjects({ + indexName, + objects: data.map((item) => ({ + ...item, + // set the object ID to allow updating later + objectID: item.id, + })), + }) + } +} +``` + +You define two methods: + +1. `getIndexName`: A method that receives an `AlgoliaIndexType` (defined in the previous snippt) and returns the index name for that type. In this case, you only have one type, `product`, so you return the product index name. + - If you want to index other types of data, you can add more cases to the switch statement. +2. `indexData`: A method that receives an array of data and an `AlgoliaIndexType`. The method indexes the data in the Algolia index for the given type. + - Notice that you set the `objectID` property of each object to the object's `id`. This ensures that you later update the object instead of creating a new one. + +#### Retrieve and Delete Methods + +The next methods you'll add to the service are methods to retrieve and delete data from the Algolia index. You'll see their use later as you keep the Algolia index in sync with Medusa. + +Add the following methods to the `AlgoliaModuleService` class: + +```ts title="src/modules/algolia/service.ts" +export default class AlgoliaModuleService { + // ... + + async retrieveFromIndex(objectIDs: string[], type: AlgoliaIndexType = "product") { + const indexName = await this.getIndexName(type) + return await this.client.getObjects>({ + requests: objectIDs.map((objectID) => ({ + indexName, + objectID, + })), + }) + } + + async deleteFromIndex(objectIDs: string[], type: AlgoliaIndexType = "product") { + const indexName = await this.getIndexName(type) + await this.client.deleteObjects({ + indexName, + objectIDs, + }) + } +} +``` + +You define two methods: + +1. `retrieveFromIndex`: A method that receives an array of object IDs and an `AlgoliaIndexType`. The method retrieves the objects with the given IDs from the Algolia index. +2. `deleteFromIndex`: A method that receives an array of object IDs and an `AlgoliaIndexType`. The method deletes the objects with the given IDs from the Algolia index. + +#### Search Method + +The last method you'll implement is a method to search through the Algolia index. You'll later use this method to expose the search functionality to clients, such as the Next.js Storefront. + +Add the following method to the `AlgoliaModuleService` class: + +```ts title="src/modules/algolia/service.ts" +export default class AlgoliaModuleService { + // ... + + async search(query: string, type: AlgoliaIndexType = "product") { + const indexName = await this.getIndexName(type) + return await this.client.search({ + requests: [ + { + indexName, + query, + }, + ], + }) + } +} +``` + +The `search` method receives a query string and an `AlgoliaIndexType`. The method searches through the Algolia index for the given type, such as products, and returns the results. + +### Export Module Definition + +The final piece to a module is its definition, which you export in an `index.ts` file at its root directory. This definition tells Medusa the name of the module and its service. + +So, create the file `src/modules/algolia/index.ts` with the following content: + +```ts title="src/modules/algolia/index.ts" +import { Module } from "@medusajs/framework/utils" +import AlgoliaModuleService from "./service" + +export const ALGOLIA_MODULE = "algolia" + +export default Module(ALGOLIA_MODULE, { + service: AlgoliaModuleService, +}) +``` + +You use the `Module` function from the Modules SDK to create the module's definition. It accepts two parameters: + +1. The module's name, which is `algolia`. +2. An object with a required property `service` indicating the module's service. + +You also export the module's name as `ALGOLIA_MODULE` so you can reference it later. + +### Add Module to Medusa's Configurations + +Once you finish building the module, add it to Medusa's configurations to start using it. + +In `medusa-config.ts`, add a `modules` property and pass an array with your custom module: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "./src/modules/algolia", + options: { + appId: process.env.ALGOLIA_APP_ID!, + apiKey: process.env.ALGOLIA_API_KEY!, + productIndexName: process.env.ALGOLIA_PRODUCT_INDEX_NAME!, + }, + }, + ], +}) +``` + +Each object in the `modules` array has a `resolve` property, whose value is either a path to the module's directory, or an `npm` package’s name. + +You also pass an `options` property with the module's options, including the Algolia App ID, API Key, and the product index name. + +### Add Environment Variables + +Before you can start using the Algolia Module, you need to set the environment variables for the Algolia App ID, API Key, and the product index name. + +Add the following environment variables to your `.env` file: + +```env +ALGOLIA_APP_ID=your-algolia-app-id +ALGOLIA_API_KEY=your-algolia-api-key +ALGOLIA_PRODUCT_INDEX_NAME=your-product-index-name +``` + +Where: + +- `your-algolia-app-id` is your Algolia App ID. You can retrieve it from the Algolia dashboard by clicking at the application ID at the top left next to the sidebar. The pop up will show the application ID below the application's name. + +![Find the Algolia App ID by clicking on the application name in the Algolia dashboard, then copying the ID below the name in the pop-up](https://res.cloudinary.com/dza7lstvk/image/upload/v1742815360/Medusa%20Resources/Screenshot_2025-03-24_at_1.19.30_PM_kdp3y5.png) + +- `your-algolia-api-key` is your Algolia API Key. To retrieve it from the Algolia dashboard: + 1. Click on Settings in the sidebar. + 2. Choose API Keys under "Team and Access". + +![In the settings page, find the Team and Access section at the right of the page and choose API Keys](https://res.cloudinary.com/dza7lstvk/image/upload/v1742815534/Medusa%20Resources/Screenshot_2025-03-24_at_1.25.09_PM_hwsiba.png) + +3. Copy the Admin API Key. + +- `your-product-index-name` is the name of the index where you'll store products. You can find it by going to Search -> Index, and copying the index name at the top of the page. + +![In the Algolia dashboard, go to Search -> Index and copy the index name at the top of the page](https://res.cloudinary.com/dza7lstvk/image/upload/v1742815790/Medusa%20Resources/Screenshot_2025-03-24_at_1.28.58_PM_yq10sf.png) + +Your module is now ready for use. You'll see how to use it in the next steps. + +*** + +## Step 3: Sync Products to Algolia Workflow + +To keep the Algolia index in sync with Medusa, you need to trigger indexing when products are created, updated, or deleted in Medusa. You can also allow the admin to manually trigger a reindex. + +To implement the indexing functionality, you need to create a [workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). A workflow is a series of actions, called steps, that complete a task. You construct a workflow like you construct a function, but it's a special function that allows you to track its executions' progress, define roll-back logic, and configure other advanced features. + +Learn more about workflows in the [Workflows documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). + +In this step, you'll create a workflow that indexes products in Algolia. In the next steps, you'll learn how to use the workflow when products are created, updated, or deleted, or when the admin manually triggers a reindex. + +The workflow has the following steps: + +- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve products matching specified filters and pagination parameters. +- [syncProductsStep](#syncProductsStep): Index products in Algolia. + +Medusa provides the `useQueryGraphStep` in its `@medusajs/medusa/core-flows` package. So, you only need to implement the second step. + +### syncProductsStep + +In the second step of the workflow, you create or update indexes in Algolia for the products retrieved in the first step. + +To create the step, create the file `src/workflows/steps/sync-products.ts` with the following content: + +```ts title="src/workflows/steps/sync-products.ts" +import { ProductDTO } from "@medusajs/framework/types" +import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" +import { ALGOLIA_MODULE } from "../../modules/algolia" +import AlgoliaModuleService from "../../modules/algolia/service" + +export type SyncProductsStepInput = { + products: ProductDTO[] +} + +export const syncProductsStep = createStep( + "sync-products", + async ({ products }: SyncProductsStepInput, { container }) => { + const algoliaModuleService: AlgoliaModuleService = container.resolve(ALGOLIA_MODULE) + + const existingProducts = (await algoliaModuleService.retrieveFromIndex( + products.map((product) => product.id), + "product" + )).results.filter(Boolean) + const newProducts = products.filter( + (product) => !existingProducts.some((p) => p.objectID === product.id) + ) + await algoliaModuleService.indexData( + products as unknown as Record[], + "product" + ) + + return new StepResponse(undefined, { + newProducts: newProducts.map((product) => product.id), + existingProducts, + }) + } + // TODO add compensation +) +``` + +You create a step with `createStep` from the Workflows SDK. It accepts two parameters: + +1. The step's unique name, which is `sync-products`. +2. An async function that receives two parameters: + - The step's input, which is in this case an object holding an array of products to sync into Algolia. + - An object that has properties including the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md), which is a registry of Framework and commerce tools that you can access in the step. + +In the step function, you resolve the Algolia Module's service from the Medusa container using the name you exported in the module definition's file. + +Then, you retrieve the products that are already indexed in Algolia and determine which products are new. You'll learn why this is useful in a bit. + +Finally, you pass the products you received in the input to Algolia to create or update its indices. + +A step function must return a `StepResponse` instance. The `StepResponse` constructor accepts two parameters: + +1. The step's output, which in this case is `undefined`. +2. Data to pass to the step's compensation function. + +#### Compensation Function + +The compensation function undoes the actions performed in a step. Then, if an error occurs during the workflow's execution, the compensation functions of executed steps are called to roll back the changes. This mechanism ensures data consistency in your application, especially as you integrate external systems. + +To add a compensation function to a step, pass it as a third parameter to `createStep`: + +```ts title="src/workflows/steps/sync-products.ts" +export const syncProductsStep = createStep( + // ... + async (input, { container }) => { + if (!input) { + return + } + + const algoliaModuleService: AlgoliaModuleService = container.resolve(ALGOLIA_MODULE) + + if (input.newProducts) { + await algoliaModuleService.deleteFromIndex( + input.newProducts, + "product" + ) + } + + if (input.existingProducts) { + await algoliaModuleService.indexData( + input.existingProducts, + "product" + ) + } + } +) +``` + +The compensation function receives two parameters: + +1. The data you passed as a second parameter of `StepResponse` in the step function. +2. A context object similar to the step function that holds the Medusa container. + +In the compensation function, you resolve the Algolia Module's service from the container. Then, you delete from Algolia the products that were newly indexed, and revert the existing products to their original data. + +### Add Sync Products Workflow + +You can now create the worklow that syncs the products to Algolia. + +To create the workflow, create the file `src/workflows/sync-products.ts` with the following content: + +```ts title="src/workflows/sync-products.ts" +import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk" +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +import { syncProductsStep, SyncProductsStepInput } from "./steps/sync-products" + +type SyncProductsWorkflowInput = { + filters?: Record + limit?: number + offset?: number +} + +export const syncProductsWorkflow = createWorkflow( + "sync-products", + ({ filters, limit, offset }: SyncProductsWorkflowInput) => { + // @ts-ignore + const { data, metadata } = useQueryGraphStep({ + entity: "product", + fields: ["id", "title", "description", "handle", "thumbnail", "categories.*", "tags.*"], + pagination: { + take: limit, + skip: offset, + }, + filters: { + // @ts-ignore + status: "published", + ...filters, + }, + }) + + syncProductsStep({ + products: data, + } as SyncProductsStepInput) + + return new WorkflowResponse({ + products: data, + metadata, + }) + } +) +``` + +You create a workflow using `createWorkflow` from the Workflows SDK. It accepts the workflow's unique name as a first parameter. + +It accepts as a second parameter a constructor function, which is the workflow's implementation. The function can accept input, which in this case is pagination and filter parameters for the products to retrieve. + +In the workflow's constructor function, you: + +1. Execute `useQueryGraphStep` to retrieve products from Medusa's database. This step uses Medusa's [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) tool to retrieve data across modules. You pass it the pagination and filter parameters you received in the input. +2. Execute `syncProductsStep` to index the products in Algolia. You pass it the products you retrieved in the previous step. + +A workflow must return an instance of `WorkflowResponse`. The `WorkflowResponse` constructor accepts the workflow's output as a parameter, which is an object holding the retrieved products and their pagination details. + +In the next step, you'll learn how to execute this workflow. + +*** + +## Step 4: Trigger Algolia Sync Manually + +As mentioned earlier, you'll trigger the Algolia sync automatically when product events occur, but you also want to allow the admin to manually trigger a reindex. + +In this step, you'll add the functionality to trigger the `syncProductsWorkflow` manually from the Medusa Admin dashboard. This requires: + +1. Creating a subscriber that listens to a custom `algolia.sync` event to trigger syncing products to Algolia. +2. Creating an API route that the Medusa Admin dashboard can call to emit the `algolia.sync` event, which triggers the subscriber. +3. Add a new page or UI route to the Medusa Admin dashboard to allow the admin to trigger the reindex. + +### Create Products Sync Subscriber + +A subscriber is an asynchronous function that listens to one or more events and performs actions when these events are emitted. A subscriber is useful when syncing data across systems, as the operation can be time-consuming and should be performed in the background. + +Learn more about subscribers in the [Events and Subscribers documentation](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). + +You create a subscriber in a TypeScript or JavaScript file under the `src/subscribers` directory. So, to create the subscriber that listens to the `algolia.sync` event, create the file `src/subscribers/algolia-sync.ts` with the following content: + +```ts title="src/subscribers/algolia-sync.ts" +import { + SubscriberArgs, + type SubscriberConfig, +} from "@medusajs/framework" +import { syncProductsWorkflow } from "../workflows/sync-products" + +export default async function algoliaSyncHandler({ + container, +}: SubscriberArgs) { + const logger = container.resolve("logger") + + let hasMore = true + let offset = 0 + const limit = 50 + let totalIndexed = 0 + + logger.info("Starting product indexing...") + + while (hasMore) { + const { result: { products, metadata } } = await syncProductsWorkflow(container) + .run({ + input: { + limit, + offset, + }, + }) + + hasMore = offset + limit < (metadata?.count ?? 0) + offset += limit + totalIndexed += products.length + } + + logger.info(`Successfully indexed ${totalIndexed} products`) +} + +export const config: SubscriberConfig = { + event: "algolia.sync", +} +``` + +A subscriber file must export: + +1. An asynchronous function, which is the subscriber that is executed when the event is emitted. +2. A configuration object that holds the name of the event the subscriber listens to, which is `algolia.sync` in this case. + +The subscriber function receives an object as a parameter that has a `container` property, which is the Medusa container. + +In the subscriber function, you initialize variables to keep track of the pagination and the total number of products indexed. + +Then, you start a loop that retrieves products in batches of 50 and indexes them in Algolia using the `syncProductsWorkflow`. Finally, you log the total number of products indexed. + +You'll learn how to emit the `algolia.sync` event next. + +If you want to sync other data types, you can do it in this subscriber as well. + +### Create API Route to Trigger Sync + +To allow the Medusa Admin dashboard to trigger the `algolia.sync` event, you need to create an API route that emits the event. + +An API Route is an endpoint that exposes commerce features to external applications and clients, such as storefronts. + +Learn more about API routes in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). + +An API route is created in a `route.ts` file under a sub-directory of the `src/api` directory. The path of the API route is the file's path relative to `src/api`. + +So, to create an API route at the path `/admin/algolia/sync`, create the file `src/api/admin/algolia/sync/route.ts` with the following content: + +```ts title="src/api/admin/algolia/sync/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { Modules } from "@medusajs/framework/utils" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +) { + const eventModuleService = req.scope.resolve(Modules.EVENT_BUS) + await eventModuleService.emit({ + name: "algolia.sync", + data: {}, + }) + res.send({ + message: "Syncing data to Algolia", + }) +} +``` + +Since you export a `POST` route handler function, you expose an `API` route at `/admin/algolia/sync`. The route handler function accepts two parameters: + +1. A request object with details and context on the request, such as body parameters or authenticated user details. +2. A response object to manipulate and send the response. + +In the route handler, you use the Medusa container that is available in the request object to resolve the [Event Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/event/index.html.md). This module manages events and their subscribers. + +Then, you emit the `algolia.sync` event using the Event Module's `emit` method, passing it the event name. + +Finally, you send a response with a message indicating that data is being synced to Algolia. + +### Add Algolia Sync Page to Admin Dashboard + +The last step is to add a new page to the admin dashboard that allows the admin to trigger the reindex. You add a new page using a [UI Route](https://docs.medusajs.com/docs/learn/fundamentals/admin/ui-routes/index.html.md). + +A UI route is a React component that specifies the content to be shown in a new page in the Medusa Admin dashboard. You'll create a UI route to display a button that triggers the reindex when clicked. + +Learn more about UI routes in the [UI Routes documentation](https://docs.medusajs.com/docs/learn/fundamentals/admin/ui-routes/index.html.md). + +#### Configure JS SDK + +Before creating the UI route, you'll configure Medusa's [JS SDK](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/js-sdk/index.html.md) that you can use to send requests to the Medusa server from any client application, including your Medusa Admin customizations. + +The JS SDK is installed by default in your Medusa application. To configure it, create the file `src/admin/lib/sdk.ts` with the following content: + +```ts title="src/admin/lib/sdk.ts" +import Medusa from "@medusajs/js-sdk" + +export const sdk = new Medusa({ + baseUrl: "http://localhost:9000", + debug: process.env.NODE_ENV === "development", + auth: { + type: "session", + }, +}) +``` + +You create an instance of the JS SDK using the `Medusa` class from the JS SDK. You pass it an object having the following properties: + +- `baseUrl`: The base URL of the Medusa server. +- `debug`: A boolean indicating whether to log debug information into the console. +- `auth`: An object specifying the authentication type. When using the JS SDK for admin customizations, you use the `session` authentication type. + +#### Create UI Route + +You'll now create the UI route that displays a button to trigger the reindex. You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/routes` directory. The file's path relative to `src/admin/routes` determines its path in the dashboard. + +So, to create a new page under the Settings section of the Medusa Admin, create the file `src/admin/routes/settings/algolia/page.tsx` with the following content: + +```tsx title="src/admin/routes/settings/algolia/page.tsx" +import { Container, Heading, Button, toast } from "@medusajs/ui" +import { useMutation } from "@tanstack/react-query" +import { sdk } from "../../../lib/sdk" +import { defineRouteConfig } from "@medusajs/admin-sdk" + +const AlgoliaPage = () => { + const { mutate, isPending } = useMutation({ + mutationFn: () => + sdk.client.fetch("/admin/algolia/sync", { + method: "POST", + }), + onSuccess: () => { + toast.success("Successfully triggered data sync to Algolia") + }, + onError: (err) => { + console.error(err) + toast.error("Failed to sync data to Algolia") + }, + }) + + const handleSync = () => { + mutate() + } + + return ( + +
+ Algolia Sync +
+
+ +
+
+ ) +} + +export const config = defineRouteConfig({ + label: "Algolia", +}) + +export default AlgoliaPage +``` + +A UI route's file must export: + +1. A React component that defines the content of the page. +2. A configuration object that specifies the route's label in the dashboard. This label is used to show a sidebar item for the new route. + +In the React component, you use `useMutation` hook from `@tanstack/react-query` to create a mutation that sends a `POST` request to the API route you created earlier. In the mutation function, you use the JS SDK to send the request. + +Then, in the return statement, you display a button that triggers the mutation when clicked, which sends a request to the API route you created earlier. + +### Test it Out + +You'll now test out the entire flow, starting from triggering the reindex manually from the Medusa Admin dashboard, to checking the Algolia dashboard for the indexed products. + +Run the following command to start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, open the Medusa Admin at `http://localhost:9000/app` and log in with the credentials you set up in the first step. + +Can't remember the credentials? Learn how to create a user in the [Medusa CLI reference](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/medusa-cli/commands/user/index.html.md). + +After you log in, go to Settings from the sidebar. You'll find in the Settings' sidebar a new "Algolia" item. If you click on it, you'll find the page you created with the button to sync products to Algolia. + +If you click on the button, the products will be synced to Algolia. + +![The Algolia Sync page in the Medusa Admin dashboard with a button to sync products to Algolia](https://res.cloudinary.com/dza7lstvk/image/upload/v1742820813/Medusa%20Resources/Screenshot_2025-03-24_at_2.52.31_PM_eiegzb.png) + +You can check that the sync ran and was completed by checking the Medusa logs in the terminal where you started the Medusa application. You should find the following messages: + +```bash +info: Processing algolia.sync which has 1 subscribers +info: Starting product indexing... +info: Successfully indexed 4 products +``` + +These messages indicate that the `algolia.sync` event was emitted, which triggered the subscriber you created to sync the products using the `syncProductsWorkflow`. + +Finally, you can check the Algolia dashboard to see the indexed products. Go to Search -> Index, and check the records of the index you set up in the Algolia Module's options (`products`, for example). + +![The Algolia dashboard showing the indexed products](https://res.cloudinary.com/dza7lstvk/image/upload/v1742821034/Medusa%20Resources/Screenshot_2025-03-24_at_2.56.38_PM_mtojrv.png) + +*** + +## Step 5: Update Index on Product Changes + +You'll now automate the indexing of the products whenever a change occurs. That includes when a product is created, updated, or deleted. + +Similar to before, you'll create subscribers to listen to these events. + +### Handle Create and Update Products + +The action to perform when a product is created or updated is the same. You'll use the `syncProductsWorkflow` to sync the product to Algolia. + +So, you only need one subscriber to handle these two events. To create the subscriber, create the file `src/subscribers/product-sync.ts` with the following content: + +```ts title="src/subscribers/product-sync.ts" +import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" +import { syncProductsWorkflow } from "../workflows/sync-products" + +export default async function handleProductEvents({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + await syncProductsWorkflow(container) + .run({ + input: { + filters: { + id: data.id, + }, + }, + }) +} + +export const config: SubscriberConfig = { + event: ["product.created", "product.updated"], +} +``` + +The subscriber listens to the `product.created` and `product.updated` events. When either of these events is emitted, the subscriber triggers the `syncProductsWorkflow` to sync the product to Algolia. + +When the `product.created` and `product.updated` events are emitted, the product's ID is passed in the event data payload, which you can access in the `event.data` property of the subscriber function's parameter. + +So, you pass the product's ID to the `syncProductsWorkflow` as a filter to retrieve only the product that was created or updated. + +#### Test it Out + +To test it out, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, either create a product or update an existing one using the Medusa Admin dashboard. If you check the Algolia dashboard, you'll find that the product was created or updated. + +### Handle Product Deletion + +When a product is deleted, you need to remove it from the Algolia index. As this requires a different action than creating or updating a product, you'll create a new workflow that deletes the product from Algolia, then create a subscriber that listens to the `product.deleted` event to trigger the workflow. + +#### Create Delete Product Step + +The workflow to delete a product from Algolia will have only one step that deletes products by their IDs from Algolia. + +So, create the step at `src/workflows/steps/delete-products-from-algolia.ts` with the following content: + +```ts title="src/workflows/steps/delete-products-from-algolia.ts" +import { + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { ALGOLIA_MODULE } from "../../modules/algolia" + +export type DeleteProductsFromAlgoliaWorkflow = { + ids: string[] +} + +export const deleteProductsFromAlgoliaStep = createStep( + "delete-products-from-algolia-step", + async ( + { ids }: DeleteProductsFromAlgoliaWorkflow, + { container } + ) => { + const algoliaModuleService = container.resolve(ALGOLIA_MODULE) + + const existingRecords = await algoliaModuleService.retrieveFromIndex( + ids, + "product" + ) + await algoliaModuleService.deleteFromIndex( + ids, + "product" + ) + + return new StepResponse(undefined, existingRecords) + }, + async (existingRecords, { container }) => { + if (!existingRecords) { + return + } + const algoliaModuleService = container.resolve(ALGOLIA_MODULE) + + await algoliaModuleService.indexData( + existingRecords as unknown as Record[], + "product" + ) + } +) +``` + +The step receives the IDs of the products to delete as an input. + +In the step, you resolve the Algolia Module's service and retrieve the existing records from Algolia. This is useful to revert the deletion if an error occurs. + +Then, you delete the products from Algolia and pass the existing records to the compensation function. + +In the compensation function, you reindex the existing records if an error occurs. + +#### Create Delete Product Workflow + +You can now create the workflow that deletes products from Algolia. Create the file `src/workflows/delete-products-from-algolia.ts` with the following content: + +```ts title="src/workflows/delete-products-from-algolia.ts" +import { createWorkflow } from "@medusajs/framework/workflows-sdk" +import { deleteProductsFromAlgoliaStep } from "./steps/delete-products-from-algolia" + +type DeleteProductsFromAlgoliaWorkflowInput = { + ids: string[] +} + +export const deleteProductsFromAlgoliaWorkflow = createWorkflow( + "delete-products-from-algolia", + (input: DeleteProductsFromAlgoliaWorkflowInput) => { + deleteProductsFromAlgoliaStep(input) + } +) +``` + +The workflow receives an object with the IDs of the products to delete. It then executes the `deleteProductsFromAlgoliaStep` to delete the products from Algolia. + +#### Create Delete Product Subscriber + +Finally, you'll create the subscriber that listens to the `product.deleted` event to trigger the above workflow. + +Create the file `src/subscribers/product-delete.ts` with the following content: + +```ts title="src/subscribers/product-delete.ts" +import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" +import { deleteProductsFromAlgoliaWorkflow } from "../workflows/delete-products-from-algolia" + +export default async function handleProductDeleted({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + await deleteProductsFromAlgoliaWorkflow(container) + .run({ + input: { + ids: [data.id], + }, + }) +} + +export const config: SubscriberConfig = { + event: "product.deleted", +} +``` + +The subscriber listens to the `product.deleted` event. When the event is emitted, the subscriber triggers the `deleteProductsFromAlgoliaWorkflow`, passing it the ID of the product to delete. + +#### Test it Out + +To test product deletion, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, delete a product from the Medusa Admin dashboard. If you check the Algolia dashboard, you'll find that the product was deleted there as well. + +*** + +## Step 6: Add Search API Route + +Before customizing the storefront to show the search UI, you'll create an API route in your Medusa application that allows storefronts to search products in Algolia. + +While you can implement the search functionality directly in the storefront to interact with Algolia, this approach centralizes your search integration in Medusa, allowing you to change or modify the integration as necessary. You can also rely on the same behavior and results across different storefronts. + +To implement the API Route, create the file `src/api/store/products/search/route.ts` with the following content: + +```ts title="src/api/store/products/search/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { ALGOLIA_MODULE } from "../../../../modules/algolia" +import AlgoliaModuleService from "../../../../modules/algolia/service" +import { z } from "zod" + +export const SearchSchema = z.object({ + query: z.string(), +}) + +type SearchRequest = z.infer + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +) { + const algoliaModuleService: AlgoliaModuleService = req.scope.resolve(ALGOLIA_MODULE) + + const { query } = req.validatedBody + + const results = await algoliaModuleService.search( + query as string + ) + + res.json(results) +} +``` + +You first define a schema with [Zod](https://zod.dev/), a library to define validation schemas. The schema defines the structure of the request body, which in this case is an object with a `query` property of type `string`. Later, you'll use the schema to enforce request body validation. + +Then, you expose a `POST` API Route at `/store/search` that searches Algolia using the `search` method you implemented in the Algolia Module's service. You pass to it the query string from the request body. + +Finally, you return the search results as it is in the response. + +### Add Validation Middleware + +To ensure that requests sent to the API route have the required request body parameters, you can use a [middleware](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md). A middleware is a function executed when a request is sent to an API Route. It's executed before the route handler. + +Learn more about middleware in the [Middlewares documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md). + +Middlewares are created in the `src/api/middlewares.ts` file. So create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + validateAndTransformBody, +} from "@medusajs/framework/http" +import { SearchSchema } from "./store/products/search/route" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/store/products/search", + method: ["POST"], + middlewares: [ + validateAndTransformBody(SearchSchema), + ], + }, + ], +}) +``` + +To export the middlewares, you use the `defineMiddlewares` function. It accepts an object having a `routes` property, whose value is an array of middleware route objects. Each middleware route object has the following properties: + +- `matcher`: The path of the route the middleware applies to. +- `method`: The HTTP methods the middleware applies to, which is in this case `POST`. +- `middlewares`: An array of middleware functions to apply to the route. You apply the `validateAndTransformBody` middleware which ensures that a request's body has the required parameters. You pass it the schema you defined earlier in the API route's file. + +You can use the search API route now. You'll see it in action as you customize the storefront in the next step. + +*** + +## Step 7: Search Products in Next.js Storefront + +The last step is to provide the search functionalities to customers on your storefront. In the first step, you installed the [Next.js Starter Storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md) along with the Medusa application. + +In this step, you'll customize the Next.js Starter Storefront to add the search functionality. + +The Next.js Starter Storefront was installed in a separate directory from Medusa. The directory's name is `{your-project}-storefront`. + +So, if your Medusa application's directory is `medusa-search`, you can find the storefront by going back to the parent directory and changing to the `medusa-search-storefront` directory: + +```bash +cd ../medusa-search-storefront # change based on your project name +``` + +### Install Algolia Packages + +Before adding the implementation of the search functionality, you need to install the Algolia packages necessary to add the search functionality in your storefront. + +Run the following command in the directory of your Next.js Starter Storefront: + +```bash npm2yarn +npm install algoliasearch react-instantsearch +``` + +This installs the Algolia Search JavaScript client and the React InstantSearch library, which you'll use to build the search functionality. + +### Add Search Client Configuration + +Next, you need to configure the search client. Not only do you need to initialize Algolia, but you also need to change the searching mechanism to use your custom API route in the Medusa application instead of Algolia's API directly. + +In `src/lib/config.ts`, add the following imports at the top of the file: + +```ts title="src/lib/config.ts" badgeLabel="Storefront" badgeColor="blue" +import { + liteClient as algoliasearch, + LiteClient as SearchClient, +} from "algoliasearch/lite" +``` + +Then, add the following at the end of the file: + +```ts title="src/lib/config.ts" badgeLabel="Storefront" badgeColor="blue" +export const searchClient: SearchClient = { + ...(algoliasearch( + process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || "", + process.env.NEXT_PUBLIC_ALGOLIA_API_KEY || "" + )), + search: async (params) => { + const request = Array.isArray(params) ? params[0] : params + const query = "params" in request ? request.params?.query : + "query" in request ? request.query : "" + + if (!query) { + return { + results: [ + { + hits: [], + nbHits: 0, + nbPages: 0, + page: 0, + hitsPerPage: 0, + processingTimeMS: 0, + query: "", + params: "", + }, + ], + } + } + + return await sdk.client.fetch(`/store/products/search`, { + method: "POST", + body: { + query, + }, + }) + }, +} +``` + +In the code above, you create a `searchClient` object that initializes the Algolia client with your Algolia App ID and API Key. + +You also define a `search` method that sends a `POST` request to the search API route you created in the Medusa application. You use the JS SDK (which is initialized in this same file) to send the request. + +### Set Environment Variables + +In the storefront's `.env.local` file, add the following Algolia-related environment variables: + +```plain badgeLabel="Storefront" badgeColor="blue" +NEXT_PUBLIC_ALGOLIA_APP_ID=your_algolia_app_id +NEXT_PUBLIC_ALGOLIA_API_KEY=your_algolia_api_key +NEXT_PUBLIC_ALGOLIA_PRODUCT_INDEX_NAME=your-products-index-name +``` + +Where: + +- `your_algolia_app_id` is your Algolia App ID, as explained in the [Add Environment Variables section](#add-environment-variables) earlier. +- `your_algolia_api_key` is your Algolia Search API key. You can retrieve it from the [same API keys page on the Algolia dashboard](#add-environment-variables) that you retrieved the Admin API key from. +- `your-products-index-name` is the name of the index you created in Algolia to store the products, which you can retrieve as explained in the [Add Environment Variables section](#add-environment-variables) earlier. You'll use this variable later. + +Do not expose your Admin API key in the storefront. The Admin API key should only be used in the Medusa application to interact with Algolia, as it has full access to your Algolia account. + +### Add Search Modal Component + +You'll now add a search modal component that customers can use to search for products. The search modal will display the search results in real-time as the customer types in the search query. + +Later, you'll add the search modal to the navigation bar, allowing customers to open the search modal from any page. + +Create the file `src/modules/search/components/modal/index.tsx` with the following content: + +```tsx title="src/modules/search/components/modal/index.tsx" badgeLabel="Storefront" badgeColor="blue" +"use client" + +import React, { useEffect, useState } from "react" +import { Hits, InstantSearch, SearchBox } from "react-instantsearch" +import { searchClient } from "../../../../lib/config" +import Modal from "../../../common/components/modal" +import { Button } from "@medusajs/ui" +import Image from "next/image" +import Link from "next/link" +import { usePathname } from "next/navigation" + +type Hit = { + objectID: string; + id: string; + title: string; + description: string; + handle: string; + thumbnail: string; +} + +export default function SearchModal() { + const [isOpen, setIsOpen] = useState(false) + const pathname = usePathname() + + useEffect(() => { + setIsOpen(false) + }, [pathname]) + + return ( + <> +
+ +
+ setIsOpen(false)}> + + + + + + + ) +} + +const Hit = ({ hit }: { hit: Hit }) => { + return ( +
+ {hit.title} +
+

{hit.title}

+

{hit.description}

+
+ +
+ ) +} +``` + +You create a `SearchModal` component that displays a search box and the search results using widgets from Algolia's `react-instantsearch` library. + +To display each result item (or hit), you create a `Hit` component that displays the product's title, description, and thumbnail. You also add a link to the product's page. + +Finally, you show the search modal when the customer clicks a "Search" button, which you'll add to the navigation bar next. + +### Add Search Button to Navigation Bar + +The last step is to show the search button in the navigation bar. + +In `src/modules/layout/templates/nav/index.tsx`, add the following imports at the top of the file: + +```tsx title="src/modules/layout/templates/nav/index.tsx" badgeLabel="Storefront" badgeColor="blue" +import SearchModal from "@modules/search/components/modal" +``` + +Then, in the return statement of the `Nav` component, add the `SearchModal` component before the `div` surrounding the "Account" link: + +```tsx title="src/modules/layout/templates/nav/index.tsx" badgeLabel="Storefront" badgeColor="blue" + +``` + +The search button will now appear in the navigation bar before the Account link. + +### Test it Out + +To test out the storefront changes and the search API route, start the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, start the Next.js Starter Storefront from its directory: + +```bash npm2yarn +npm run dev +``` + +Next, go to `localhost:8000`. You'll find a Search button at the top right of the navigation bar. If you click on it, you can search through your products. You can also click on a product to view its page. + +![The Next.js Starter Storefront showing the search modal with search results](https://res.cloudinary.com/dza7lstvk/image/upload/v1742827777/Medusa%20Resources/Screenshot_2025-03-24_at_4.49.23_PM_kzhldx.png) + +*** + +## Next Steps + +You've now integrated Algolia with Medusa and added search functionality to your storefront. You can expand on these features to: + +- Add filters to the search results. You can do that using Algolia's [widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/react/) and customizing the search API route in Medusa to accept filter parameters. +- Support indexing other data types, such as product categories. You can create the subscribers and workflows for categories similar to products. + +If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth learning of all the concepts you've used in this guide and more. + +To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md). + + # Implement Localization in Medusa by Integrating Contentful In this tutorial, you'll learn how to localize your Medusa store's data with Contentful. -When you install a Medusa application, you get a fully-fledged commerce platform with a framework for customization. While Medusa provides features essential for internationalization, such as support for multiple [regions](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/region/index.html.md) and [currencies](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/index.html.md), it doesn't provide content localization. +When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. While Medusa provides features essential for internationalization, such as support for multiple [regions](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/region/index.html.md) and [currencies](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/index.html.md), it doesn't provide content localization. However, Medusa's architecture supports the integration of third-party services to provide additional features, such as data localization. One service you can integrate is [Contentful](https://www.contentful.com/), a headless content management system (CMS) that allows you to manage and deliver content across multiple channels. @@ -46359,7 +47575,7 @@ You create a step with `createStep` from the Workflows SDK. It accepts three par 1. The step's unique name, which is `create-products-contentful-step`. 2. An async function that receives two parameters: - The step's input, which is in this case an object holding an array of products to create in Contentful. - - An object that has properties including the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md), which is a registry of framework and commerce tools that you can access in the step. + - An object that has properties including the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md), which is a registry of Framework and commerce tools that you can access in the step. 3. An optional compensation function that undoes the actions performed in the step if an error occurs in the workflow's execution. This mechanism ensures data consistency in your application, especially as you integrate external systems. The Medusa container is different from the module's container. Since modules are isolated, they each have a container with their resources. Refer to the [Module Container](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md) documentation for more information. @@ -46488,7 +47704,7 @@ A subscriber file must export: The subscriber function receives an object as a parameter that has the following properties: - `event`: An object that holds the event's data payload. The payload of the `product.created` event is an array of product IDs. -- `container`: The Medusa container to access the framework and commerce tools. +- `container`: The Medusa container to access the Framework and commerce tools. In the subscriber function, you execute the `createProductsContentfulWorkflow` by invoking it, passing the Medusa container as a parameter. Then, you chain a `run` method, passing it the product ID from the event's data payload as input. @@ -47913,7 +49129,7 @@ You've now integrated Contentful with Medusa and supported localized product det 1. Add support for other data types, such as product categories or collections. - Refer to the data model references for each [Commerce Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) to figure out the content types you need to create in Contentful. 2. Listen to other product events and update the Contentful entries accordingly. - - Refer to the [Events Reference](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/events-reference/index.html.md) for details on all events emitted in Medusa. + - Refer to the [Events Reference](https://docs.medusajs.com/references/events/index.html.md) for details on all events emitted in Medusa. 3. Add localization for the entire Next.js Starter Storefront. You can either: - Create content types in Contentful for different sections in the storefront, then use them to retrieve the localized content; - Or use the approaches recommended in the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/routing/internationalization). @@ -47935,34 +49151,42 @@ If you encounter issues not covered in the troubleshooting guides: 3. Contact the [sales team](https://medusajs.com/contact/) to get help from the Medusa team. -# Integrate Algolia (Search) with Medusa +# How to Build Magento Data Migration Plugin -In this tutorial, you'll learn how to integrate Medusa with Algolia. +In this tutorial, you'll learn how to build a [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) that migrates data, such as products, from Magento to Medusa. -When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. Medusa's architecture supports integrating third-party services, such as a search engine, allowing you to build your unique requirements around core commerce flows. +Magento is known for its customization capabilities. However, its monolithic architecture imposes limitations on business requirements, often forcing development teams to implement hacky workarounds. Over time, these customizations become challenging to maintain, especially as the business scales, leading to increased technical debt and slower feature delivery. -[Algolia](https://www.algolia.com/doc/) is a search engine that enables you to build and manage an intuitive search experience for your customers. By integrating Algolia with Medusa, you can index e-commerce data, such as products, and allow clients to search through them. +Medusa's modular architecture allows you to build a custom digital commerce platform that meets your business requirements without the limitations of a monolithic system. By migrating from Magento to Medusa, you can take advantage of Medusa's modern technology stack to build a scalable and flexible commerce platform that grows with your business. -You can follow this guide whether you're new to Medusa or an advanced Medusa developer. +By following this tutorial, you'll create a Medusa plugin that migrates data from a Magento server to a Medusa application in minimal time. You can re-use this plugin across multiple Medusa applications, allowing you to adopt Medusa across your projects. ## Summary -By following this tutorial, you'll learn how to: +### Prerequisites -- Install and set up Medusa. -- Integrate Algolia into Medusa. -- Trigger Algolia reindexing when a product is created, updated, deleted, or when the admin manually triggers a reindex. -- Customize the Next.js Starter Storefront to allow searching for products through Algolia. -![Diagram illustrating the integration of Algolia with Medusa](https://res.cloudinary.com/dza7lstvk/image/upload/v1742889842/Medusa%20Resources/algolia-summary_lhegrr.jpg) -- [Algolia Integration Repository](https://github.com/medusajs/examples/tree/main/algolia-integration): Find the full code for this guide in this repository. -- [OpenApi Specs for Postman](https://res.cloudinary.com/dza7lstvk/raw/upload/v1742829748/OpenApi/Algolia-Search_t1zlkd.yaml): Import this OpenApi Specs file into tools like Postman. +This tutorial will teach you how to: + +- Install and set up a Medusa application project. +- Install and set up a Medusa plugin. +- Implement a Magento Module in the plugin to connect to Magento's APIs and retrieve products. + - This guide will only focus on migrating product data from Magento to Medusa. You can extend the implementation to migrate other data, such as customers, orders, and more. +- Trigger data migration from Magento to Medusa in a scheduled job. + +You can follow this tutorial whether you're new to Medusa or an advanced Medusa developer. + +![Diagram showcasing the flow of migrating data from Magento to Medusa](https://res.cloudinary.com/dza7lstvk/image/upload/v1739360550/Medusa%20Resources/magento-summary_hsewci.jpg) + +[Example Repository](https://github.com/medusajs/examples/tree/main/migrate-from-magento): Find the full code of the guide in this repository. The repository also includes additional features, such as triggering migrations from the Medusa Admin dashboard. *** ## Step 1: Install a Medusa Application +You'll first install a Medusa application that exposes core commerce features through REST APIs. You'll later install the Magento plugin in this application to test it out. + ### Prerequisites - [Node.js v20+](https://nodejs.org/en/download) @@ -47975,1176 +49199,1000 @@ Start by installing the Medusa application on your machine with the following co npx create-medusa-app@latest ``` -You'll first be asked for the project's name. Then, when asked whether you want to install the [Next.js starter storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md), choose "Yes." +You'll be asked for the project's name. You can also optionally choose to install the [Next.js starter storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md). -Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name and the Next.js Starter Storefront in a separate directory named `{project-name}-storefront`. +Afterward, the installation process will start, which will install the Medusa application in a directory with your project's name. If you chose to install the Next.js starter, it'll be installed in a separate directory with the `{project-name}-storefront` name. -The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more in [Medusa's Architecture documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md). +The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Refer to the [Medusa Architecture](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md) documentation to learn more. -Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard. +Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterward, you can log in with the new user and explore the dashboard. Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help. *** -## Step 2: Create Algolia Module +## Step 2: Install a Medusa Plugin Project -To integrate third-party services into Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup. +A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. You can add in the plugin [API Routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md), [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), and other customizations, as you'll see in this guide. Afterward, you can test it out locally in a Medusa application, then publish it to npm to install and use it in any Medusa application. -In this step, you'll create a custom module that provides the necessary functionalities to integrate Algolia with Medusa. +Refer to the [Plugins](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) documentation to learn more about plugins. -Refer to the [Modules documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) to learn more. +A Medusa plugin is set up in a different project, giving you the flexibility in building and publishing it, while providing you with the tools to test it out locally in a Medusa application. -Before building the module, you need to install Algolia's JavaScript client. Run the following command in your Medusa application's root directory: +To create a new Medusa plugin project, run the following command in a directory different than that of the Medusa application: ```bash npm2yarn -npm install algoliasearch +npx create-medusa-app@latest medusa-plugin-magento --plugin ``` +Where `medusa-plugin-magento` is the name of the plugin's directory and the name set in the plugin's `package.json`. So, if you wish to publish it to NPM later under a different name, you can change it here in the command or later in `package.json`. + +Once the installation process is done, a new directory named `medusa-plugin-magento` will be created with the plugin project files. + +![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg) + +*** + +## Step 3: Set up Plugin in Medusa Application + +Before you start your development, you'll set up the plugin in the Medusa application you installed in the first step. This will allow you to test the plugin during your development process. + +In the plugin's directory, run the following command to publish the plugin to the local package registry: + +```bash title="Plugin project" +npx medusa plugin:publish +``` + +This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`. + +Next, you'll install the plugin in the Medusa application from the local registry. + +If you've installed your Medusa project before v2.3.1, you must install [yalc](https://github.com/wclr/yalc) as a development dependency first. + +Run the following command in the Medusa application's directory to install the plugin: + +```bash title="Medusa application" +npx medusa plugin:add medusa-plugin-magento +``` + +This command installs the plugin in the Medusa application from the local package registry. + +Next, register the plugin in the `medusa-config.ts` file of the Medusa application: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + plugins: [ + { + resolve: "medusa-plugin-magento", + options: { + // TODO add options + }, + }, + ], +}) +``` + +You add the plugin to the array of plugins. Later, you'll pass options useful to retrieve data from Magento. + +Finally, to ensure your plugin's changes are constantly published to the local registry, simplifying your testing process, keep the following command running in the plugin project during development: + +```bash title="Plugin project" +npx medusa plugin:develop +``` + +*** + +## Step 4: Implement Magento Module + +To connect to external applications in Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup. + +In this step, you'll create a Magento Module in the Magento plugin that connects to a Magento server's REST APIs and retrieves data, such as products. + +Refer to the [Modules](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) documentation to learn more about modules. + ### Create Module Directory -A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/algolia`. +A module is created under the `src/modules` directory of your plugin. So, create the directory `src/modules/magento`. -### Create Service +![Diagram showcasing the module directory to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739272368/magento-1_ikev4x.jpg) -You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to the database, which is useful if your module defines tables in the database, or connect to a third-party service. +### Create Module's Service -In this section, you'll create the Algolia Module's service and the methods necessary to manage indexed products in Algolia and search through them. +You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to external systems or the database, which is useful if your module defines tables in the database. -To create the Algolia Module's service, create the file `src/modules/algolia/service.ts` with the following content: +In this section, you'll create the Magento Module's service that connects to Magento's REST APIs and retrieves data. -```ts title="src/modules/algolia/service.ts" -import { algoliasearch, SearchClient } from "algoliasearch" +Start by creating the file `src/modules/magento/service.ts` in the plugin with the following content: -type AlgoliaOptions = { - apiKey: string; - appId: string; - productIndexName: string; -} +![Diagram showcasing the service file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739272483/magento-2_ajetpr.jpg) -export type AlgoliaIndexType = "product" - -export default class AlgoliaModuleService { - private client: SearchClient - private options: AlgoliaOptions - - constructor({}, options: AlgoliaOptions) { - this.client = algoliasearch(options.appId, options.apiKey) - this.options = options +```ts title="src/modules/magento/service.ts" +type Options = { + baseUrl: string + storeCode?: string + username: string + password: string + migrationOptions?: { + imageBaseUrl?: string } - - // TODO add methods } -``` -You export a class that will be the Algolia Module's main service. In the service, you define two properties: +export default class MagentoModuleService { + private options: Options -- `client`: An instance of the Algolia Search Client, which you'll use to perform actions with Algolia's API. -- `options`: An object of options that the Module receives when it's registered, which you'll learn about later. The options contain: - - `apiKey`: The Algolia API key. - - `appId`: The Algolia App ID. - - `productIndexName`: The name of the index where products are stored. - -If you want to index other types of data, such as product categories, you can add new properties for their index names in the `AlgoliaOptions` type. - -A module's service receives the module's options as a second parameter in its constructor. In the constructor, you initialize the Algolia client using the module's options. - -A module has a container that holds all resources registered in that module, and you can access those resources in the first parameter of the constructor. Learn more about it in the [Module Container documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md). - -#### Index Data Method - -The first method you need to add to the servie is a method that receives an array of data to add or update in Algolia's index. - -Add the following methods to the `AlgoliaModuleService` class: - -```ts title="src/modules/algolia/service.ts" -export default class AlgoliaModuleService { - // ... - async getIndexName(type: AlgoliaIndexType) { - switch (type) { - case "product": - return this.options.productIndexName - default: - throw new Error(`Invalid index type: ${type}`) + constructor({}, options: Options) { + this.options = { + ...options, + storeCode: options.storeCode || "default", } } +} +``` - async indexData(data: Record[], type: AlgoliaIndexType = "product") { - const indexName = await this.getIndexName(type) - this.client.saveObjects({ - indexName, - objects: data.map((item) => ({ - ...item, - // set the object ID to allow updating later - objectID: item.id, - })), +You create a `MagentoModuleService` that has an `options` property to store the module's options. These options include: + +- `baseUrl`: The base URL of the Magento server. +- `storeCode`: The store code of the Magento store, which is `default` by default. +- `username`: The username of a Magento admin user to authenticate with the Magento server. +- `password`: The password of the Magento admin user. +- `migrationOptions`: Additional options useful for migrating data, such as the base URL to use for product images. + +The service's constructor accepts as a first parameter the [Module Container](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md), which allows you to access resources available for the module. As a second parameter, it accepts the module's options. + +### Add Authentication Logic + +To authenticate with the Magento server, you'll add a method to the service that retrieves an access token from Magento using the username and password in the options. This access token is used in subsequent requests to the Magento server. + +First, add the following property to the `MagentoModuleService` class: + +```ts title="src/modules/magento/service.ts" +export default class MagentoModuleService { + private accessToken: { + token: string + expiresAt: Date + } + // ... +} +``` + +You add an `accessToken` property to store the access token and its expiration date. The access token Magento returns expires after four hours, so you store the expiration date to know when to refresh the token. + +Next, add the following `authenticate` method to the `MagentoModuleService` class: + +```ts title="src/modules/magento/service.ts" +import { MedusaError } from "@medusajs/framework/utils" + +export default class MagentoModuleService { + // ... + async authenticate() { + const response = await fetch(`${this.options.baseUrl}/rest/${this.options.storeCode}/V1/integration/admin/token`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username: this.options.username, password: this.options.password }), }) + + const token = await response.text() + + if (!response.ok) { + throw new MedusaError(MedusaError.Types.UNAUTHORIZED, `Failed to authenticate with Magento: ${token}`) + } + + this.accessToken = { + token: token.replaceAll("\"", ""), + expiresAt: new Date(Date.now() + 4 * 60 * 60 * 1000), // 4 hours in milliseconds + } } } ``` -You define two methods: +You create an `authenticate` method that sends a POST request to the Magento server's `/rest/{storeCode}/V1/integration/admin/token` endpoint, passing the username and password in the request body. -1. `getIndexName`: A method that receives an `AlgoliaIndexType` (defined in the previous snippt) and returns the index name for that type. In this case, you only have one type, `product`, so you return the product index name. - - If you want to index other types of data, you can add more cases to the switch statement. -2. `indexData`: A method that receives an array of data and an `AlgoliaIndexType`. The method indexes the data in the Algolia index for the given type. - - Notice that you set the `objectID` property of each object to the object's `id`. This ensures that you later update the object instead of creating a new one. +If the request is successful, you store the access token and its expiration date in the `accessToken` property. If the request fails, you throw a `MedusaError` with the error message returned by Magento. -#### Retrieve and Delete Methods +Lastly, add an `isAccessTokenExpired` method that checks if the access token has expired: -The next methods you'll add to the service are methods to retrieve and delete data from the Algolia index. You'll see their use later as you keep the Algolia index in sync with Medusa. - -Add the following methods to the `AlgoliaModuleService` class: - -```ts title="src/modules/algolia/service.ts" -export default class AlgoliaModuleService { +```ts title="src/modules/magento/service.ts" +export default class MagentoModuleService { // ... - - async retrieveFromIndex(objectIDs: string[], type: AlgoliaIndexType = "product") { - const indexName = await this.getIndexName(type) - return await this.client.getObjects>({ - requests: objectIDs.map((objectID) => ({ - indexName, - objectID, - })), - }) - } - - async deleteFromIndex(objectIDs: string[], type: AlgoliaIndexType = "product") { - const indexName = await this.getIndexName(type) - await this.client.deleteObjects({ - indexName, - objectIDs, - }) + async isAccessTokenExpired(): Promise { + return !this.accessToken || this.accessToken.expiresAt < new Date() } } ``` -You define two methods: +In the `isAccessTokenExpired` method, you return a boolean indicating whether the access token has expired. You'll use this in later methods to check if you need to refresh the access token. -1. `retrieveFromIndex`: A method that receives an array of object IDs and an `AlgoliaIndexType`. The method retrieves the objects with the given IDs from the Algolia index. -2. `deleteFromIndex`: A method that receives an array of object IDs and an `AlgoliaIndexType`. The method deletes the objects with the given IDs from the Algolia index. +### Retrieve Products from Magento -#### Search Method +Next, you'll add a method that retrieves products from Magento. Due to limitations in Magento's API that makes it difficult to differentiate between simple products that don't belong to a configurable product and those that do, you'll only retrieve configurable products and their children. You'll also retrieve the configurable attributes of the product, such as color and size. -The last method you'll implement is a method to search through the Algolia index. You'll later use this method to expose the search functionality to clients, such as the Next.js Storefront. +First, you'll add some types to represent a Magento product and its attributes. Create the file `src/modules/magento/types.ts` in the plugin with the following content: -Add the following method to the `AlgoliaModuleService` class: +![Diagram showcasing the types file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739346287/Medusa%20Resources/magento-3_fpghog.jpg) -```ts title="src/modules/algolia/service.ts" -export default class AlgoliaModuleService { +```ts title="src/modules/magento/types.ts" +export type MagentoProduct = { + id: number + sku: string + name: string + price: number + status: number + // not handling other types + type_id: "simple" | "configurable" + created_at: string + updated_at: string + extension_attributes: { + category_links: { + category_id: string + }[] + configurable_product_links?: number[] + configurable_product_options?: { + id: number + attribute_id: string + label: string + position: number + values: { + value_index: number + }[] + }[] + } + media_gallery_entries: { + id: number + media_type: string + label: string + position: number + disabled: boolean + types: string[] + file: string + }[] + custom_attributes: { + attribute_code: string + value: string + }[] + // added by module + children?: MagentoProduct[] +} + +export type MagentoAttribute = { + attribute_code: string + attribute_id: number + default_frontend_label: string + options: { + label: string + value: string + }[] +} + +export type MagentoPagination = { + search_criteria: { + filter_groups: [], + page_size: number + current_page: number + } + total_count: number +} + +export type MagentoPaginatedResponse = { + items: TData[] +} & MagentoPagination +``` + +You define the following types: + +- `MagentoProduct`: Represents a product in Magento. +- `MagentoAttribute`: Represents an attribute in Magento. +- `MagentoPagination`: Represents the pagination information returned by Magento's API. +- `MagentoPaginatedResponse`: Represents a paginated response from Magento's API for a specific item type, such as products. + +Next, add the `getProducts` method to the `MagentoModuleService` class: + +```ts title="src/modules/magento/service.ts" +export default class MagentoModuleService { // ... + async getProducts(options?: { + currentPage?: number + pageSize?: number + }): Promise<{ + products: MagentoProduct[] + attributes: MagentoAttribute[] + pagination: MagentoPagination + }> { + const { currentPage = 1, pageSize = 100 } = options || {} + const getAccessToken = await this.isAccessTokenExpired() + if (getAccessToken) { + await this.authenticate() + } - async search(query: string, type: AlgoliaIndexType = "product") { - const indexName = await this.getIndexName(type) - return await this.client.search({ - requests: [ - { - indexName, - query, + // TODO prepare query params + } +} +``` + +The `getProducts` method receives an optional `options` object with the `currentPage` and `pageSize` properties. So far, you check if the access token has expired and, if so, retrieve a new one using the `authenticate` method. + +Next, you'll prepare the query parameters to pass in the request that retrieves products. Replace the `TODO` with the following: + +```ts title="src/modules/magento/service.ts" +const searchQuery = new URLSearchParams() +// pass pagination parameters +searchQuery.append( + "searchCriteria[currentPage]", + currentPage?.toString() || "1" +) +searchQuery.append( + "searchCriteria[pageSize]", + pageSize?.toString() || "100" +) + +// retrieve only configurable products +searchQuery.append( + "searchCriteria[filter_groups][1][filters][0][field]", + "type_id" +) +searchQuery.append( + "searchCriteria[filter_groups][1][filters][0][value]", + "configurable" +) +searchQuery.append( + "searchCriteria[filter_groups][1][filters][0][condition_type]", + "in" +) + +// TODO send request to retrieve products +``` + +You create a `searchQuery` object to store the query parameters to pass in the request. Then, you add the pagination parameters and the filter to retrieve only configurable products. + +Next, you'll send the request to retrieve products from Magento. Replace the `TODO` with the following: + +```ts title="src/modules/magento/service.ts" +const { items: products, ...pagination }: MagentoPaginatedResponse = await fetch( + `${this.options.baseUrl}/rest/${this.options.storeCode}/V1/products?${searchQuery}`, + { + headers: { + "Authorization": `Bearer ${this.accessToken.token}`, + }, + } +).then((res) => res.json()) +.catch((err) => { + console.log(err) + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Failed to get products from Magento: ${err.message}` + ) +}) + +// TODO prepare products +``` + +You send a `GET` request to the Magento server's `/rest/{storeCode}/V1/products` endpoint, passing the query parameters in the URL. You also pass the access token in the `Authorization` header. + +Next, you'll prepare the retrieved products by retrieving their children, configurable attributes, and modifying their image URLs. Replace the `TODO` with the following: + +```ts title="src/modules/magento/service.ts" +const attributeIds: string[] = [] + +await promiseAll( + products.map(async (product) => { + // retrieve its children + product.children = await fetch( + `${this.options.baseUrl}/rest/${this.options.storeCode}/V1/configurable-products/${product.sku}/children`, + { + headers: { + "Authorization": `Bearer ${this.accessToken.token}`, }, - ], + } + ).then((res) => res.json()) + .catch((err) => { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Failed to get product children from Magento: ${err.message}` + ) }) + + product.media_gallery_entries = product.media_gallery_entries.map( + (entry) => ({ + ...entry, + file: `${this.options.migrationOptions?.imageBaseUrl}${entry.file}`, + } + )) + + attributeIds.push(...( + product.extension_attributes.configurable_product_options?.map( + (option) => option.attribute_id) || [] + ) + ) + }) +) + +// TODO retrieve attributes +``` + +You loop over the retrieved products and retrieve their children using the `/rest/{storeCode}/V1/configurable-products/{sku}/children` endpoint. You also modify the image URLs to use the base URL in the migration options, if provided. + +In addition, you store the IDs of the configurable products' attributes in the `attributeIds` array. You'll add a method that retrieves these attributes. + +Add the new method `getAttributes` to the `MagentoModuleService` class: + +```ts title="src/modules/magento/service.ts" +export default class MagentoModuleService { + // ... + async getAttributes({ + ids, + }: { + ids: string[] + }): Promise { + const getAccessToken = await this.isAccessTokenExpired() + if (getAccessToken) { + await this.authenticate() + } + + // filter by attribute IDs + const searchQuery = new URLSearchParams() + searchQuery.append( + "searchCriteria[filter_groups][0][filters][0][field]", + "attribute_id" + ) + searchQuery.append( + "searchCriteria[filter_groups][0][filters][0][value]", + ids.join(",") + ) + searchQuery.append( + "searchCriteria[filter_groups][0][filters][0][condition_type]", + "in" + ) + + const { + items: attributes, + }: MagentoPaginatedResponse = await fetch( + `${this.options.baseUrl}/rest/${this.options.storeCode}/V1/products/attributes?${searchQuery}`, + { + headers: { + "Authorization": `Bearer ${this.accessToken.token}`, + }, + } + ).then((res) => res.json()) + .catch((err) => { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Failed to get attributes from Magento: ${err.message}` + ) + }) + + return attributes } } ``` -The `search` method receives a query string and an `AlgoliaIndexType`. The method searches through the Algolia index for the given type, such as products, and returns the results. +The `getAttributes` method receives an object with the `ids` property, which is an array of attribute IDs. You check if the access token has expired and, if so, retrieve a new one using the `authenticate` method. + +Next, you prepare the query parameters to pass in the request to retrieve attributes. You send a `GET` request to the Magento server's `/rest/{storeCode}/V1/products/attributes` endpoint, passing the query parameters in the URL. You also pass the access token in the `Authorization` header. + +Finally, you return the retrieved attributes. + +Now, go back to the `getProducts` method and replace the `TODO` with the following: + +```ts title="src/modules/magento/service.ts" +const attributes = await this.getAttributes({ ids: attributeIds }) + +return { products, attributes, pagination } +``` + +You retrieve the configurable products' attributes using the `getAttributes` method and return the products, attributes, and pagination information. + +You'll use this method in a later step to retrieve products from Magento. ### Export Module Definition The final piece to a module is its definition, which you export in an `index.ts` file at its root directory. This definition tells Medusa the name of the module and its service. -So, create the file `src/modules/algolia/index.ts` with the following content: +So, create the file `src/modules/magento/index.ts` with the following content: -```ts title="src/modules/algolia/index.ts" +![Diagram showcasing the module definition file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739348316/Medusa%20Resources/magento-4_bmepvh.jpg) + +```ts title="src/modules/magento/index.ts" import { Module } from "@medusajs/framework/utils" -import AlgoliaModuleService from "./service" +import MagentoModuleService from "./service" -export const ALGOLIA_MODULE = "algolia" +export const MAGENTO_MODULE = "magento" -export default Module(ALGOLIA_MODULE, { - service: AlgoliaModuleService, +export default Module(MAGENTO_MODULE, { + service: MagentoModuleService, }) ``` You use the `Module` function from the Modules SDK to create the module's definition. It accepts two parameters: -1. The module's name, which is `algolia`. +1. The module's name, which is `magento`. 2. An object with a required property `service` indicating the module's service. -You also export the module's name as `ALGOLIA_MODULE` so you can reference it later. +You'll later use the module's service to retrieve products from Magento. -### Add Module to Medusa's Configurations +### Pass Options to Plugin -Once you finish building the module, add it to Medusa's configurations to start using it. +As mentioned earlier when you registered the plugin in the Medusa Application's `medusa-config.ts` file, you can pass options to the plugin. These options are then passed to the modules in the plugin. -In `medusa-config.ts`, add a `modules` property and pass an array with your custom module: +So, add the following options to the plugin's registration in the `medusa-config.ts` file of the Medusa application: ```ts title="medusa-config.ts" module.exports = defineConfig({ // ... - modules: [ + plugins: [ { - resolve: "./src/modules/algolia", + resolve: "medusa-plugin-magento", options: { - appId: process.env.ALGOLIA_APP_ID!, - apiKey: process.env.ALGOLIA_API_KEY!, - productIndexName: process.env.ALGOLIA_PRODUCT_INDEX_NAME!, + baseUrl: process.env.MAGENTO_BASE_URL, + username: process.env.MAGENTO_USERNAME, + password: process.env.MAGENTO_PASSWORD, + migrationOptions: { + imageBaseUrl: process.env.MAGENTO_IMAGE_BASE_URL, + }, }, }, ], }) ``` -Each object in the `modules` array has a `resolve` property, whose value is either a path to the module's directory, or an `npm` package’s name. +You pass the options that you defined in the `MagentoModuleService`. Make sure to also set their environment variables in the `.env` file: -You also pass an `options` property with the module's options, including the Algolia App ID, API Key, and the product index name. - -### Add Environment Variables - -Before you can start using the Algolia Module, you need to set the environment variables for the Algolia App ID, API Key, and the product index name. - -Add the following environment variables to your `.env` file: - -```env -ALGOLIA_APP_ID=your-algolia-app-id -ALGOLIA_API_KEY=your-algolia-api-key -ALGOLIA_PRODUCT_INDEX_NAME=your-product-index-name +```bash +MAGENTO_BASE_URL=https://magento.example.com +MAGENTO_USERNAME=admin +MAGENTO_PASSWORD=password +MAGENTO_IMAGE_BASE_URL=https://magento.example.com/pub/media/catalog/product ``` Where: -- `your-algolia-app-id` is your Algolia App ID. You can retrieve it from the Algolia dashboard by clicking at the application ID at the top left next to the sidebar. The pop up will show the application ID below the application's name. +- `MAGENTO_BASE_URL`: The base URL of the Magento server. It can also be a local URL, such as `http://localhost:8080`. +- `MAGENTO_USERNAME`: The username of a Magento admin user to authenticate with the Magento server. +- `MAGENTO_PASSWORD`: The password of the Magento admin user. +- `MAGENTO_IMAGE_BASE_URL`: The base URL to use for product images. Magento stores product images in the `pub/media/catalog/product` directory, so you can reference them directly or use a CDN URL. If the URLs of product images in the Medusa server already have a different base URL, you can omit this option. -![Find the Algolia App ID by clicking on the application name in the Algolia dashboard, then copying the ID below the name in the pop-up](https://res.cloudinary.com/dza7lstvk/image/upload/v1742815360/Medusa%20Resources/Screenshot_2025-03-24_at_1.19.30_PM_kdp3y5.png) +Medusa supports integrating third-party services, such as [S3](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/file/s3/index.html.md), in a File Module Provider. Refer to the [File Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/file/index.html.md) documentation to find other module providers and how to create a custom provider. -- `your-algolia-api-key` is your Algolia API Key. To retrieve it from the Algolia dashboard: - 1. Click on Settings in the sidebar. - 2. Choose API Keys under "Team and Access". - -![In the settings page, find the Team and Access section at the right of the page and choose API Keys](https://res.cloudinary.com/dza7lstvk/image/upload/v1742815534/Medusa%20Resources/Screenshot_2025-03-24_at_1.25.09_PM_hwsiba.png) - -3. Copy the Admin API Key. - -- `your-product-index-name` is the name of the index where you'll store products. You can find it by going to Search -> Index, and copying the index name at the top of the page. - -![In the Algolia dashboard, go to Search -> Index and copy the index name at the top of the page](https://res.cloudinary.com/dza7lstvk/image/upload/v1742815790/Medusa%20Resources/Screenshot_2025-03-24_at_1.28.58_PM_yq10sf.png) - -Your module is now ready for use. You'll see how to use it in the next steps. +You can now use the Magento Module to migrate data, which you'll do in the next steps. *** -## Step 3: Sync Products to Algolia Workflow +## Step 5: Build Product Migration Workflow -To keep the Algolia index in sync with Medusa, you need to trigger indexing when products are created, updated, or deleted in Medusa. You can also allow the admin to manually trigger a reindex. +In this section, you'll add the feature to migrate products from Magento to Medusa. To implement this feature, you'll use a workflow. -To implement the indexing functionality, you need to create a [workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). A workflow is a series of actions, called steps, that complete a task. You construct a workflow like you construct a function, but it's a special function that allows you to track its executions' progress, define roll-back logic, and configure other advanced features. +A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow like you construct a function, but it's a special function that allows you to track its executions' progress, define roll-back logic, and configure other advanced features. Then, you execute the workflow from other customizations, such as in an API route or a scheduled job. -Learn more about workflows in the [Workflows documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). +By implementing the migration feature in a workflow, you ensure that the data remains consistent and that the migration process can be rolled back if an error occurs. -In this step, you'll create a workflow that indexes products in Algolia. In the next steps, you'll learn how to use the workflow when products are created, updated, or deleted, or when the admin manually triggers a reindex. +Refer to the [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md) documentation to learn more about workflows. -The workflow has the following steps: +### Workflow Steps -- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve products matching specified filters and pagination parameters. -- [syncProductsStep](#syncProductsStep): Index products in Algolia. +The workflow you'll create will have the following steps: -Medusa provides the `useQueryGraphStep` in its `@medusajs/medusa/core-flows` package. So, you only need to implement the second step. +- [getMagentoProductsStep](#getMagentoProductsStep): Retrieve products from Magento using the Magento Module. +- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve Medusa store details, which you'll need when creating the products. +- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve a shipping profile, which you'll associate the created products with. +- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve Magento products that are already in Medusa to update them, instead of creating them. +- [createProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductsWorkflow/index.html.md): Create products in the Medusa application. +- [updateProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductsWorkflow/index.html.md): Update existing products in the Medusa application. -### syncProductsStep +You only need to implement the `getMagentoProductsStep` step, which retrieves the products from Magento. The other steps and workflows are provided by Medusa's `@medusajs/medusa/core-flows` package. -In the second step of the workflow, you create or update indexes in Algolia for the products retrieved in the first step. +### getMagentoProductsStep -To create the step, create the file `src/workflows/steps/sync-products.ts` with the following content: +The first step of the workflow retrieves and returns the products from Magento. -```ts title="src/workflows/steps/sync-products.ts" -import { ProductDTO } from "@medusajs/framework/types" +In your plugin, create the file `src/workflows/steps/get-magento-products.ts` with the following content: + +![Diagram showcasing the get-magento-products file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739349590/Medusa%20Resources/magento-5_ueb4wn.jpg) + +```ts title="src/workflows/steps/get-magento-products.ts" import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" -import { ALGOLIA_MODULE } from "../../modules/algolia" -import AlgoliaModuleService from "../../modules/algolia/service" +import { MAGENTO_MODULE } from "../../modules/magento" +import MagentoModuleService from "../../modules/magento/service" -export type SyncProductsStepInput = { - products: ProductDTO[] +type GetMagentoProductsInput = { + currentPage: number + pageSize: number } -export const syncProductsStep = createStep( - "sync-products", - async ({ products }: SyncProductsStepInput, { container }) => { - const algoliaModuleService: AlgoliaModuleService = container.resolve(ALGOLIA_MODULE) +export const getMagentoProductsStep = createStep( + "get-magento-products", + async ({ currentPage, pageSize }: GetMagentoProductsInput, { container }) => { + const magentoModuleService: MagentoModuleService = + container.resolve(MAGENTO_MODULE) - const existingProducts = (await algoliaModuleService.retrieveFromIndex( - products.map((product) => product.id), - "product" - )).results.filter(Boolean) - const newProducts = products.filter( - (product) => !existingProducts.some((p) => p.objectID === product.id) - ) - await algoliaModuleService.indexData( - products as unknown as Record[], - "product" - ) - - return new StepResponse(undefined, { - newProducts: newProducts.map((product) => product.id), - existingProducts, + const response = await magentoModuleService.getProducts({ + currentPage, + pageSize, }) - } - // TODO add compensation -) -``` -You create a step with `createStep` from the Workflows SDK. It accepts two parameters: - -1. The step's unique name, which is `sync-products`. -2. An async function that receives two parameters: - - The step's input, which is in this case an object holding an array of products to sync into Algolia. - - An object that has properties including the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md), which is a registry of Framework and commerce tools that you can access in the step. - -In the step function, you resolve the Algolia Module's service from the Medusa container using the name you exported in the module definition's file. - -Then, you retrieve the products that are already indexed in Algolia and determine which products are new. You'll learn why this is useful in a bit. - -Finally, you pass the products you received in the input to Algolia to create or update its indices. - -A step function must return a `StepResponse` instance. The `StepResponse` constructor accepts two parameters: - -1. The step's output, which in this case is `undefined`. -2. Data to pass to the step's compensation function. - -#### Compensation Function - -The compensation function undoes the actions performed in a step. Then, if an error occurs during the workflow's execution, the compensation functions of executed steps are called to roll back the changes. This mechanism ensures data consistency in your application, especially as you integrate external systems. - -To add a compensation function to a step, pass it as a third parameter to `createStep`: - -```ts title="src/workflows/steps/sync-products.ts" -export const syncProductsStep = createStep( - // ... - async (input, { container }) => { - if (!input) { - return - } - - const algoliaModuleService: AlgoliaModuleService = container.resolve(ALGOLIA_MODULE) - - if (input.newProducts) { - await algoliaModuleService.deleteFromIndex( - input.newProducts, - "product" - ) - } - - if (input.existingProducts) { - await algoliaModuleService.indexData( - input.existingProducts, - "product" - ) - } + return new StepResponse(response) } ) ``` -The compensation function receives two parameters: +You create a step using `createStep` from the Workflows SDK. It accepts two parameters: -1. The data you passed as a second parameter of `StepResponse` in the step function. -2. A context object similar to the step function that holds the Medusa container. +1. The step's name, which is `get-magento-products`. +2. An async function that executes the step's logic. The function receives two parameters: + - The input data for the step, which in this case is the pagination parameters. + - An object holding the workflow's context, including the [Medusa Container](https://docs.medusajs.com/docslearn/fundamentals/medusa-container/index.html.md) that allows you to resolve Framework and commerce tools. -In the compensation function, you resolve the Algolia Module's service from the container. Then, you delete from Algolia the products that were newly indexed, and revert the existing products to their original data. +In the step function, you resolve the Magento Module's service from the container, then use its `getProducts` method to retrieve the products from Magento. -### Add Sync Products Workflow +Steps that return data must return them in a `StepResponse` instance. The `StepResponse` constructor accepts as a parameter the data to return. -You can now create the worklow that syncs the products to Algolia. +### Create migrateProductsFromMagentoWorkflow -To create the workflow, create the file `src/workflows/sync-products.ts` with the following content: +You'll now create the workflow that migrates products from Magento using the step you created and steps from Medusa's `@medusajs/medusa/core-flows` package. -```ts title="src/workflows/sync-products.ts" -import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk" -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" -import { syncProductsStep, SyncProductsStepInput } from "./steps/sync-products" +In your plugin, create the file `src/workflows/migrate-products-from-magento.ts` with the following content: -type SyncProductsWorkflowInput = { - filters?: Record - limit?: number - offset?: number +![Diagram showcasing the migrate-products-from-magento file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739349820/Medusa%20Resources/magento-6_jjdaxj.jpg) + +```ts title="src/workflows/migrate-products-from-magento.ts" +import { + createWorkflow, transform, WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" +import { + CreateProductWorkflowInputDTO, UpsertProductDTO, +} from "@medusajs/framework/types" +import { + createProductsWorkflow, + updateProductsWorkflow, + useQueryGraphStep, +} from "@medusajs/medusa/core-flows" +import { getMagentoProductsStep } from "./steps/get-magento-products" + +type MigrateProductsFromMagentoWorkflowInput = { + currentPage: number + pageSize: number } -export const syncProductsWorkflow = createWorkflow( - "sync-products", - ({ filters, limit, offset }: SyncProductsWorkflowInput) => { - // @ts-ignore - const { data, metadata } = useQueryGraphStep({ - entity: "product", - fields: ["id", "title", "description", "handle", "thumbnail", "categories.*", "tags.*"], - pagination: { - take: limit, - skip: offset, - }, - filters: { - // @ts-ignore - status: "published", - ...filters, - }, - }) +export const migrateProductsFromMagentoWorkflowId = + "migrate-products-from-magento" - syncProductsStep({ - products: data, - } as SyncProductsStepInput) - - return new WorkflowResponse({ - products: data, - metadata, - }) +export const migrateProductsFromMagentoWorkflow = createWorkflow( + { + name: migrateProductsFromMagentoWorkflowId, + retentionTime: 10000, + store: true, + }, + (input: MigrateProductsFromMagentoWorkflowInput) => { + const { pagination, products, attributes } = getMagentoProductsStep( + input + ) + // TODO prepare data to create and update products } ) ``` -You create a workflow using `createWorkflow` from the Workflows SDK. It accepts the workflow's unique name as a first parameter. +You create a workflow using `createWorkflow` from the Workflows SDK. It accepts two parameters: -It accepts as a second parameter a constructor function, which is the workflow's implementation. The function can accept input, which in this case is pagination and filter parameters for the products to retrieve. +1. An object with the workflow's configuration, including the name and whether to store the workflow's executions. You enable storing the workflow execution so that you can view it later in the Medusa Admin dashboard. +2. A worflow constructor function, which holds the workflow's implementation. The function receives the input data for the workflow, which is the pagination parameters. -In the workflow's constructor function, you: +In the workflow constructor function, you use the `getMagentoProductsStep` step to retrieve the products from Magento, passing it the pagination parameters from the workflow's input. -1. Execute `useQueryGraphStep` to retrieve products from Medusa's database. This step uses Medusa's [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) tool to retrieve data across modules. You pass it the pagination and filter parameters you received in the input. -2. Execute `syncProductsStep` to index the products in Algolia. You pass it the products you retrieved in the previous step. +Next, you'll retrieve the Medusa store details and shipping profiles. These are necessary to prepare the data of the products to create or update. -A workflow must return an instance of `WorkflowResponse`. The `WorkflowResponse` constructor accepts the workflow's output as a parameter, which is an object holding the retrieved products and their pagination details. +Replace the `TODO` in the workflow function with the following: -In the next step, you'll learn how to execute this workflow. +```ts title="src/workflows/migrate-products-from-magento.ts" +const { data: stores } = useQueryGraphStep({ + entity: "store", + fields: ["supported_currencies.*", "default_sales_channel_id"], + pagination: { + take: 1, + skip: 0, + }, +}) -*** +const { data: shippingProfiles } = useQueryGraphStep({ + entity: "shipping_profile", + fields: ["id"], + pagination: { + take: 1, + skip: 0, + }, +}).config({ name: "get-shipping-profiles" }) -## Step 4: Trigger Algolia Sync Manually +// TODO retrieve existing products +``` -As mentioned earlier, you'll trigger the Algolia sync automatically when product events occur, but you also want to allow the admin to manually trigger a reindex. +You use the `useQueryGraphStep` step to retrieve the store details and shipping profiles. `useQueryGraphStep` is a Medusa step that wraps [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), allowing you to use it in a workflow. Query is a tool that retrieves data across modules. -In this step, you'll add the functionality to trigger the `syncProductsWorkflow` manually from the Medusa Admin dashboard. This requires: +Whe retrieving the store details, you specifically retrieve its supported currencies and default sales channel ID. You'll associate the products with the store's default sales channel, and set their variant prices in the supported currencies. You'll also associate the products with a shipping profile. -1. Creating a subscriber that listens to a custom `algolia.sync` event to trigger syncing products to Algolia. -2. Creating an API route that the Medusa Admin dashboard can call to emit the `algolia.sync` event, which triggers the subscriber. -3. Add a new page or UI route to the Medusa Admin dashboard to allow the admin to trigger the reindex. +Next, you'll retrieve products that were previously migrated from Magento to determine which products to create or update. Replace the `TODO` with the following: -### Create Products Sync Subscriber +```ts title="src/workflows/migrate-products-from-magento.ts" +const externalIdFilters = transform({ + products, +}, (data) => { + return data.products.map((product) => product.id.toString()) +}) -A subscriber is an asynchronous function that listens to one or more events and performs actions when these events are emitted. A subscriber is useful when syncing data across systems, as the operation can be time-consuming and should be performed in the background. +const { data: existingProducts } = useQueryGraphStep({ + entity: "product", + fields: ["id", "external_id", "variants.id", "variants.metadata"], + filters: { + external_id: externalIdFilters, + }, +}).config({ name: "get-existing-products" }) -Learn more about subscribers in the [Events and Subscribers documentation](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). +// TODO prepare products to create or update +``` -You create a subscriber in a TypeScript or JavaScript file under the `src/subscribers` directory. So, to create the subscriber that listens to the `algolia.sync` event, create the file `src/subscribers/algolia-sync.ts` with the following content: +Since the Medusa application creates an internal representation of the workflow's constructor function, you can't manipulate data directly, as variables have no value while creating the internal representation. -```ts title="src/subscribers/algolia-sync.ts" -import { - SubscriberArgs, - type SubscriberConfig, -} from "@medusajs/framework" -import { syncProductsWorkflow } from "../workflows/sync-products" +Refer to the [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/constructor-constraints/index.html.md) documentation to learn more about the workflow constructor function's constraints. -export default async function algoliaSyncHandler({ - container, -}: SubscriberArgs) { - const logger = container.resolve("logger") - - let hasMore = true - let offset = 0 - const limit = 50 - let totalIndexed = 0 +Instead, you can manipulate data in a workflow's constructor function using `transform` from the Workflows SDK. `transform` is a function that accepts two parameters: - logger.info("Starting product indexing...") +- The data to transform, which in this case is the Magento products. +- A function that transforms the data. The function receives the data passed in the first parameter and returns the transformed data. - while (hasMore) { - const { result: { products, metadata } } = await syncProductsWorkflow(container) - .run({ - input: { - limit, - offset, - }, +In the transformation function, you return the IDs of the Magento products. Then, you use the `useQueryGraphStep` to retrieve products in the Medusa application that have an `external_id` property matching the IDs of the Magento products. You'll use this property to store the IDs of the products in Magento. + +Next, you'll prepare the data to create and update the products. Replace the `TODO` in the workflow function with the following: + +```ts title="src/workflows/migrate-products-from-magento.ts" highlights={prepareHighlights} +const { + productsToCreate, + productsToUpdate, +} = transform({ + products, + attributes, + stores, + shippingProfiles, + existingProducts, +}, (data) => { + const productsToCreate = new Map() + const productsToUpdate = new Map() + + data.products.forEach((magentoProduct) => { + const productData: CreateProductWorkflowInputDTO | UpsertProductDTO = { + title: magentoProduct.name, + description: magentoProduct.custom_attributes.find( + (attr) => attr.attribute_code === "description" + )?.value, + status: magentoProduct.status === 1 ? "published" : "draft", + handle: magentoProduct.custom_attributes.find( + (attr) => attr.attribute_code === "url_key" + )?.value, + external_id: magentoProduct.id.toString(), + thumbnail: magentoProduct.media_gallery_entries.find( + (entry) => entry.types.includes("thumbnail") + )?.file, + sales_channels: [{ + id: data.stores[0].default_sales_channel_id, + }], + shipping_profile_id: data.shippingProfiles[0].id, + } + const existingProduct = data.existingProducts.find((p) => p.external_id === productData.external_id) + + if (existingProduct) { + productData.id = existingProduct.id + } + + productData.options = magentoProduct.extension_attributes.configurable_product_options?.map((option) => { + const attribute = data.attributes.find((attr) => attr.attribute_id === parseInt(option.attribute_id)) + return { + title: option.label, + values: attribute?.options.filter((opt) => { + return option.values.find((v) => v.value_index === parseInt(opt.value)) + }).map((opt) => opt.label) || [], + } + }) || [] + + productData.variants = magentoProduct.children?.map((child) => { + const childOptions: Record = {} + + child.custom_attributes.forEach((attr) => { + const attrData = data.attributes.find((a) => a.attribute_code === attr.attribute_code) + if (!attrData) { + return + } + + childOptions[attrData.default_frontend_label] = attrData.options.find((opt) => opt.value === attr.value)?.label || "" }) - hasMore = offset + limit < (metadata?.count ?? 0) - offset += limit - totalIndexed += products.length - } + const variantExternalId = child.id.toString() + const existingVariant = existingProduct.variants.find((v) => v.metadata.external_id === variantExternalId) - logger.info(`Successfully indexed ${totalIndexed} products`) -} - -export const config: SubscriberConfig = { - event: "algolia.sync", -} -``` - -A subscriber file must export: - -1. An asynchronous function, which is the subscriber that is executed when the event is emitted. -2. A configuration object that holds the name of the event the subscriber listens to, which is `algolia.sync` in this case. - -The subscriber function receives an object as a parameter that has a `container` property, which is the Medusa container. - -In the subscriber function, you initialize variables to keep track of the pagination and the total number of products indexed. - -Then, you start a loop that retrieves products in batches of 50 and indexes them in Algolia using the `syncProductsWorkflow`. Finally, you log the total number of products indexed. - -You'll learn how to emit the `algolia.sync` event next. - -If you want to sync other data types, you can do it in this subscriber as well. - -### Create API Route to Trigger Sync - -To allow the Medusa Admin dashboard to trigger the `algolia.sync` event, you need to create an API route that emits the event. - -An API Route is an endpoint that exposes commerce features to external applications and clients, such as storefronts. - -Learn more about API routes in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). - -An API route is created in a `route.ts` file under a sub-directory of the `src/api` directory. The path of the API route is the file's path relative to `src/api`. - -So, to create an API route at the path `/admin/algolia/sync`, create the file `src/api/admin/algolia/sync/route.ts` with the following content: - -```ts title="src/api/admin/algolia/sync/route.ts" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { Modules } from "@medusajs/framework/utils" - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - const eventModuleService = req.scope.resolve(Modules.EVENT_BUS) - await eventModuleService.emit({ - name: "algolia.sync", - data: {}, - }) - res.send({ - message: "Syncing data to Algolia", - }) -} -``` - -Since you export a `POST` route handler function, you expose an `API` route at `/admin/algolia/sync`. The route handler function accepts two parameters: - -1. A request object with details and context on the request, such as body parameters or authenticated user details. -2. A response object to manipulate and send the response. - -In the route handler, you use the Medusa container that is available in the request object to resolve the [Event Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/event/index.html.md). This module manages events and their subscribers. - -Then, you emit the `algolia.sync` event using the Event Module's `emit` method, passing it the event name. - -Finally, you send a response with a message indicating that data is being synced to Algolia. - -### Add Algolia Sync Page to Admin Dashboard - -The last step is to add a new page to the admin dashboard that allows the admin to trigger the reindex. You add a new page using a [UI Route](https://docs.medusajs.com/docs/learn/fundamentals/admin/ui-routes/index.html.md). - -A UI route is a React component that specifies the content to be shown in a new page in the Medusa Admin dashboard. You'll create a UI route to display a button that triggers the reindex when clicked. - -Learn more about UI routes in the [UI Routes documentation](https://docs.medusajs.com/docs/learn/fundamentals/admin/ui-routes/index.html.md). - -#### Configure JS SDK - -Before creating the UI route, you'll configure Medusa's [JS SDK](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/js-sdk/index.html.md) that you can use to send requests to the Medusa server from any client application, including your Medusa Admin customizations. - -The JS SDK is installed by default in your Medusa application. To configure it, create the file `src/admin/lib/sdk.ts` with the following content: - -```ts title="src/admin/lib/sdk.ts" -import Medusa from "@medusajs/js-sdk" - -export const sdk = new Medusa({ - baseUrl: "http://localhost:9000", - debug: process.env.NODE_ENV === "development", - auth: { - type: "session", - }, -}) -``` - -You create an instance of the JS SDK using the `Medusa` class from the JS SDK. You pass it an object having the following properties: - -- `baseUrl`: The base URL of the Medusa server. -- `debug`: A boolean indicating whether to log debug information into the console. -- `auth`: An object specifying the authentication type. When using the JS SDK for admin customizations, you use the `session` authentication type. - -#### Create UI Route - -You'll now create the UI route that displays a button to trigger the reindex. You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/routes` directory. The file's path relative to `src/admin/routes` determines its path in the dashboard. - -So, to create a new page under the Settings section of the Medusa Admin, create the file `src/admin/routes/settings/algolia/page.tsx` with the following content: - -```tsx title="src/admin/routes/settings/algolia/page.tsx" -import { Container, Heading, Button, toast } from "@medusajs/ui" -import { useMutation } from "@tanstack/react-query" -import { sdk } from "../../../lib/sdk" -import { defineRouteConfig } from "@medusajs/admin-sdk" - -const AlgoliaPage = () => { - const { mutate, isPending } = useMutation({ - mutationFn: () => - sdk.client.fetch("/admin/algolia/sync", { - method: "POST", - }), - onSuccess: () => { - toast.success("Successfully triggered data sync to Algolia") - }, - onError: (err) => { - console.error(err) - toast.error("Failed to sync data to Algolia") - }, - }) - - const handleSync = () => { - mutate() - } - - return ( - -
- Algolia Sync -
-
- -
-
- ) -} - -export const config = defineRouteConfig({ - label: "Algolia", -}) - -export default AlgoliaPage -``` - -A UI route's file must export: - -1. A React component that defines the content of the page. -2. A configuration object that specifies the route's label in the dashboard. This label is used to show a sidebar item for the new route. - -In the React component, you use `useMutation` hook from `@tanstack/react-query` to create a mutation that sends a `POST` request to the API route you created earlier. In the mutation function, you use the JS SDK to send the request. - -Then, in the return statement, you display a button that triggers the mutation when clicked, which sends a request to the API route you created earlier. - -### Test it Out - -You'll now test out the entire flow, starting from triggering the reindex manually from the Medusa Admin dashboard, to checking the Algolia dashboard for the indexed products. - -Run the following command to start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, open the Medusa Admin at `http://localhost:9000/app` and log in with the credentials you set up in the first step. - -Can't remember the credentials? Learn how to create a user in the [Medusa CLI reference](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/medusa-cli/commands/user/index.html.md). - -After you log in, go to Settings from the sidebar. You'll find in the Settings' sidebar a new "Algolia" item. If you click on it, you'll find the page you created with the button to sync products to Algolia. - -If you click on the button, the products will be synced to Algolia. - -![The Algolia Sync page in the Medusa Admin dashboard with a button to sync products to Algolia](https://res.cloudinary.com/dza7lstvk/image/upload/v1742820813/Medusa%20Resources/Screenshot_2025-03-24_at_2.52.31_PM_eiegzb.png) - -You can check that the sync ran and was completed by checking the Medusa logs in the terminal where you started the Medusa application. You should find the following messages: - -```bash -info: Processing algolia.sync which has 1 subscribers -info: Starting product indexing... -info: Successfully indexed 4 products -``` - -These messages indicate that the `algolia.sync` event was emitted, which triggered the subscriber you created to sync the products using the `syncProductsWorkflow`. - -Finally, you can check the Algolia dashboard to see the indexed products. Go to Search -> Index, and check the records of the index you set up in the Algolia Module's options (`products`, for example). - -![The Algolia dashboard showing the indexed products](https://res.cloudinary.com/dza7lstvk/image/upload/v1742821034/Medusa%20Resources/Screenshot_2025-03-24_at_2.56.38_PM_mtojrv.png) - -*** - -## Step 5: Update Index on Product Changes - -You'll now automate the indexing of the products whenever a change occurs. That includes when a product is created, updated, or deleted. - -Similar to before, you'll create subscribers to listen to these events. - -### Handle Create and Update Products - -The action to perform when a product is created or updated is the same. You'll use the `syncProductsWorkflow` to sync the product to Algolia. - -So, you only need one subscriber to handle these two events. To create the subscriber, create the file `src/subscribers/product-sync.ts` with the following content: - -```ts title="src/subscribers/product-sync.ts" -import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" -import { syncProductsWorkflow } from "../workflows/sync-products" - -export default async function handleProductEvents({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - await syncProductsWorkflow(container) - .run({ - input: { - filters: { - id: data.id, - }, - }, - }) -} - -export const config: SubscriberConfig = { - event: ["product.created", "product.updated"], -} -``` - -The subscriber listens to the `product.created` and `product.updated` events. When either of these events is emitted, the subscriber triggers the `syncProductsWorkflow` to sync the product to Algolia. - -When the `product.created` and `product.updated` events are emitted, the product's ID is passed in the event data payload, which you can access in the `event.data` property of the subscriber function's parameter. - -So, you pass the product's ID to the `syncProductsWorkflow` as a filter to retrieve only the product that was created or updated. - -#### Test it Out - -To test it out, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, either create a product or update an existing one using the Medusa Admin dashboard. If you check the Algolia dashboard, you'll find that the product was created or updated. - -### Handle Product Deletion - -When a product is deleted, you need to remove it from the Algolia index. As this requires a different action than creating or updating a product, you'll create a new workflow that deletes the product from Algolia, then create a subscriber that listens to the `product.deleted` event to trigger the workflow. - -#### Create Delete Product Step - -The workflow to delete a product from Algolia will have only one step that deletes products by their IDs from Algolia. - -So, create the step at `src/workflows/steps/delete-products-from-algolia.ts` with the following content: - -```ts title="src/workflows/steps/delete-products-from-algolia.ts" -import { - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { ALGOLIA_MODULE } from "../../modules/algolia" - -export type DeleteProductsFromAlgoliaWorkflow = { - ids: string[] -} - -export const deleteProductsFromAlgoliaStep = createStep( - "delete-products-from-algolia-step", - async ( - { ids }: DeleteProductsFromAlgoliaWorkflow, - { container } - ) => { - const algoliaModuleService = container.resolve(ALGOLIA_MODULE) - - const existingRecords = await algoliaModuleService.retrieveFromIndex( - ids, - "product" - ) - await algoliaModuleService.deleteFromIndex( - ids, - "product" - ) - - return new StepResponse(undefined, existingRecords) - }, - async (existingRecords, { container }) => { - if (!existingRecords) { - return - } - const algoliaModuleService = container.resolve(ALGOLIA_MODULE) - - await algoliaModuleService.indexData( - existingRecords as unknown as Record[], - "product" - ) - } -) -``` - -The step receives the IDs of the products to delete as an input. - -In the step, you resolve the Algolia Module's service and retrieve the existing records from Algolia. This is useful to revert the deletion if an error occurs. - -Then, you delete the products from Algolia and pass the existing records to the compensation function. - -In the compensation function, you reindex the existing records if an error occurs. - -#### Create Delete Product Workflow - -You can now create the workflow that deletes products from Algolia. Create the file `src/workflows/delete-products-from-algolia.ts` with the following content: - -```ts title="src/workflows/delete-products-from-algolia.ts" -import { createWorkflow } from "@medusajs/framework/workflows-sdk" -import { deleteProductsFromAlgoliaStep } from "./steps/delete-products-from-algolia" - -type DeleteProductsFromAlgoliaWorkflowInput = { - ids: string[] -} - -export const deleteProductsFromAlgoliaWorkflow = createWorkflow( - "delete-products-from-algolia", - (input: DeleteProductsFromAlgoliaWorkflowInput) => { - deleteProductsFromAlgoliaStep(input) - } -) -``` - -The workflow receives an object with the IDs of the products to delete. It then executes the `deleteProductsFromAlgoliaStep` to delete the products from Algolia. - -#### Create Delete Product Subscriber - -Finally, you'll create the subscriber that listens to the `product.deleted` event to trigger the above workflow. - -Create the file `src/subscribers/product-delete.ts` with the following content: - -```ts title="src/subscribers/product-delete.ts" -import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" -import { deleteProductsFromAlgoliaWorkflow } from "../workflows/delete-products-from-algolia" - -export default async function handleProductDeleted({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - await deleteProductsFromAlgoliaWorkflow(container) - .run({ - input: { - ids: [data.id], - }, - }) -} - -export const config: SubscriberConfig = { - event: "product.deleted", -} -``` - -The subscriber listens to the `product.deleted` event. When the event is emitted, the subscriber triggers the `deleteProductsFromAlgoliaWorkflow`, passing it the ID of the product to delete. - -#### Test it Out - -To test product deletion, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, delete a product from the Medusa Admin dashboard. If you check the Algolia dashboard, you'll find that the product was deleted there as well. - -*** - -## Step 6: Add Search API Route - -Before customizing the storefront to show the search UI, you'll create an API route in your Medusa application that allows storefronts to search products in Algolia. - -While you can implement the search functionality directly in the storefront to interact with Algolia, this approach centralizes your search integration in Medusa, allowing you to change or modify the integration as necessary. You can also rely on the same behavior and results across different storefronts. - -To implement the API Route, create the file `src/api/store/products/search/route.ts` with the following content: - -```ts title="src/api/store/products/search/route.ts" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { ALGOLIA_MODULE } from "../../../../modules/algolia" -import AlgoliaModuleService from "../../../../modules/algolia/service" -import { z } from "zod" - -export const SearchSchema = z.object({ - query: z.string(), -}) - -type SearchRequest = z.infer - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - const algoliaModuleService: AlgoliaModuleService = req.scope.resolve(ALGOLIA_MODULE) - - const { query } = req.validatedBody - - const results = await algoliaModuleService.search( - query as string - ) - - res.json(results) -} -``` - -You first define a schema with [Zod](https://zod.dev/), a library to define validation schemas. The schema defines the structure of the request body, which in this case is an object with a `query` property of type `string`. Later, you'll use the schema to enforce request body validation. - -Then, you expose a `POST` API Route at `/store/search` that searches Algolia using the `search` method you implemented in the Algolia Module's service. You pass to it the query string from the request body. - -Finally, you return the search results as it is in the response. - -### Add Validation Middleware - -To ensure that requests sent to the API route have the required request body parameters, you can use a [middleware](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md). A middleware is a function executed when a request is sent to an API Route. It's executed before the route handler. - -Learn more about middleware in the [Middlewares documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md). - -Middlewares are created in the `src/api/middlewares.ts` file. So create the file `src/api/middlewares.ts` with the following content: - -```ts title="src/api/middlewares.ts" -import { - defineMiddlewares, - validateAndTransformBody, -} from "@medusajs/framework/http" -import { SearchSchema } from "./store/products/search/route" - -export default defineMiddlewares({ - routes: [ - { - matcher: "/store/products/search", - method: ["POST"], - middlewares: [ - validateAndTransformBody(SearchSchema), - ], - }, - ], -}) -``` - -To export the middlewares, you use the `defineMiddlewares` function. It accepts an object having a `routes` property, whose value is an array of middleware route objects. Each middleware route object has the following properties: - -- `matcher`: The path of the route the middleware applies to. -- `method`: The HTTP methods the middleware applies to, which is in this case `POST`. -- `middlewares`: An array of middleware functions to apply to the route. You apply the `validateAndTransformBody` middleware which ensures that a request's body has the required parameters. You pass it the schema you defined earlier in the API route's file. - -You can use the search API route now. You'll see it in action as you customize the storefront in the next step. - -*** - -## Step 7: Search Products in Next.js Storefront - -The last step is to provide the search functionalities to customers on your storefront. In the first step, you installed the [Next.js Starter Storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md) along with the Medusa application. - -In this step, you'll customize the Next.js Starter Storefront to add the search functionality. - -The Next.js Starter Storefront was installed in a separate directory from Medusa. The directory's name is `{your-project}-storefront`. - -So, if your Medusa application's directory is `medusa-search`, you can find the storefront by going back to the parent directory and changing to the `medusa-search-storefront` directory: - -```bash -cd ../medusa-search-storefront # change based on your project name -``` - -### Install Algolia Packages - -Before adding the implementation of the search functionality, you need to install the Algolia packages necessary to add the search functionality in your storefront. - -Run the following command in the directory of your Next.js Starter Storefront: - -```bash npm2yarn -npm install algoliasearch react-instantsearch -``` - -This installs the Algolia Search JavaScript client and the React InstantSearch library, which you'll use to build the search functionality. - -### Add Search Client Configuration - -Next, you need to configure the search client. Not only do you need to initialize Algolia, but you also need to change the searching mechanism to use your custom API route in the Medusa application instead of Algolia's API directly. - -In `src/lib/config.ts`, add the following imports at the top of the file: - -```ts title="src/lib/config.ts" badgeLabel="Storefront" badgeColor="blue" -import { - liteClient as algoliasearch, - LiteClient as SearchClient, -} from "algoliasearch/lite" -``` - -Then, add the following at the end of the file: - -```ts title="src/lib/config.ts" badgeLabel="Storefront" badgeColor="blue" -export const searchClient: SearchClient = { - ...(algoliasearch( - process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || "", - process.env.NEXT_PUBLIC_ALGOLIA_API_KEY || "" - )), - search: async (params) => { - const request = Array.isArray(params) ? params[0] : params - const query = "params" in request ? request.params?.query : - "query" in request ? request.query : "" - - if (!query) { return { - results: [ - { - hits: [], - nbHits: 0, - nbPages: 0, - page: 0, - hitsPerPage: 0, - processingTimeMS: 0, - query: "", - params: "", - }, - ], + title: child.name, + sku: child.sku, + options: childOptions, + prices: data.stores[0].supported_currencies.map(({ currency_code }) => { + return { + amount: child.price, + currency_code, + } + }), + metadata: { + external_id: variantExternalId, + }, + id: existingVariant?.id, } - } - - return await sdk.client.fetch(`/store/products/search`, { - method: "POST", - body: { - query, - }, }) + + productData.images = magentoProduct.media_gallery_entries.filter((entry) => !entry.types.includes("thumbnail")).map((entry) => { + return { + url: entry.file, + metadata: { + external_id: entry.id.toString(), + }, + } + }) + + if (productData.id) { + productsToUpdate.set(existingProduct.id, productData) + } else { + productsToCreate.set(productData.external_id!, productData) + } + }) + + return { + productsToCreate: Array.from(productsToCreate.values()), + productsToUpdate: Array.from(productsToUpdate.values()), + } +}) + +// TODO create and update products +``` + +You use `transform` again to prepare the data to create and update the products in the Medusa application. For each Magento product, you map its equivalent Medusa product's data: + +- You set the product's general details, such as the title, description, status, handle, external ID, and thumbnail using the Magento product's data and custom attributes. +- You associate the product with the default sales channel and shipping profile retrieved previously. +- You map the Magento product's configurable product options to Medusa product options. In Medusa, a product's option has a label, such as "Color", and values, such as "Red". To map the option values, you use the attributes retrieved from Magento. +- You map the Magento product's children to Medusa product variants. For the variant options, you pass an object whose keys is the option's label, such as "Color", and values is the option's value, such as "Red". For the prices, you set the variant's price based on the Magento child's price for every supported currency in the Medusa store. Also, you set the Magento child product's ID in the Medusa variant's `metadata.external_id` property. +- You map the Magento product's media gallery entries to Medusa product images. You filter out the thumbnail image and set the URL and the Magento image's ID in the Medusa image's `metadata.external_id` property. + +In addition, you use the existing products retrieved in the previous step to determine whether a product should be created or updated. If there's an existing product whose `external_id` matches the ID of the magento product, you set the existing product's ID in the `id` property of the product to be updated. You also do the same for its variants. + +Finally, you return the products to create and update. + +The last steps of the workflow is to create and update the products. Replace the `TODO` in the workflow function with the following: + +```ts title="src/workflows/migrate-products-from-magento.ts" +createProductsWorkflow.runAsStep({ + input: { + products: productsToCreate, }, +}) + +updateProductsWorkflow.runAsStep({ + input: { + products: productsToUpdate, + }, +}) + +return new WorkflowResponse(pagination) +``` + +You use the `createProductsWorkflow` and `updateProductsWorkflow` workflows from Medusa's `@medusajs/medusa/core-flows` package to create and update the products in the Medusa application. + +Workflows must return an instance of `WorkflowResponse`, passing as a parameter the data to return to the workflow's executor. This workflow returns the pagination parameters, allowing you to paginate the product migration process. + +You can now use this workflow to migrate products from Magento to Medusa. You'll learn how to use it in the next steps. + +*** + +## Step 6: Schedule Product Migration + +There are many ways to execute tasks asynchronously in Medusa, such as [scheduling a job](https://docs.medusajs.com/docs/learn/fundamentals/scheduled-jobs/index.html.md) or [handling emitted events](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). + +In this guide, you'll learn how to schedule the product migration at a specified interval using a scheduled job. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. + +Refer to the [Scheduled Jobs](https://docs.medusajs.com/docs/learn/fundamentals/scheduled-jobs/index.html.md) documentation to learn more about scheduled jobs. + +To create a scheduled job, in your plugin, create the file `src/jobs/migrate-magento.ts` with the following content: + +![Diagram showcasing the migrate-magento file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739358924/Medusa%20Resources/magento-7_rqoodo.jpg) + +```ts title="src/jobs/migrate-magento.ts" +import { MedusaContainer } from "@medusajs/framework/types" +import { migrateProductsFromMagentoWorkflow } from "../workflows" + +export default async function migrateMagentoJob( + container: MedusaContainer +) { + const logger = container.resolve("logger") + logger.info("Migrating products from Magento...") + + let currentPage = 0 + const pageSize = 100 + let totalCount = 0 + + do { + currentPage++ + + const { + result: pagination, + } = await migrateProductsFromMagentoWorkflow(container).run({ + input: { + currentPage, + pageSize, + }, + }) + + totalCount = pagination.total_count + } while (currentPage * pageSize < totalCount) + + logger.info("Finished migrating products from Magento") +} + +export const config = { + name: "migrate-magento-job", + schedule: "0 0 * * *", } ``` -In the code above, you create a `searchClient` object that initializes the Algolia client with your Algolia App ID and API Key. +A scheduled job file must export: -You also define a `search` method that sends a `POST` request to the search API route you created in the Medusa application. You use the JS SDK (which is initialized in this same file) to send the request. +- An asynchronous function that executes the job's logic. The function receives the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md) as a parameter. +- An object with the job's configuration, including the name and the schedule. The schedule is a cron job pattern as a string. -### Set Environment Variables +In the job function, you resolve the [logger](https://docs.medusajs.com/docs/learn/debugging-and-testing/logging/index.html.md) from the container to log messages. Then, you paginate the product migration process by running the `migrateProductsFromMagentoWorkflow` workflow at each page until you've migrated all products. You use the pagination result returned by the workflow to determine whether there are more products to migrate. -In the storefront's `.env.local` file, add the following Algolia-related environment variables: - -```plain badgeLabel="Storefront" badgeColor="blue" -NEXT_PUBLIC_ALGOLIA_APP_ID=your_algolia_app_id -NEXT_PUBLIC_ALGOLIA_API_KEY=your_algolia_api_key -NEXT_PUBLIC_ALGOLIA_PRODUCT_INDEX_NAME=your-products-index-name -``` - -Where: - -- `your_algolia_app_id` is your Algolia App ID, as explained in the [Add Environment Variables section](#add-environment-variables) earlier. -- `your_algolia_api_key` is your Algolia Search API key. You can retrieve it from the [same API keys page on the Algolia dashboard](#add-environment-variables) that you retrieved the Admin API key from. -- `your-products-index-name` is the name of the index you created in Algolia to store the products, which you can retrieve as explained in the [Add Environment Variables section](#add-environment-variables) earlier. You'll use this variable later. - -Do not expose your Admin API key in the storefront. The Admin API key should only be used in the Medusa application to interact with Algolia, as it has full access to your Algolia account. - -### Add Search Modal Component - -You'll now add a search modal component that customers can use to search for products. The search modal will display the search results in real-time as the customer types in the search query. - -Later, you'll add the search modal to the navigation bar, allowing customers to open the search modal from any page. - -Create the file `src/modules/search/components/modal/index.tsx` with the following content: - -```tsx title="src/modules/search/components/modal/index.tsx" badgeLabel="Storefront" badgeColor="blue" -"use client" - -import React, { useEffect, useState } from "react" -import { Hits, InstantSearch, SearchBox } from "react-instantsearch" -import { searchClient } from "../../../../lib/config" -import Modal from "../../../common/components/modal" -import { Button } from "@medusajs/ui" -import Image from "next/image" -import Link from "next/link" -import { usePathname } from "next/navigation" - -type Hit = { - objectID: string; - id: string; - title: string; - description: string; - handle: string; - thumbnail: string; -} - -export default function SearchModal() { - const [isOpen, setIsOpen] = useState(false) - const pathname = usePathname() - - useEffect(() => { - setIsOpen(false) - }, [pathname]) - - return ( - <> -
- -
- setIsOpen(false)}> - - - - - - - ) -} - -const Hit = ({ hit }: { hit: Hit }) => { - return ( -
- {hit.title} -
-

{hit.title}

-

{hit.description}

-
- -
- ) -} -``` - -You create a `SearchModal` component that displays a search box and the search results using widgets from Algolia's `react-instantsearch` library. - -To display each result item (or hit), you create a `Hit` component that displays the product's title, description, and thumbnail. You also add a link to the product's page. - -Finally, you show the search modal when the customer clicks a "Search" button, which you'll add to the navigation bar next. - -### Add Search Button to Navigation Bar - -The last step is to show the search button in the navigation bar. - -In `src/modules/layout/templates/nav/index.tsx`, add the following imports at the top of the file: - -```tsx title="src/modules/layout/templates/nav/index.tsx" badgeLabel="Storefront" badgeColor="blue" -import SearchModal from "@modules/search/components/modal" -``` - -Then, in the return statement of the `Nav` component, add the `SearchModal` component before the `div` surrounding the "Account" link: - -```tsx title="src/modules/layout/templates/nav/index.tsx" badgeLabel="Storefront" badgeColor="blue" - -``` - -The search button will now appear in the navigation bar before the Account link. +Based on the job's configurations, the Medusa application will run the job at midnight every day. ### Test it Out -To test out the storefront changes and the search API route, start the Medusa application: +To test out this scheduled job, first, change the configuration to run the job every minute: + +```ts title="src/jobs/migrate-magento.ts" +export const config = { + // ... + schedule: "* * * * *", +} +``` + +Then, make sure to run the `plugin:develop` command in the plugin if you haven't already: + +```bash +npx medusa plugin:develop +``` + +This ensures that the plugin's latest changes are reflected in the Medusa application. + +Finally, start the Medusa application that the plugin is installed in: ```bash npm2yarn npm run dev ``` -Then, start the Next.js Starter Storefront from its directory: +After a minute, you'll see a message in the terminal indicating that the migration started: -```bash npm2yarn -npm run dev +```plain title="Terminal" +info: Migrating products from Magento... ``` -Next, go to `localhost:8000`. You'll find a Search button at the top right of the navigation bar. If you click on it, you can search through your products. You can also click on a product to view its page. +Once the migration is done, you'll see the following message: -![The Next.js Starter Storefront showing the search modal with search results](https://res.cloudinary.com/dza7lstvk/image/upload/v1742827777/Medusa%20Resources/Screenshot_2025-03-24_at_4.49.23_PM_kzhldx.png) +```plain title="Terminal" +info: Finished migrating products from Magento +``` + +To confirm that the products were migrated, open the Medusa Admin dashboard at `http://localhost:9000/app` and log in. Then, click on Products in the sidebar. You'll see your magento products in the list of products. + +![Click on products at the sidebar on the right, then view the products in the table in the middle.](https://res.cloudinary.com/dza7lstvk/image/upload/v1739359394/Medusa%20Resources/Screenshot_2025-02-12_at_1.22.44_PM_uva98i.png) *** ## Next Steps -You've now integrated Algolia with Medusa and added search functionality to your storefront. You can expand on these features to: +You've now implemented the logic to migrate products from Magento to Medusa. You can re-use the plugin across Medusa applications. You can also expand on the plugin to: -- Add filters to the search results. You can do that using Algolia's [widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/react/) and customizing the search API route in Medusa to accept filter parameters. -- Support indexing other data types, such as product categories. You can create the subscribers and workflows for categories similar to products. +- Migrate other entities, such as orders, customers, and categories. Migrating other entities follows the same pattern as migrating products, using workflows and scheduled jobs. You only need to format the data to be migrated as needed. +- Allow triggering migrations from the Medusa Admin dashboard using [Admin Customizations](https://docs.medusajs.com/docs/learn/fundamentals/admin/index.html.md). This feature is available in the [Example Repository](https://github.com/medusajs/example-repository/tree/main/src/admin). If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth learning of all the concepts you've used in this guide and more. @@ -50414,7 +51462,7 @@ If you check the inbox of the email address you specified in the shipping addres ## Next Steps -You've now integrated Medusa with Resend. You can add more templates for other emails, such as customer registration confirmation, user invites, and more. Check out the [Events Reference](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/events-reference/index.html.md) for a list of all events that the Medusa application emits. +You've now integrated Medusa with Resend. You can add more templates for other emails, such as customer registration confirmation, user invites, and more. Check out the [Events Reference](https://docs.medusajs.com/references/events/index.html.md) for a list of all events that the Medusa application emits. If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth learning of all the concepts you've used in this guide and more. @@ -51238,7 +52286,7 @@ A subscriber is an asynchronous function that listens to one or more events. The Subscribers are useful when you want to perform an action that isn't an integral part of a flow, but as a reaction to a performed action. In this case, syncing the products to Sanity isn't integral to creating a product, so you do it in a subscriber after the product is created. -Learn more about events and subscribers in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). You can also find the list of emitted events in [this reference](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/events-reference/index.html.md). +Learn more about events and subscribers in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). You can also find the list of emitted events in [this reference](https://docs.medusajs.com/references/events/index.html.md). So, to run the workflow you defined in the previous event when a product is created or updated, you'll create a subscriber that listens to the `product.created` and `product.updated` events. @@ -53474,1054 +54522,6 @@ If you're new to Medusa, check out the [main documentation](https://docs.medusaj To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md). -# How to Build Magento Data Migration Plugin - -In this tutorial, you'll learn how to build a [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) that migrates data, such as products, from Magento to Medusa. - -Magento is known for its customization capabilities. However, its monolithic architecture imposes limitations on business requirements, often forcing development teams to implement hacky workarounds. Over time, these customizations become challenging to maintain, especially as the business scales, leading to increased technical debt and slower feature delivery. - -Medusa's modular architecture allows you to build a custom digital commerce platform that meets your business requirements without the limitations of a monolithic system. By migrating from Magento to Medusa, you can take advantage of Medusa's modern technology stack to build a scalable and flexible commerce platform that grows with your business. - -By following this tutorial, you'll create a Medusa plugin that migrates data from a Magento server to a Medusa application in minimal time. You can re-use this plugin across multiple Medusa applications, allowing you to adopt Medusa across your projects. - -## Summary - -### Prerequisites - - - -This tutorial will teach you how to: - -- Install and set up a Medusa application project. -- Install and set up a Medusa plugin. -- Implement a Magento Module in the plugin to connect to Magento's APIs and retrieve products. - - This guide will only focus on migrating product data from Magento to Medusa. You can extend the implementation to migrate other data, such as customers, orders, and more. -- Trigger data migration from Magento to Medusa in a scheduled job. - -You can follow this tutorial whether you're new to Medusa or an advanced Medusa developer. - -![Diagram showcasing the flow of migrating data from Magento to Medusa](https://res.cloudinary.com/dza7lstvk/image/upload/v1739360550/Medusa%20Resources/magento-summary_hsewci.jpg) - -[Example Repository](https://github.com/medusajs/examples/tree/main/migrate-from-magento): Find the full code of the guide in this repository. The repository also includes additional features, such as triggering migrations from the Medusa Admin dashboard. - -*** - -## Step 1: Install a Medusa Application - -You'll first install a Medusa application that exposes core commerce features through REST APIs. You'll later install the Magento plugin in this application to test it out. - -### Prerequisites - -- [Node.js v20+](https://nodejs.org/en/download) -- [Git CLI tool](https://git-scm.com/downloads) -- [PostgreSQL](https://www.postgresql.org/download/) - -Start by installing the Medusa application on your machine with the following command: - -```bash -npx create-medusa-app@latest -``` - -You'll be asked for the project's name. You can also optionally choose to install the [Next.js starter storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md). - -Afterward, the installation process will start, which will install the Medusa application in a directory with your project's name. If you chose to install the Next.js starter, it'll be installed in a separate directory with the `{project-name}-storefront` name. - -The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Refer to the [Medusa Architecture](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md) documentation to learn more. - -Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterward, you can log in with the new user and explore the dashboard. - -Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help. - -*** - -## Step 2: Install a Medusa Plugin Project - -A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. You can add in the plugin [API Routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md), [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), and other customizations, as you'll see in this guide. Afterward, you can test it out locally in a Medusa application, then publish it to npm to install and use it in any Medusa application. - -Refer to the [Plugins](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) documentation to learn more about plugins. - -A Medusa plugin is set up in a different project, giving you the flexibility in building and publishing it, while providing you with the tools to test it out locally in a Medusa application. - -To create a new Medusa plugin project, run the following command in a directory different than that of the Medusa application: - -```bash npm2yarn -npx create-medusa-app@latest medusa-plugin-magento --plugin -``` - -Where `medusa-plugin-magento` is the name of the plugin's directory and the name set in the plugin's `package.json`. So, if you wish to publish it to NPM later under a different name, you can change it here in the command or later in `package.json`. - -Once the installation process is done, a new directory named `medusa-plugin-magento` will be created with the plugin project files. - -![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg) - -*** - -## Step 3: Set up Plugin in Medusa Application - -Before you start your development, you'll set up the plugin in the Medusa application you installed in the first step. This will allow you to test the plugin during your development process. - -In the plugin's directory, run the following command to publish the plugin to the local package registry: - -```bash title="Plugin project" -npx medusa plugin:publish -``` - -This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`. - -Next, you'll install the plugin in the Medusa application from the local registry. - -If you've installed your Medusa project before v2.3.1, you must install [yalc](https://github.com/wclr/yalc) as a development dependency first. - -Run the following command in the Medusa application's directory to install the plugin: - -```bash title="Medusa application" -npx medusa plugin:add medusa-plugin-magento -``` - -This command installs the plugin in the Medusa application from the local package registry. - -Next, register the plugin in the `medusa-config.ts` file of the Medusa application: - -```ts title="medusa-config.ts" -module.exports = defineConfig({ - // ... - plugins: [ - { - resolve: "medusa-plugin-magento", - options: { - // TODO add options - }, - }, - ], -}) -``` - -You add the plugin to the array of plugins. Later, you'll pass options useful to retrieve data from Magento. - -Finally, to ensure your plugin's changes are constantly published to the local registry, simplifying your testing process, keep the following command running in the plugin project during development: - -```bash title="Plugin project" -npx medusa plugin:develop -``` - -*** - -## Step 4: Implement Magento Module - -To connect to external applications in Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup. - -In this step, you'll create a Magento Module in the Magento plugin that connects to a Magento server's REST APIs and retrieves data, such as products. - -Refer to the [Modules](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) documentation to learn more about modules. - -### Create Module Directory - -A module is created under the `src/modules` directory of your plugin. So, create the directory `src/modules/magento`. - -![Diagram showcasing the module directory to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739272368/magento-1_ikev4x.jpg) - -### Create Module's Service - -You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to external systems or the database, which is useful if your module defines tables in the database. - -In this section, you'll create the Magento Module's service that connects to Magento's REST APIs and retrieves data. - -Start by creating the file `src/modules/magento/service.ts` in the plugin with the following content: - -![Diagram showcasing the service file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739272483/magento-2_ajetpr.jpg) - -```ts title="src/modules/magento/service.ts" -type Options = { - baseUrl: string - storeCode?: string - username: string - password: string - migrationOptions?: { - imageBaseUrl?: string - } -} - -export default class MagentoModuleService { - private options: Options - - constructor({}, options: Options) { - this.options = { - ...options, - storeCode: options.storeCode || "default", - } - } -} -``` - -You create a `MagentoModuleService` that has an `options` property to store the module's options. These options include: - -- `baseUrl`: The base URL of the Magento server. -- `storeCode`: The store code of the Magento store, which is `default` by default. -- `username`: The username of a Magento admin user to authenticate with the Magento server. -- `password`: The password of the Magento admin user. -- `migrationOptions`: Additional options useful for migrating data, such as the base URL to use for product images. - -The service's constructor accepts as a first parameter the [Module Container](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md), which allows you to access resources available for the module. As a second parameter, it accepts the module's options. - -### Add Authentication Logic - -To authenticate with the Magento server, you'll add a method to the service that retrieves an access token from Magento using the username and password in the options. This access token is used in subsequent requests to the Magento server. - -First, add the following property to the `MagentoModuleService` class: - -```ts title="src/modules/magento/service.ts" -export default class MagentoModuleService { - private accessToken: { - token: string - expiresAt: Date - } - // ... -} -``` - -You add an `accessToken` property to store the access token and its expiration date. The access token Magento returns expires after four hours, so you store the expiration date to know when to refresh the token. - -Next, add the following `authenticate` method to the `MagentoModuleService` class: - -```ts title="src/modules/magento/service.ts" -import { MedusaError } from "@medusajs/framework/utils" - -export default class MagentoModuleService { - // ... - async authenticate() { - const response = await fetch(`${this.options.baseUrl}/rest/${this.options.storeCode}/V1/integration/admin/token`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ username: this.options.username, password: this.options.password }), - }) - - const token = await response.text() - - if (!response.ok) { - throw new MedusaError(MedusaError.Types.UNAUTHORIZED, `Failed to authenticate with Magento: ${token}`) - } - - this.accessToken = { - token: token.replaceAll("\"", ""), - expiresAt: new Date(Date.now() + 4 * 60 * 60 * 1000), // 4 hours in milliseconds - } - } -} -``` - -You create an `authenticate` method that sends a POST request to the Magento server's `/rest/{storeCode}/V1/integration/admin/token` endpoint, passing the username and password in the request body. - -If the request is successful, you store the access token and its expiration date in the `accessToken` property. If the request fails, you throw a `MedusaError` with the error message returned by Magento. - -Lastly, add an `isAccessTokenExpired` method that checks if the access token has expired: - -```ts title="src/modules/magento/service.ts" -export default class MagentoModuleService { - // ... - async isAccessTokenExpired(): Promise { - return !this.accessToken || this.accessToken.expiresAt < new Date() - } -} -``` - -In the `isAccessTokenExpired` method, you return a boolean indicating whether the access token has expired. You'll use this in later methods to check if you need to refresh the access token. - -### Retrieve Products from Magento - -Next, you'll add a method that retrieves products from Magento. Due to limitations in Magento's API that makes it difficult to differentiate between simple products that don't belong to a configurable product and those that do, you'll only retrieve configurable products and their children. You'll also retrieve the configurable attributes of the product, such as color and size. - -First, you'll add some types to represent a Magento product and its attributes. Create the file `src/modules/magento/types.ts` in the plugin with the following content: - -![Diagram showcasing the types file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739346287/Medusa%20Resources/magento-3_fpghog.jpg) - -```ts title="src/modules/magento/types.ts" -export type MagentoProduct = { - id: number - sku: string - name: string - price: number - status: number - // not handling other types - type_id: "simple" | "configurable" - created_at: string - updated_at: string - extension_attributes: { - category_links: { - category_id: string - }[] - configurable_product_links?: number[] - configurable_product_options?: { - id: number - attribute_id: string - label: string - position: number - values: { - value_index: number - }[] - }[] - } - media_gallery_entries: { - id: number - media_type: string - label: string - position: number - disabled: boolean - types: string[] - file: string - }[] - custom_attributes: { - attribute_code: string - value: string - }[] - // added by module - children?: MagentoProduct[] -} - -export type MagentoAttribute = { - attribute_code: string - attribute_id: number - default_frontend_label: string - options: { - label: string - value: string - }[] -} - -export type MagentoPagination = { - search_criteria: { - filter_groups: [], - page_size: number - current_page: number - } - total_count: number -} - -export type MagentoPaginatedResponse = { - items: TData[] -} & MagentoPagination -``` - -You define the following types: - -- `MagentoProduct`: Represents a product in Magento. -- `MagentoAttribute`: Represents an attribute in Magento. -- `MagentoPagination`: Represents the pagination information returned by Magento's API. -- `MagentoPaginatedResponse`: Represents a paginated response from Magento's API for a specific item type, such as products. - -Next, add the `getProducts` method to the `MagentoModuleService` class: - -```ts title="src/modules/magento/service.ts" -export default class MagentoModuleService { - // ... - async getProducts(options?: { - currentPage?: number - pageSize?: number - }): Promise<{ - products: MagentoProduct[] - attributes: MagentoAttribute[] - pagination: MagentoPagination - }> { - const { currentPage = 1, pageSize = 100 } = options || {} - const getAccessToken = await this.isAccessTokenExpired() - if (getAccessToken) { - await this.authenticate() - } - - // TODO prepare query params - } -} -``` - -The `getProducts` method receives an optional `options` object with the `currentPage` and `pageSize` properties. So far, you check if the access token has expired and, if so, retrieve a new one using the `authenticate` method. - -Next, you'll prepare the query parameters to pass in the request that retrieves products. Replace the `TODO` with the following: - -```ts title="src/modules/magento/service.ts" -const searchQuery = new URLSearchParams() -// pass pagination parameters -searchQuery.append( - "searchCriteria[currentPage]", - currentPage?.toString() || "1" -) -searchQuery.append( - "searchCriteria[pageSize]", - pageSize?.toString() || "100" -) - -// retrieve only configurable products -searchQuery.append( - "searchCriteria[filter_groups][1][filters][0][field]", - "type_id" -) -searchQuery.append( - "searchCriteria[filter_groups][1][filters][0][value]", - "configurable" -) -searchQuery.append( - "searchCriteria[filter_groups][1][filters][0][condition_type]", - "in" -) - -// TODO send request to retrieve products -``` - -You create a `searchQuery` object to store the query parameters to pass in the request. Then, you add the pagination parameters and the filter to retrieve only configurable products. - -Next, you'll send the request to retrieve products from Magento. Replace the `TODO` with the following: - -```ts title="src/modules/magento/service.ts" -const { items: products, ...pagination }: MagentoPaginatedResponse = await fetch( - `${this.options.baseUrl}/rest/${this.options.storeCode}/V1/products?${searchQuery}`, - { - headers: { - "Authorization": `Bearer ${this.accessToken.token}`, - }, - } -).then((res) => res.json()) -.catch((err) => { - console.log(err) - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Failed to get products from Magento: ${err.message}` - ) -}) - -// TODO prepare products -``` - -You send a `GET` request to the Magento server's `/rest/{storeCode}/V1/products` endpoint, passing the query parameters in the URL. You also pass the access token in the `Authorization` header. - -Next, you'll prepare the retrieved products by retrieving their children, configurable attributes, and modifying their image URLs. Replace the `TODO` with the following: - -```ts title="src/modules/magento/service.ts" -const attributeIds: string[] = [] - -await promiseAll( - products.map(async (product) => { - // retrieve its children - product.children = await fetch( - `${this.options.baseUrl}/rest/${this.options.storeCode}/V1/configurable-products/${product.sku}/children`, - { - headers: { - "Authorization": `Bearer ${this.accessToken.token}`, - }, - } - ).then((res) => res.json()) - .catch((err) => { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Failed to get product children from Magento: ${err.message}` - ) - }) - - product.media_gallery_entries = product.media_gallery_entries.map( - (entry) => ({ - ...entry, - file: `${this.options.migrationOptions?.imageBaseUrl}${entry.file}`, - } - )) - - attributeIds.push(...( - product.extension_attributes.configurable_product_options?.map( - (option) => option.attribute_id) || [] - ) - ) - }) -) - -// TODO retrieve attributes -``` - -You loop over the retrieved products and retrieve their children using the `/rest/{storeCode}/V1/configurable-products/{sku}/children` endpoint. You also modify the image URLs to use the base URL in the migration options, if provided. - -In addition, you store the IDs of the configurable products' attributes in the `attributeIds` array. You'll add a method that retrieves these attributes. - -Add the new method `getAttributes` to the `MagentoModuleService` class: - -```ts title="src/modules/magento/service.ts" -export default class MagentoModuleService { - // ... - async getAttributes({ - ids, - }: { - ids: string[] - }): Promise { - const getAccessToken = await this.isAccessTokenExpired() - if (getAccessToken) { - await this.authenticate() - } - - // filter by attribute IDs - const searchQuery = new URLSearchParams() - searchQuery.append( - "searchCriteria[filter_groups][0][filters][0][field]", - "attribute_id" - ) - searchQuery.append( - "searchCriteria[filter_groups][0][filters][0][value]", - ids.join(",") - ) - searchQuery.append( - "searchCriteria[filter_groups][0][filters][0][condition_type]", - "in" - ) - - const { - items: attributes, - }: MagentoPaginatedResponse = await fetch( - `${this.options.baseUrl}/rest/${this.options.storeCode}/V1/products/attributes?${searchQuery}`, - { - headers: { - "Authorization": `Bearer ${this.accessToken.token}`, - }, - } - ).then((res) => res.json()) - .catch((err) => { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Failed to get attributes from Magento: ${err.message}` - ) - }) - - return attributes - } -} -``` - -The `getAttributes` method receives an object with the `ids` property, which is an array of attribute IDs. You check if the access token has expired and, if so, retrieve a new one using the `authenticate` method. - -Next, you prepare the query parameters to pass in the request to retrieve attributes. You send a `GET` request to the Magento server's `/rest/{storeCode}/V1/products/attributes` endpoint, passing the query parameters in the URL. You also pass the access token in the `Authorization` header. - -Finally, you return the retrieved attributes. - -Now, go back to the `getProducts` method and replace the `TODO` with the following: - -```ts title="src/modules/magento/service.ts" -const attributes = await this.getAttributes({ ids: attributeIds }) - -return { products, attributes, pagination } -``` - -You retrieve the configurable products' attributes using the `getAttributes` method and return the products, attributes, and pagination information. - -You'll use this method in a later step to retrieve products from Magento. - -### Export Module Definition - -The final piece to a module is its definition, which you export in an `index.ts` file at its root directory. This definition tells Medusa the name of the module and its service. - -So, create the file `src/modules/magento/index.ts` with the following content: - -![Diagram showcasing the module definition file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739348316/Medusa%20Resources/magento-4_bmepvh.jpg) - -```ts title="src/modules/magento/index.ts" -import { Module } from "@medusajs/framework/utils" -import MagentoModuleService from "./service" - -export const MAGENTO_MODULE = "magento" - -export default Module(MAGENTO_MODULE, { - service: MagentoModuleService, -}) -``` - -You use the `Module` function from the Modules SDK to create the module's definition. It accepts two parameters: - -1. The module's name, which is `magento`. -2. An object with a required property `service` indicating the module's service. - -You'll later use the module's service to retrieve products from Magento. - -### Pass Options to Plugin - -As mentioned earlier when you registered the plugin in the Medusa Application's `medusa-config.ts` file, you can pass options to the plugin. These options are then passed to the modules in the plugin. - -So, add the following options to the plugin's registration in the `medusa-config.ts` file of the Medusa application: - -```ts title="medusa-config.ts" -module.exports = defineConfig({ - // ... - plugins: [ - { - resolve: "medusa-plugin-magento", - options: { - baseUrl: process.env.MAGENTO_BASE_URL, - username: process.env.MAGENTO_USERNAME, - password: process.env.MAGENTO_PASSWORD, - migrationOptions: { - imageBaseUrl: process.env.MAGENTO_IMAGE_BASE_URL, - }, - }, - }, - ], -}) -``` - -You pass the options that you defined in the `MagentoModuleService`. Make sure to also set their environment variables in the `.env` file: - -```bash -MAGENTO_BASE_URL=https://magento.example.com -MAGENTO_USERNAME=admin -MAGENTO_PASSWORD=password -MAGENTO_IMAGE_BASE_URL=https://magento.example.com/pub/media/catalog/product -``` - -Where: - -- `MAGENTO_BASE_URL`: The base URL of the Magento server. It can also be a local URL, such as `http://localhost:8080`. -- `MAGENTO_USERNAME`: The username of a Magento admin user to authenticate with the Magento server. -- `MAGENTO_PASSWORD`: The password of the Magento admin user. -- `MAGENTO_IMAGE_BASE_URL`: The base URL to use for product images. Magento stores product images in the `pub/media/catalog/product` directory, so you can reference them directly or use a CDN URL. If the URLs of product images in the Medusa server already have a different base URL, you can omit this option. - -Medusa supports integrating third-party services, such as [S3](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/file/s3/index.html.md), in a File Module Provider. Refer to the [File Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/file/index.html.md) documentation to find other module providers and how to create a custom provider. - -You can now use the Magento Module to migrate data, which you'll do in the next steps. - -*** - -## Step 5: Build Product Migration Workflow - -In this section, you'll add the feature to migrate products from Magento to Medusa. To implement this feature, you'll use a workflow. - -A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow like you construct a function, but it's a special function that allows you to track its executions' progress, define roll-back logic, and configure other advanced features. Then, you execute the workflow from other customizations, such as in an API route or a scheduled job. - -By implementing the migration feature in a workflow, you ensure that the data remains consistent and that the migration process can be rolled back if an error occurs. - -Refer to the [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md) documentation to learn more about workflows. - -### Workflow Steps - -The workflow you'll create will have the following steps: - -- [getMagentoProductsStep](#getMagentoProductsStep): Retrieve products from Magento using the Magento Module. -- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve Medusa store details, which you'll need when creating the products. -- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve a shipping profile, which you'll associate the created products with. -- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve Magento products that are already in Medusa to update them, instead of creating them. -- [createProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductsWorkflow/index.html.md): Create products in the Medusa application. -- [updateProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductsWorkflow/index.html.md): Update existing products in the Medusa application. - -You only need to implement the `getMagentoProductsStep` step, which retrieves the products from Magento. The other steps and workflows are provided by Medusa's `@medusajs/medusa/core-flows` package. - -### getMagentoProductsStep - -The first step of the workflow retrieves and returns the products from Magento. - -In your plugin, create the file `src/workflows/steps/get-magento-products.ts` with the following content: - -![Diagram showcasing the get-magento-products file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739349590/Medusa%20Resources/magento-5_ueb4wn.jpg) - -```ts title="src/workflows/steps/get-magento-products.ts" -import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" -import { MAGENTO_MODULE } from "../../modules/magento" -import MagentoModuleService from "../../modules/magento/service" - -type GetMagentoProductsInput = { - currentPage: number - pageSize: number -} - -export const getMagentoProductsStep = createStep( - "get-magento-products", - async ({ currentPage, pageSize }: GetMagentoProductsInput, { container }) => { - const magentoModuleService: MagentoModuleService = - container.resolve(MAGENTO_MODULE) - - const response = await magentoModuleService.getProducts({ - currentPage, - pageSize, - }) - - return new StepResponse(response) - } -) -``` - -You create a step using `createStep` from the Workflows SDK. It accepts two parameters: - -1. The step's name, which is `get-magento-products`. -2. An async function that executes the step's logic. The function receives two parameters: - - The input data for the step, which in this case is the pagination parameters. - - An object holding the workflow's context, including the [Medusa Container](https://docs.medusajs.com/docslearn/fundamentals/medusa-container/index.html.md) that allows you to resolve Framework and commerce tools. - -In the step function, you resolve the Magento Module's service from the container, then use its `getProducts` method to retrieve the products from Magento. - -Steps that return data must return them in a `StepResponse` instance. The `StepResponse` constructor accepts as a parameter the data to return. - -### Create migrateProductsFromMagentoWorkflow - -You'll now create the workflow that migrates products from Magento using the step you created and steps from Medusa's `@medusajs/medusa/core-flows` package. - -In your plugin, create the file `src/workflows/migrate-products-from-magento.ts` with the following content: - -![Diagram showcasing the migrate-products-from-magento file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739349820/Medusa%20Resources/magento-6_jjdaxj.jpg) - -```ts title="src/workflows/migrate-products-from-magento.ts" -import { - createWorkflow, transform, WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" -import { - CreateProductWorkflowInputDTO, UpsertProductDTO, -} from "@medusajs/framework/types" -import { - createProductsWorkflow, - updateProductsWorkflow, - useQueryGraphStep, -} from "@medusajs/medusa/core-flows" -import { getMagentoProductsStep } from "./steps/get-magento-products" - -type MigrateProductsFromMagentoWorkflowInput = { - currentPage: number - pageSize: number -} - -export const migrateProductsFromMagentoWorkflowId = - "migrate-products-from-magento" - -export const migrateProductsFromMagentoWorkflow = createWorkflow( - { - name: migrateProductsFromMagentoWorkflowId, - retentionTime: 10000, - store: true, - }, - (input: MigrateProductsFromMagentoWorkflowInput) => { - const { pagination, products, attributes } = getMagentoProductsStep( - input - ) - // TODO prepare data to create and update products - } -) -``` - -You create a workflow using `createWorkflow` from the Workflows SDK. It accepts two parameters: - -1. An object with the workflow's configuration, including the name and whether to store the workflow's executions. You enable storing the workflow execution so that you can view it later in the Medusa Admin dashboard. -2. A worflow constructor function, which holds the workflow's implementation. The function receives the input data for the workflow, which is the pagination parameters. - -In the workflow constructor function, you use the `getMagentoProductsStep` step to retrieve the products from Magento, passing it the pagination parameters from the workflow's input. - -Next, you'll retrieve the Medusa store details and shipping profiles. These are necessary to prepare the data of the products to create or update. - -Replace the `TODO` in the workflow function with the following: - -```ts title="src/workflows/migrate-products-from-magento.ts" -const { data: stores } = useQueryGraphStep({ - entity: "store", - fields: ["supported_currencies.*", "default_sales_channel_id"], - pagination: { - take: 1, - skip: 0, - }, -}) - -const { data: shippingProfiles } = useQueryGraphStep({ - entity: "shipping_profile", - fields: ["id"], - pagination: { - take: 1, - skip: 0, - }, -}).config({ name: "get-shipping-profiles" }) - -// TODO retrieve existing products -``` - -You use the `useQueryGraphStep` step to retrieve the store details and shipping profiles. `useQueryGraphStep` is a Medusa step that wraps [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), allowing you to use it in a workflow. Query is a tool that retrieves data across modules. - -Whe retrieving the store details, you specifically retrieve its supported currencies and default sales channel ID. You'll associate the products with the store's default sales channel, and set their variant prices in the supported currencies. You'll also associate the products with a shipping profile. - -Next, you'll retrieve products that were previously migrated from Magento to determine which products to create or update. Replace the `TODO` with the following: - -```ts title="src/workflows/migrate-products-from-magento.ts" -const externalIdFilters = transform({ - products, -}, (data) => { - return data.products.map((product) => product.id.toString()) -}) - -const { data: existingProducts } = useQueryGraphStep({ - entity: "product", - fields: ["id", "external_id", "variants.id", "variants.metadata"], - filters: { - external_id: externalIdFilters, - }, -}).config({ name: "get-existing-products" }) - -// TODO prepare products to create or update -``` - -Since the Medusa application creates an internal representation of the workflow's constructor function, you can't manipulate data directly, as variables have no value while creating the internal representation. - -Refer to the [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/constructor-constraints/index.html.md) documentation to learn more about the workflow constructor function's constraints. - -Instead, you can manipulate data in a workflow's constructor function using `transform` from the Workflows SDK. `transform` is a function that accepts two parameters: - -- The data to transform, which in this case is the Magento products. -- A function that transforms the data. The function receives the data passed in the first parameter and returns the transformed data. - -In the transformation function, you return the IDs of the Magento products. Then, you use the `useQueryGraphStep` to retrieve products in the Medusa application that have an `external_id` property matching the IDs of the Magento products. You'll use this property to store the IDs of the products in Magento. - -Next, you'll prepare the data to create and update the products. Replace the `TODO` in the workflow function with the following: - -```ts title="src/workflows/migrate-products-from-magento.ts" highlights={prepareHighlights} -const { - productsToCreate, - productsToUpdate, -} = transform({ - products, - attributes, - stores, - shippingProfiles, - existingProducts, -}, (data) => { - const productsToCreate = new Map() - const productsToUpdate = new Map() - - data.products.forEach((magentoProduct) => { - const productData: CreateProductWorkflowInputDTO | UpsertProductDTO = { - title: magentoProduct.name, - description: magentoProduct.custom_attributes.find( - (attr) => attr.attribute_code === "description" - )?.value, - status: magentoProduct.status === 1 ? "published" : "draft", - handle: magentoProduct.custom_attributes.find( - (attr) => attr.attribute_code === "url_key" - )?.value, - external_id: magentoProduct.id.toString(), - thumbnail: magentoProduct.media_gallery_entries.find( - (entry) => entry.types.includes("thumbnail") - )?.file, - sales_channels: [{ - id: data.stores[0].default_sales_channel_id, - }], - shipping_profile_id: data.shippingProfiles[0].id, - } - const existingProduct = data.existingProducts.find((p) => p.external_id === productData.external_id) - - if (existingProduct) { - productData.id = existingProduct.id - } - - productData.options = magentoProduct.extension_attributes.configurable_product_options?.map((option) => { - const attribute = data.attributes.find((attr) => attr.attribute_id === parseInt(option.attribute_id)) - return { - title: option.label, - values: attribute?.options.filter((opt) => { - return option.values.find((v) => v.value_index === parseInt(opt.value)) - }).map((opt) => opt.label) || [], - } - }) || [] - - productData.variants = magentoProduct.children?.map((child) => { - const childOptions: Record = {} - - child.custom_attributes.forEach((attr) => { - const attrData = data.attributes.find((a) => a.attribute_code === attr.attribute_code) - if (!attrData) { - return - } - - childOptions[attrData.default_frontend_label] = attrData.options.find((opt) => opt.value === attr.value)?.label || "" - }) - - const variantExternalId = child.id.toString() - const existingVariant = existingProduct.variants.find((v) => v.metadata.external_id === variantExternalId) - - return { - title: child.name, - sku: child.sku, - options: childOptions, - prices: data.stores[0].supported_currencies.map(({ currency_code }) => { - return { - amount: child.price, - currency_code, - } - }), - metadata: { - external_id: variantExternalId, - }, - id: existingVariant?.id, - } - }) - - productData.images = magentoProduct.media_gallery_entries.filter((entry) => !entry.types.includes("thumbnail")).map((entry) => { - return { - url: entry.file, - metadata: { - external_id: entry.id.toString(), - }, - } - }) - - if (productData.id) { - productsToUpdate.set(existingProduct.id, productData) - } else { - productsToCreate.set(productData.external_id!, productData) - } - }) - - return { - productsToCreate: Array.from(productsToCreate.values()), - productsToUpdate: Array.from(productsToUpdate.values()), - } -}) - -// TODO create and update products -``` - -You use `transform` again to prepare the data to create and update the products in the Medusa application. For each Magento product, you map its equivalent Medusa product's data: - -- You set the product's general details, such as the title, description, status, handle, external ID, and thumbnail using the Magento product's data and custom attributes. -- You associate the product with the default sales channel and shipping profile retrieved previously. -- You map the Magento product's configurable product options to Medusa product options. In Medusa, a product's option has a label, such as "Color", and values, such as "Red". To map the option values, you use the attributes retrieved from Magento. -- You map the Magento product's children to Medusa product variants. For the variant options, you pass an object whose keys is the option's label, such as "Color", and values is the option's value, such as "Red". For the prices, you set the variant's price based on the Magento child's price for every supported currency in the Medusa store. Also, you set the Magento child product's ID in the Medusa variant's `metadata.external_id` property. -- You map the Magento product's media gallery entries to Medusa product images. You filter out the thumbnail image and set the URL and the Magento image's ID in the Medusa image's `metadata.external_id` property. - -In addition, you use the existing products retrieved in the previous step to determine whether a product should be created or updated. If there's an existing product whose `external_id` matches the ID of the magento product, you set the existing product's ID in the `id` property of the product to be updated. You also do the same for its variants. - -Finally, you return the products to create and update. - -The last steps of the workflow is to create and update the products. Replace the `TODO` in the workflow function with the following: - -```ts title="src/workflows/migrate-products-from-magento.ts" -createProductsWorkflow.runAsStep({ - input: { - products: productsToCreate, - }, -}) - -updateProductsWorkflow.runAsStep({ - input: { - products: productsToUpdate, - }, -}) - -return new WorkflowResponse(pagination) -``` - -You use the `createProductsWorkflow` and `updateProductsWorkflow` workflows from Medusa's `@medusajs/medusa/core-flows` package to create and update the products in the Medusa application. - -Workflows must return an instance of `WorkflowResponse`, passing as a parameter the data to return to the workflow's executor. This workflow returns the pagination parameters, allowing you to paginate the product migration process. - -You can now use this workflow to migrate products from Magento to Medusa. You'll learn how to use it in the next steps. - -*** - -## Step 6: Schedule Product Migration - -There are many ways to execute tasks asynchronously in Medusa, such as [scheduling a job](https://docs.medusajs.com/docs/learn/fundamentals/scheduled-jobs/index.html.md) or [handling emitted events](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). - -In this guide, you'll learn how to schedule the product migration at a specified interval using a scheduled job. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. - -Refer to the [Scheduled Jobs](https://docs.medusajs.com/docs/learn/fundamentals/scheduled-jobs/index.html.md) documentation to learn more about scheduled jobs. - -To create a scheduled job, in your plugin, create the file `src/jobs/migrate-magento.ts` with the following content: - -![Diagram showcasing the migrate-magento file to create](https://res.cloudinary.com/dza7lstvk/image/upload/v1739358924/Medusa%20Resources/magento-7_rqoodo.jpg) - -```ts title="src/jobs/migrate-magento.ts" -import { MedusaContainer } from "@medusajs/framework/types" -import { migrateProductsFromMagentoWorkflow } from "../workflows" - -export default async function migrateMagentoJob( - container: MedusaContainer -) { - const logger = container.resolve("logger") - logger.info("Migrating products from Magento...") - - let currentPage = 0 - const pageSize = 100 - let totalCount = 0 - - do { - currentPage++ - - const { - result: pagination, - } = await migrateProductsFromMagentoWorkflow(container).run({ - input: { - currentPage, - pageSize, - }, - }) - - totalCount = pagination.total_count - } while (currentPage * pageSize < totalCount) - - logger.info("Finished migrating products from Magento") -} - -export const config = { - name: "migrate-magento-job", - schedule: "0 0 * * *", -} -``` - -A scheduled job file must export: - -- An asynchronous function that executes the job's logic. The function receives the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md) as a parameter. -- An object with the job's configuration, including the name and the schedule. The schedule is a cron job pattern as a string. - -In the job function, you resolve the [logger](https://docs.medusajs.com/docs/learn/debugging-and-testing/logging/index.html.md) from the container to log messages. Then, you paginate the product migration process by running the `migrateProductsFromMagentoWorkflow` workflow at each page until you've migrated all products. You use the pagination result returned by the workflow to determine whether there are more products to migrate. - -Based on the job's configurations, the Medusa application will run the job at midnight every day. - -### Test it Out - -To test out this scheduled job, first, change the configuration to run the job every minute: - -```ts title="src/jobs/migrate-magento.ts" -export const config = { - // ... - schedule: "* * * * *", -} -``` - -Then, make sure to run the `plugin:develop` command in the plugin if you haven't already: - -```bash -npx medusa plugin:develop -``` - -This ensures that the plugin's latest changes are reflected in the Medusa application. - -Finally, start the Medusa application that the plugin is installed in: - -```bash npm2yarn -npm run dev -``` - -After a minute, you'll see a message in the terminal indicating that the migration started: - -```plain title="Terminal" -info: Migrating products from Magento... -``` - -Once the migration is done, you'll see the following message: - -```plain title="Terminal" -info: Finished migrating products from Magento -``` - -To confirm that the products were migrated, open the Medusa Admin dashboard at `http://localhost:9000/app` and log in. Then, click on Products in the sidebar. You'll see your magento products in the list of products. - -![Click on products at the sidebar on the right, then view the products in the table in the middle.](https://res.cloudinary.com/dza7lstvk/image/upload/v1739359394/Medusa%20Resources/Screenshot_2025-02-12_at_1.22.44_PM_uva98i.png) - -*** - -## Next Steps - -You've now implemented the logic to migrate products from Magento to Medusa. You can re-use the plugin across Medusa applications. You can also expand on the plugin to: - -- Migrate other entities, such as orders, customers, and categories. Migrating other entities follows the same pattern as migrating products, using workflows and scheduled jobs. You only need to format the data to be migrated as needed. -- Allow triggering migrations from the Medusa Admin dashboard using [Admin Customizations](https://docs.medusajs.com/docs/learn/fundamentals/admin/index.html.md). This feature is available in the [Example Repository](https://github.com/medusajs/example-repository/tree/main/src/admin). - -If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth learning of all the concepts you've used in this guide and more. - -To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md). - - # How to Build a Wishlist Plugin In this guide, you'll learn how to build a wishlist [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) in Medusa. @@ -56359,350 +56359,350 @@ To learn more about the commerce features that Medusa provides, check out Medusa ## JS SDK Admin -- [clearToken](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken/index.html.md) -- [clearToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken_/index.html.md) -- [fetch](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetch/index.html.md) -- [getApiKeyHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getApiKeyHeader_/index.html.md) -- [fetchStream](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetchStream/index.html.md) -- [getJwtHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getJwtHeader_/index.html.md) -- [getPublishableKeyHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getPublishableKeyHeader_/index.html.md) -- [getTokenStorageInfo\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getTokenStorageInfo_/index.html.md) -- [initClient](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.initClient/index.html.md) -- [setToken](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken/index.html.md) -- [setToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken_/index.html.md) -- [getToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getToken_/index.html.md) -- [throwError\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.throwError_/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.retrieve/index.html.md) -- [getItem](https://docs.medusajs.com/references/js_sdk/admin/CustomStorage/methods/js_sdk.admin.CustomStorage.getItem/index.html.md) -- [setItem](https://docs.medusajs.com/references/js_sdk/admin/CustomStorage/methods/js_sdk.admin.CustomStorage.setItem/index.html.md) -- [removeItem](https://docs.medusajs.com/references/js_sdk/admin/CustomStorage/methods/js_sdk.admin.CustomStorage.removeItem/index.html.md) -- [batchSalesChannels](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.batchSalesChannels/index.html.md) +- [batchPromotions](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.batchPromotions/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.update/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.delete/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.create/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.delete/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.retrieve/index.html.md) +- [batchSalesChannels](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.batchSalesChannels/index.html.md) - [revoke](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.revoke/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.list/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.update/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.delete/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.create/index.html.md) -- [batchPromotions](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.batchPromotions/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.list/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.update/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.retrieve/index.html.md) +- [addInboundItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundItems/index.html.md) +- [addInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundShipping/index.html.md) +- [addItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addItems/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancel/index.html.md) +- [addOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundShipping/index.html.md) +- [addOutboundItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundItems/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.create/index.html.md) +- [deleteInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteInboundShipping/index.html.md) +- [deleteOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteOutboundShipping/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.list/index.html.md) +- [removeInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeInboundItem/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancelRequest/index.html.md) +- [request](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.request/index.html.md) +- [removeOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeOutboundItem/index.html.md) +- [removeItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeItem/index.html.md) +- [updateInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundItem/index.html.md) +- [updateItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateItem/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.retrieve/index.html.md) +- [updateOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundItem/index.html.md) +- [updateOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundShipping/index.html.md) +- [updateInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundShipping/index.html.md) +- [clearToken](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken/index.html.md) +- [fetch](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetch/index.html.md) +- [clearToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken_/index.html.md) +- [getApiKeyHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getApiKeyHeader_/index.html.md) +- [getJwtHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getJwtHeader_/index.html.md) +- [getPublishableKeyHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getPublishableKeyHeader_/index.html.md) +- [fetchStream](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetchStream/index.html.md) +- [getToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getToken_/index.html.md) +- [initClient](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.initClient/index.html.md) +- [getTokenStorageInfo\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getTokenStorageInfo_/index.html.md) +- [setToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken_/index.html.md) +- [throwError\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.throwError_/index.html.md) +- [setToken](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.list/index.html.md) +- [removeItem](https://docs.medusajs.com/references/js_sdk/admin/CustomStorage/methods/js_sdk.admin.CustomStorage.removeItem/index.html.md) +- [getItem](https://docs.medusajs.com/references/js_sdk/admin/CustomStorage/methods/js_sdk.admin.CustomStorage.getItem/index.html.md) +- [setItem](https://docs.medusajs.com/references/js_sdk/admin/CustomStorage/methods/js_sdk.admin.CustomStorage.setItem/index.html.md) +- [batchCustomerGroups](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.batchCustomerGroups/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.create/index.html.md) +- [createAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.createAddress/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.delete/index.html.md) +- [deleteAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.deleteAddress/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.update/index.html.md) +- [listAddresses](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.listAddresses/index.html.md) +- [retrieveAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.retrieveAddress/index.html.md) +- [updateAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.updateAddress/index.html.md) +- [batchCustomers](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.batchCustomers/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.update/index.html.md) - [addItems](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.addItems/index.html.md) -- [addShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.addShippingMethod/index.html.md) -- [beginEdit](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.beginEdit/index.html.md) -- [cancelEdit](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.cancelEdit/index.html.md) - [addPromotions](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.addPromotions/index.html.md) +- [addShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.addShippingMethod/index.html.md) - [confirmEdit](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.confirmEdit/index.html.md) - [convertToOrder](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.convertToOrder/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.list/index.html.md) +- [cancelEdit](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.cancelEdit/index.html.md) +- [beginEdit](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.beginEdit/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.create/index.html.md) -- [removeActionShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removeActionShippingMethod/index.html.md) -- [removeShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removeShippingMethod/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.list/index.html.md) +- [removeActionItem](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removeActionItem/index.html.md) - [removePromotions](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removePromotions/index.html.md) +- [removeShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removeShippingMethod/index.html.md) - [requestEdit](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.requestEdit/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.retrieve/index.html.md) -- [removeActionItem](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removeActionItem/index.html.md) +- [removeActionShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.removeActionShippingMethod/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.update/index.html.md) -- [updateItem](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.updateItem/index.html.md) - [updateActionItem](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.updateActionItem/index.html.md) - [updateActionShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.updateActionShippingMethod/index.html.md) -- [updateShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.updateShippingMethod/index.html.md) - [addInboundItems](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addInboundItems/index.html.md) +- [updateShippingMethod](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.updateShippingMethod/index.html.md) +- [updateItem](https://docs.medusajs.com/references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.updateItem/index.html.md) - [addInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addInboundShipping/index.html.md) - [addOutboundItems](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundItems/index.html.md) -- [addOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundShipping/index.html.md) - [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.cancelRequest/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.create/index.html.md) - [cancel](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.cancel/index.html.md) - [deleteInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.deleteInboundShipping/index.html.md) +- [addOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundShipping/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.list/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.create/index.html.md) - [removeInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeInboundItem/index.html.md) -- [removeOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeOutboundItem/index.html.md) - [deleteOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.deleteOutboundShipping/index.html.md) - [request](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.request/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.retrieve/index.html.md) - [updateInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateInboundItem/index.html.md) +- [removeOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeOutboundItem/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.retrieve/index.html.md) - [updateOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundItem/index.html.md) - [updateInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateInboundShipping/index.html.md) -- [updateOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundShipping/index.html.md) - [cancel](https://docs.medusajs.com/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.cancel/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.create/index.html.md) - [createShipment](https://docs.medusajs.com/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.createShipment/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.list/index.html.md) - [listFulfillmentOptions](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.listFulfillmentOptions/index.html.md) -- [batchInventoryItemLocationLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemLocationLevels/index.html.md) -- [batchInventoryItemsLocationLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemsLocationLevels/index.html.md) -- [batchUpdateLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchUpdateLevels/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.create/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.delete/index.html.md) -- [deleteLevel](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.deleteLevel/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.list/index.html.md) -- [listLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.listLevels/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.retrieve/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.update/index.html.md) -- [updateLevel](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.updateLevel/index.html.md) +- [updateOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundShipping/index.html.md) +- [createServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.createServiceZone/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.delete/index.html.md) +- [updateServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.updateServiceZone/index.html.md) +- [deleteServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.deleteServiceZone/index.html.md) +- [retrieveServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.retrieveServiceZone/index.html.md) - [accept](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.accept/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.delete/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.create/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.list/index.html.md) - [resend](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.resend/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.retrieve/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.delete/index.html.md) -- [deleteServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.deleteServiceZone/index.html.md) -- [createServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.createServiceZone/index.html.md) -- [retrieveServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.retrieveServiceZone/index.html.md) -- [addInboundItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundItems/index.html.md) -- [addInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundShipping/index.html.md) -- [updateServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.updateServiceZone/index.html.md) -- [addItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addItems/index.html.md) -- [addOutboundItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundItems/index.html.md) -- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancel/index.html.md) -- [addOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundShipping/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.create/index.html.md) -- [deleteInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteInboundShipping/index.html.md) -- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancelRequest/index.html.md) -- [deleteOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteOutboundShipping/index.html.md) -- [removeInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeInboundItem/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.list/index.html.md) -- [removeItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeItem/index.html.md) -- [request](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.request/index.html.md) -- [removeOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeOutboundItem/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.retrieve/index.html.md) -- [updateInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundShipping/index.html.md) -- [updateInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundItem/index.html.md) -- [updateItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateItem/index.html.md) -- [updateOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundShipping/index.html.md) -- [updateOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundItem/index.html.md) -- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancel/index.html.md) -- [createCreditLine](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createCreditLine/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.list/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.delete/index.html.md) +- [batchInventoryItemLocationLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemLocationLevels/index.html.md) +- [batchUpdateLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchUpdateLevels/index.html.md) +- [batchInventoryItemsLocationLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemsLocationLevels/index.html.md) +- [deleteLevel](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.deleteLevel/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.list/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.create/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.retrieve/index.html.md) +- [listLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.listLevels/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.update/index.html.md) - [cancelFulfillment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancelFulfillment/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancel/index.html.md) - [cancelTransfer](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancelTransfer/index.html.md) -- [createFulfillment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createFulfillment/index.html.md) -- [listChanges](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listChanges/index.html.md) - [createShipment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createShipment/index.html.md) +- [updateLevel](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.updateLevel/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.list/index.html.md) +- [createFulfillment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createFulfillment/index.html.md) +- [createCreditLine](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createCreditLine/index.html.md) - [listLineItems](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listLineItems/index.html.md) +- [listChanges](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listChanges/index.html.md) - [requestTransfer](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.requestTransfer/index.html.md) -- [markAsDelivered](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.markAsDelivered/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrieve/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.update/index.html.md) +- [markAsDelivered](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.markAsDelivered/index.html.md) - [addItems](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.addItems/index.html.md) -- [retrievePreview](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrievePreview/index.html.md) -- [initiateRequest](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.initiateRequest/index.html.md) -- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.cancelRequest/index.html.md) - [confirm](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.confirm/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.cancelRequest/index.html.md) +- [retrievePreview](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrievePreview/index.html.md) - [removeAddedItem](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.removeAddedItem/index.html.md) - [request](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.request/index.html.md) +- [initiateRequest](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.initiateRequest/index.html.md) - [updateAddedItem](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.updateAddedItem/index.html.md) - [updateOriginalItem](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.updateOriginalItem/index.html.md) -- [batchCustomers](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.batchCustomers/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.create/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.delete/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.retrieve/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.update/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Plugin/methods/js_sdk.admin.Plugin.list/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.delete/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.create/index.html.md) -- [linkProducts](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.linkProducts/index.html.md) -- [batchPrices](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.batchPrices/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.list/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.update/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.retrieve/index.html.md) -- [batchCustomerGroups](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.batchCustomerGroups/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.create/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.delete/index.html.md) -- [deleteAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.deleteAddress/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.list/index.html.md) -- [listAddresses](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.listAddresses/index.html.md) -- [createAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.createAddress/index.html.md) -- [retrieveAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.retrieveAddress/index.html.md) -- [updateAddress](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.updateAddress/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.retrieve/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.update/index.html.md) -- [listPaymentProviders](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.listPaymentProviders/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.list/index.html.md) -- [refund](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.refund/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.retrieve/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.create/index.html.md) +- [markAsPaid](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.markAsPaid/index.html.md) - [capture](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.capture/index.html.md) -- [batch](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batch/index.html.md) -- [batchVariantInventoryItems](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariantInventoryItems/index.html.md) -- [confirmImport](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.confirmImport/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.create/index.html.md) -- [batchVariants](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariants/index.html.md) -- [createVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createVariant/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.delete/index.html.md) -- [createOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createOption/index.html.md) -- [deleteOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteOption/index.html.md) -- [deleteVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteVariant/index.html.md) -- [export](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.export/index.html.md) -- [import](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.import/index.html.md) -- [listVariants](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listVariants/index.html.md) -- [listOptions](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listOptions/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.list/index.html.md) -- [retrieveVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveVariant/index.html.md) -- [retrieveOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveOption/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieve/index.html.md) -- [updateOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateOption/index.html.md) -- [updateVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateVariant/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.retrieve/index.html.md) +- [listPaymentProviders](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.listPaymentProviders/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Plugin/methods/js_sdk.admin.Plugin.list/index.html.md) +- [batchPrices](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.batchPrices/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.delete/index.html.md) +- [refund](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.refund/index.html.md) +- [linkProducts](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.linkProducts/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.retrieve/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/Notification/methods/js_sdk.admin.Notification.list/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Notification/methods/js_sdk.admin.Notification.retrieve/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.delete/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.retrieve/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.update/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.create/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.retrieve/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.update/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.list/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.retrieve/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.update/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.list/index.html.md) - [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.updateProducts/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductVariant/methods/js_sdk.admin.ProductVariant.list/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.list/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.delete/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.update/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.create/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.retrieve/index.html.md) -- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.updateProducts/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/RefundReason/methods/js_sdk.admin.RefundReason.list/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.create/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.retrieve/index.html.md) +- [batchVariantInventoryItems](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariantInventoryItems/index.html.md) +- [batch](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batch/index.html.md) +- [batchVariants](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariants/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.create/index.html.md) +- [confirmImport](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.confirmImport/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.create/index.html.md) +- [createOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createOption/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.delete/index.html.md) +- [deleteOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteOption/index.html.md) +- [deleteVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteVariant/index.html.md) +- [export](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.export/index.html.md) +- [createVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createVariant/index.html.md) +- [import](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.import/index.html.md) +- [listOptions](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listOptions/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieve/index.html.md) +- [retrieveVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveVariant/index.html.md) +- [retrieveOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveOption/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.update/index.html.md) +- [listVariants](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listVariants/index.html.md) +- [updateOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateOption/index.html.md) +- [updateVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateVariant/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.delete/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.retrieve/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.update/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.create/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.create/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.list/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.retrieve/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.update/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.list/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.delete/index.html.md) -- [markAsPaid](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.markAsPaid/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.delete/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.create/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.list/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.create/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.update/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.retrieve/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductVariant/methods/js_sdk.admin.ProductVariant.list/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.create/index.html.md) +- [addRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.addRules/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.list/index.html.md) +- [listRuleAttributes](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleAttributes/index.html.md) +- [listRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRules/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.retrieve/index.html.md) +- [removeRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.removeRules/index.html.md) +- [updateRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.updateRules/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/RefundReason/methods/js_sdk.admin.RefundReason.list/index.html.md) - [create](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.delete/index.html.md) +- [listRuleValues](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleValues/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.list/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.update/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.retrieve/index.html.md) -- [addRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.addRules/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.delete/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.create/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.list/index.html.md) -- [listRuleAttributes](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleAttributes/index.html.md) -- [listRuleValues](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleValues/index.html.md) -- [listRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRules/index.html.md) -- [removeRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.removeRules/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.update/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.retrieve/index.html.md) -- [updateRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.updateRules/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.update/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.create/index.html.md) +- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.updateProducts/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.list/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.create/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.update/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.update/index.html.md) +- [batchProducts](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.batchProducts/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.update/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.retrieve/index.html.md) +- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.updateProducts/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.update/index.html.md) +- [updateRules](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.updateRules/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.create/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.create/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.retrieve/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.list/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.update/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.create/index.html.md) -- [createFulfillmentSet](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.createFulfillmentSet/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.retrieve/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.delete/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.list/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.update/index.html.md) -- [updateFulfillmentProviders](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateFulfillmentProviders/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.retrieve/index.html.md) -- [updateSalesChannels](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateSalesChannels/index.html.md) -- [batchProducts](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.batchProducts/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.create/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.retrieve/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.delete/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.update/index.html.md) -- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.updateProducts/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.create/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.delete/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.retrieve/index.html.md) - [addReturnItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.addReturnItem/index.html.md) - [addReturnShipping](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.addReturnShipping/index.html.md) -- [cancelReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelReceive/index.html.md) -- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelRequest/index.html.md) - [cancel](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancel/index.html.md) +- [cancelReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelReceive/index.html.md) - [confirmReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.confirmReceive/index.html.md) - [deleteReturnShipping](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.deleteReturnShipping/index.html.md) - [confirmRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.confirmRequest/index.html.md) -- [dismissItems](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.dismissItems/index.html.md) - [initiateReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.initiateReceive/index.html.md) +- [dismissItems](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.dismissItems/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelRequest/index.html.md) - [initiateRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.initiateRequest/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.list/index.html.md) - [receiveItems](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.receiveItems/index.html.md) - [removeReceiveItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeReceiveItem/index.html.md) +- [removeDismissItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeDismissItem/index.html.md) - [removeReturnItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeReturnItem/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.retrieve/index.html.md) - [updateDismissItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateDismissItem/index.html.md) - [updateRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateRequest/index.html.md) - [updateReceiveItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReceiveItem/index.html.md) - [updateReturnShipping](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReturnShipping/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.create/index.html.md) - [updateReturnItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReturnItem/index.html.md) -- [removeDismissItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeDismissItem/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.create/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.delete/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.retrieve/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.list/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.delete/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.create/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.update/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.update/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.retrieve/index.html.md) -- [updateRules](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.updateRules/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.retrieve/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.create/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.delete/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.delete/index.html.md) +- [createFulfillmentSet](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.createFulfillmentSet/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.update/index.html.md) +- [updateSalesChannels](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateSalesChannels/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.list/index.html.md) +- [updateFulfillmentProviders](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateFulfillmentProviders/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.update/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.list/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.create/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.retrieve/index.html.md) - [delete](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.delete/index.html.md) -- [me](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.me/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.retrieve/index.html.md) +- [me](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.me/index.html.md) - [update](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.update/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.list/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.create/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.update/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.retrieve/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.retrieve/index.html.md) - [list](https://docs.medusajs.com/references/js_sdk/admin/WorkflowExecution/methods/js_sdk.admin.WorkflowExecution.list/index.html.md) - [retrieve](https://docs.medusajs.com/references/js_sdk/admin/WorkflowExecution/methods/js_sdk.admin.WorkflowExecution.retrieve/index.html.md) -- [delete](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.delete/index.html.md) -- [create](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.create/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.list/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.update/index.html.md) -- [list](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.list/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.retrieve/index.html.md) -- [update](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.update/index.html.md) -- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.retrieve/index.html.md) ## JS SDK Auth +- [logout](https://docs.medusajs.com/references/js-sdk/auth/logout/index.html.md) - [callback](https://docs.medusajs.com/references/js-sdk/auth/callback/index.html.md) - [login](https://docs.medusajs.com/references/js-sdk/auth/login/index.html.md) -- [register](https://docs.medusajs.com/references/js-sdk/auth/register/index.html.md) -- [resetPassword](https://docs.medusajs.com/references/js-sdk/auth/resetPassword/index.html.md) - [refresh](https://docs.medusajs.com/references/js-sdk/auth/refresh/index.html.md) +- [register](https://docs.medusajs.com/references/js-sdk/auth/register/index.html.md) - [updateProvider](https://docs.medusajs.com/references/js-sdk/auth/updateProvider/index.html.md) -- [logout](https://docs.medusajs.com/references/js-sdk/auth/logout/index.html.md) +- [resetPassword](https://docs.medusajs.com/references/js-sdk/auth/resetPassword/index.html.md) ## JS SDK Store -- [category](https://docs.medusajs.com/references/js-sdk/store/category/index.html.md) -- [cart](https://docs.medusajs.com/references/js-sdk/store/cart/index.html.md) -- [customer](https://docs.medusajs.com/references/js-sdk/store/customer/index.html.md) -- [order](https://docs.medusajs.com/references/js-sdk/store/order/index.html.md) -- [payment](https://docs.medusajs.com/references/js-sdk/store/payment/index.html.md) -- [fulfillment](https://docs.medusajs.com/references/js-sdk/store/fulfillment/index.html.md) -- [product](https://docs.medusajs.com/references/js-sdk/store/product/index.html.md) -- [region](https://docs.medusajs.com/references/js-sdk/store/region/index.html.md) - [collection](https://docs.medusajs.com/references/js-sdk/store/collection/index.html.md) +- [category](https://docs.medusajs.com/references/js-sdk/store/category/index.html.md) +- [customer](https://docs.medusajs.com/references/js-sdk/store/customer/index.html.md) +- [cart](https://docs.medusajs.com/references/js-sdk/store/cart/index.html.md) +- [fulfillment](https://docs.medusajs.com/references/js-sdk/store/fulfillment/index.html.md) +- [payment](https://docs.medusajs.com/references/js-sdk/store/payment/index.html.md) +- [order](https://docs.medusajs.com/references/js-sdk/store/order/index.html.md) +- [region](https://docs.medusajs.com/references/js-sdk/store/region/index.html.md) +- [product](https://docs.medusajs.com/references/js-sdk/store/product/index.html.md) # Configure Medusa Backend @@ -57355,6 +57355,145 @@ These layout components allow you to set the layout of your [UI routes](https:// These components allow you to use common Medusa Admin components in your custom UI routes and widgets. +# Single Column Layout - Admin Components + +The Medusa Admin has pages with a single column of content. + +This doesn't include the sidebar, only the main content. + +![An example of an admin page with a single column](https://res.cloudinary.com/dza7lstvk/image/upload/v1728286605/Medusa%20Resources/single-column.png) + +To create a layout that you can use in UI routes to support one column of content, create the component `src/admin/layouts/single-column.tsx` with the following content: + +```tsx title="src/admin/layouts/single-column.tsx" +export type SingleColumnLayoutProps = { + children: React.ReactNode +} + +export const SingleColumnLayout = ({ children }: SingleColumnLayoutProps) => { + return ( +
+ {children} +
+ ) +} +``` + +The `SingleColumnLayout` accepts the content in the `children` props. + +*** + +## Example + +Use the `SingleColumnLayout` component in your UI routes that have a single column. For example: + +```tsx title="src/admin/routes/custom/page.tsx" highlights={[["9"]]} +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { ChatBubbleLeftRight } from "@medusajs/icons" +import { Container } from "../../components/container" +import { SingleColumnLayout } from "../../layouts/single-column" +import { Header } from "../../components/header" + +const CustomPage = () => { + return ( + + +
+ + + ) +} + +export const config = defineRouteConfig({ + label: "Custom", + icon: ChatBubbleLeftRight, +}) + +export default CustomPage +``` + +This UI route also uses a [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and a [Header]() custom components. + + +# Two Column Layout - Admin Components + +The Medusa Admin has pages with two columns of content. + +This doesn't include the sidebar, only the main content. + +![An example of an admin page with two columns](https://res.cloudinary.com/dza7lstvk/image/upload/v1728286690/Medusa%20Resources/two-column_sdnkg0.png) + +To create a layout that you can use in UI routes to support two columns of content, create the component `src/admin/layouts/two-column.tsx` with the following content: + +```tsx title="src/admin/layouts/two-column.tsx" +export type TwoColumnLayoutProps = { + firstCol: React.ReactNode + secondCol: React.ReactNode +} + +export const TwoColumnLayout = ({ + firstCol, + secondCol, +}: TwoColumnLayoutProps) => { + return ( +
+
+ {firstCol} +
+
+ {secondCol} +
+
+ ) +} +``` + +The `TwoColumnLayout` accepts two props: + +- `firstCol` indicating the content of the first column. +- `secondCol` indicating the content of the second column. + +*** + +## Example + +Use the `TwoColumnLayout` component in your UI routes that have a single column. For example: + +```tsx title="src/admin/routes/custom/page.tsx" highlights={[["9"]]} +import { defineRouteConfig } from "@medusajs/admin-sdk" +import { ChatBubbleLeftRight } from "@medusajs/icons" +import { Container } from "../../components/container" +import { Header } from "../../components/header" +import { TwoColumnLayout } from "../../layouts/two-column" + +const CustomPage = () => { + return ( + +
+ + } + secondCol={ + +
+ + } + /> + ) +} + +export const config = defineRouteConfig({ + label: "Custom", + icon: ChatBubbleLeftRight, +}) + +export default CustomPage +``` + +This UI route also uses [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and [Header]() custom components. + + # Action Menu - Admin Components The Medusa Admin often provides additional actions in a dropdown shown when users click a three-dot icon. @@ -58067,65 +58206,6 @@ export default CustomPage ``` -# Container - Admin Components - -The Medusa Admin wraps each section of a page in a container. - -![Example of a container in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728287102/Medusa%20Resources/container_soenir.png) - -To create a component that uses the same container styling in your widgets or UI routes, create the file `src/admin/components/container.tsx` with the following content: - -```tsx -import { - Container as UiContainer, - clx, -} from "@medusajs/ui" - -type ContainerProps = React.ComponentProps - -export const Container = (props: ContainerProps) => { - return ( - - ) -} -``` - -The `Container` component re-uses the component from the [Medusa UI package](https://docs.medusajs.com/ui/components/container/index.html.md) and applies to it classes to match the Medusa Admin's design conventions. - -*** - -## Example - -Use that `Container` component in any widget or UI route. - -For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: - -```tsx title="src/admin/widgets/product-widget.tsx" -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Container } from "../components/container" -import { Header } from "../components/header" - -const ProductWidget = () => { - return ( - -
- - ) -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget -``` - -This widget also uses a [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom component. - - # Forms - Admin Components The Medusa Admin has two types of forms: @@ -58845,6 +58925,293 @@ export default ProductWidget This widget also uses a [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) custom component. +# Container - Admin Components + +The Medusa Admin wraps each section of a page in a container. + +![Example of a container in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728287102/Medusa%20Resources/container_soenir.png) + +To create a component that uses the same container styling in your widgets or UI routes, create the file `src/admin/components/container.tsx` with the following content: + +```tsx +import { + Container as UiContainer, + clx, +} from "@medusajs/ui" + +type ContainerProps = React.ComponentProps + +export const Container = (props: ContainerProps) => { + return ( + + ) +} +``` + +The `Container` component re-uses the component from the [Medusa UI package](https://docs.medusajs.com/ui/components/container/index.html.md) and applies to it classes to match the Medusa Admin's design conventions. + +*** + +## Example + +Use that `Container` component in any widget or UI route. + +For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: + +```tsx title="src/admin/widgets/product-widget.tsx" +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { Container } from "../components/container" +import { Header } from "../components/header" + +const ProductWidget = () => { + return ( + +
+ + ) +} + +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductWidget +``` + +This widget also uses a [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom component. + + +# JSON View - Admin Components + +Detail pages in the Medusa Admin show a JSON section to view the current page's details in JSON format. + +![Example of a JSON section in the admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728295129/Medusa%20Resources/json_dtbsgm.png) + +To create a component that shows a JSON section in your customizations, create the file `src/admin/components/json-view-section.tsx` with the following content: + +```tsx title="src/admin/components/json-view-section.tsx" +import { + ArrowUpRightOnBox, + Check, + SquareTwoStack, + TriangleDownMini, + XMarkMini, +} from "@medusajs/icons" +import { + Badge, + Container, + Drawer, + Heading, + IconButton, + Kbd, +} from "@medusajs/ui" +import Primitive from "@uiw/react-json-view" +import { CSSProperties, MouseEvent, Suspense, useState } from "react" + +type JsonViewSectionProps = { + data: object + title?: string +} + +export const JsonViewSection = ({ data }: JsonViewSectionProps) => { + const numberOfKeys = Object.keys(data).length + + return ( + +
+ JSON + + {numberOfKeys} keys + +
+ + + + + + + +
+
+ + + + {numberOfKeys} + + + +
+
+ + esc + + + + + + +
+
+ +
+
} + > + + } /> + ( + null + )} + /> + ( + undefined + )} + /> + { + return ( + + {Object.keys(value as object).length} items + + ) + }} + /> + + + + + : + + { + return + }} + /> + + + +
+
+
+
+ ) +} + +type CopiedProps = { + style?: CSSProperties + value: object | undefined +} + +const Copied = ({ style, value }: CopiedProps) => { + const [copied, setCopied] = useState(false) + + const handler = (e: MouseEvent) => { + e.stopPropagation() + setCopied(true) + + if (typeof value === "string") { + navigator.clipboard.writeText(value) + } else { + const json = JSON.stringify(value, null, 2) + navigator.clipboard.writeText(json) + } + + setTimeout(() => { + setCopied(false) + }, 2000) + } + + const styl = { whiteSpace: "nowrap", width: "20px" } + + if (copied) { + return ( + + + + ) + } + + return ( + + + + ) +} +``` + +The `JsonViewSection` component shows a section with the "JSON" title and a button to show the data as JSON in a drawer or side window. + +The `JsonViewSection` accepts a `data` prop, which is the data to show as a JSON object in the drawer. + +*** + +## Example + +Use the `JsonViewSection` component in any widget or UI route. + +For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: + +```tsx title="src/admin/widgets/product-widget.tsx" +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { JsonViewSection } from "../components/json-view-section" + +const ProductWidget = () => { + return +} + +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductWidget +``` + +This shows the JSON section at the top of the product page, passing it the object `{ name: "John" }`. + + # Section Row - Admin Components The Medusa Admin often shows information in rows of label-values, such as when showing a product's details. @@ -59227,373 +59594,6 @@ If `data` isn't `undefined`, you display the `Table` component passing it the fo To test it out, log into the Medusa Admin and open `http://localhost:9000/app/custom`. You'll find a table of products with pagination. -# JSON View - Admin Components - -Detail pages in the Medusa Admin show a JSON section to view the current page's details in JSON format. - -![Example of a JSON section in the admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728295129/Medusa%20Resources/json_dtbsgm.png) - -To create a component that shows a JSON section in your customizations, create the file `src/admin/components/json-view-section.tsx` with the following content: - -```tsx title="src/admin/components/json-view-section.tsx" -import { - ArrowUpRightOnBox, - Check, - SquareTwoStack, - TriangleDownMini, - XMarkMini, -} from "@medusajs/icons" -import { - Badge, - Container, - Drawer, - Heading, - IconButton, - Kbd, -} from "@medusajs/ui" -import Primitive from "@uiw/react-json-view" -import { CSSProperties, MouseEvent, Suspense, useState } from "react" - -type JsonViewSectionProps = { - data: object - title?: string -} - -export const JsonViewSection = ({ data }: JsonViewSectionProps) => { - const numberOfKeys = Object.keys(data).length - - return ( - -
- JSON - - {numberOfKeys} keys - -
- - - - - - - -
-
- - - - {numberOfKeys} - - - -
-
- - esc - - - - - - -
-
- -
-
} - > - - } /> - ( - null - )} - /> - ( - undefined - )} - /> - { - return ( - - {Object.keys(value as object).length} items - - ) - }} - /> - - - - - : - - { - return - }} - /> - - - -
-
-
-
- ) -} - -type CopiedProps = { - style?: CSSProperties - value: object | undefined -} - -const Copied = ({ style, value }: CopiedProps) => { - const [copied, setCopied] = useState(false) - - const handler = (e: MouseEvent) => { - e.stopPropagation() - setCopied(true) - - if (typeof value === "string") { - navigator.clipboard.writeText(value) - } else { - const json = JSON.stringify(value, null, 2) - navigator.clipboard.writeText(json) - } - - setTimeout(() => { - setCopied(false) - }, 2000) - } - - const styl = { whiteSpace: "nowrap", width: "20px" } - - if (copied) { - return ( - - - - ) - } - - return ( - - - - ) -} -``` - -The `JsonViewSection` component shows a section with the "JSON" title and a button to show the data as JSON in a drawer or side window. - -The `JsonViewSection` accepts a `data` prop, which is the data to show as a JSON object in the drawer. - -*** - -## Example - -Use the `JsonViewSection` component in any widget or UI route. - -For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: - -```tsx title="src/admin/widgets/product-widget.tsx" -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { JsonViewSection } from "../components/json-view-section" - -const ProductWidget = () => { - return -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget -``` - -This shows the JSON section at the top of the product page, passing it the object `{ name: "John" }`. - - -# Two Column Layout - Admin Components - -The Medusa Admin has pages with two columns of content. - -This doesn't include the sidebar, only the main content. - -![An example of an admin page with two columns](https://res.cloudinary.com/dza7lstvk/image/upload/v1728286690/Medusa%20Resources/two-column_sdnkg0.png) - -To create a layout that you can use in UI routes to support two columns of content, create the component `src/admin/layouts/two-column.tsx` with the following content: - -```tsx title="src/admin/layouts/two-column.tsx" -export type TwoColumnLayoutProps = { - firstCol: React.ReactNode - secondCol: React.ReactNode -} - -export const TwoColumnLayout = ({ - firstCol, - secondCol, -}: TwoColumnLayoutProps) => { - return ( -
-
- {firstCol} -
-
- {secondCol} -
-
- ) -} -``` - -The `TwoColumnLayout` accepts two props: - -- `firstCol` indicating the content of the first column. -- `secondCol` indicating the content of the second column. - -*** - -## Example - -Use the `TwoColumnLayout` component in your UI routes that have a single column. For example: - -```tsx title="src/admin/routes/custom/page.tsx" highlights={[["9"]]} -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { ChatBubbleLeftRight } from "@medusajs/icons" -import { Container } from "../../components/container" -import { Header } from "../../components/header" -import { TwoColumnLayout } from "../../layouts/two-column" - -const CustomPage = () => { - return ( - -
- - } - secondCol={ - -
- - } - /> - ) -} - -export const config = defineRouteConfig({ - label: "Custom", - icon: ChatBubbleLeftRight, -}) - -export default CustomPage -``` - -This UI route also uses [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and [Header]() custom components. - - -# Single Column Layout - Admin Components - -The Medusa Admin has pages with a single column of content. - -This doesn't include the sidebar, only the main content. - -![An example of an admin page with a single column](https://res.cloudinary.com/dza7lstvk/image/upload/v1728286605/Medusa%20Resources/single-column.png) - -To create a layout that you can use in UI routes to support one column of content, create the component `src/admin/layouts/single-column.tsx` with the following content: - -```tsx title="src/admin/layouts/single-column.tsx" -export type SingleColumnLayoutProps = { - children: React.ReactNode -} - -export const SingleColumnLayout = ({ children }: SingleColumnLayoutProps) => { - return ( -
- {children} -
- ) -} -``` - -The `SingleColumnLayout` accepts the content in the `children` props. - -*** - -## Example - -Use the `SingleColumnLayout` component in your UI routes that have a single column. For example: - -```tsx title="src/admin/routes/custom/page.tsx" highlights={[["9"]]} -import { defineRouteConfig } from "@medusajs/admin-sdk" -import { ChatBubbleLeftRight } from "@medusajs/icons" -import { Container } from "../../components/container" -import { SingleColumnLayout } from "../../layouts/single-column" -import { Header } from "../../components/header" - -const CustomPage = () => { - return ( - - -
- - - ) -} - -export const config = defineRouteConfig({ - label: "Custom", - icon: ChatBubbleLeftRight, -}) - -export default CustomPage -``` - -This UI route also uses a [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and a [Header]() custom components. - - # Service Factory Reference This section of the documentation provides a reference of the methods generated for services extending the service factory (`MedusaService`), and how to use them. @@ -59621,44 +59621,6 @@ Some examples of method names: The reference uses only the operation name to refer to the method. -# create Method - Service Factory Reference - -This method creates one or more records of the data model. - -## Create One Record - -```ts -const post = await postModuleService.createPosts({ - name: "My Post", - published_at: new Date(), - metadata: { - external_id: "1234", - }, -}) -``` - -If an object is passed of the method, an object of the created record is also returned. - -*** - -## Create Multiple Records - -```ts -const posts = await postModuleService.createPosts([ - { - name: "My Post", - published_at: new Date(), - }, - { - name: "My Other Post", - published_at: new Date(), - }, -]) -``` - -If an array is passed of the method, an array of the created records is also returned. - - # delete Method - Service Factory Reference This method deletes one or more records. @@ -59699,6 +59661,44 @@ To delete records matching a set of filters, pass an object of filters as a para Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). +# create Method - Service Factory Reference + +This method creates one or more records of the data model. + +## Create One Record + +```ts +const post = await postModuleService.createPosts({ + name: "My Post", + published_at: new Date(), + metadata: { + external_id: "1234", + }, +}) +``` + +If an object is passed of the method, an object of the created record is also returned. + +*** + +## Create Multiple Records + +```ts +const posts = await postModuleService.createPosts([ + { + name: "My Post", + published_at: new Date(), + }, + { + name: "My Other Post", + published_at: new Date(), + }, +]) +``` + +If an array is passed of the method, an array of the created records is also returned. + + # list Method - Service Factory Reference This method retrieves a list of records. @@ -59817,6 +59817,216 @@ To sort records by one or more properties, pass to the second object parameter t The method returns an array of the first `15` records matching the filters. +# softDelete Method - Service Factory Reference + +This method soft deletes one or more records of the data model. + +## Soft Delete One Record + +```ts +const deletedPosts = await postModuleService.softDeletePosts( + "123" +) +``` + +### Parameters + +To soft delete a record, pass its ID as a parameter of the method. + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of soft-deleted records' IDs. + +For example, the returned object of the above example is: + +```ts +deletedPosts = { + post_id: ["123"], +} +``` + +*** + +## Soft Delete Multiple Records + +```ts +const deletedPosts = await postModuleService.softDeletePosts([ + "123", + "321", +]) +``` + +### Parameters + +To soft delete multiple records, pass an array of IDs as a parameter of the method. + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of soft-deleted records' IDs. + +For example, the returned object of the above example is: + +```ts +deletedPosts = { + post_id: [ + "123", + "321", + ], +} +``` + +*** + +## Soft Delete Records Matching Filters + +```ts +const deletedPosts = await postModuleService.softDeletePosts({ + name: "My Post", +}) +``` + +### Parameters + +To soft delete records matching a set of filters, pass an object of filters as a parameter. + +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of soft-deleted records' IDs. + +For example, the returned object of the above example is: + +```ts +deletedPosts = { + post_id: ["123"], +} +``` + + +# update Method - Service Factory Reference + +This method updates one or more records of the data model. + +## Update One Record + +```ts +const post = await postModuleService.updatePosts({ + id: "123", + name: "My Post", +}) +``` + +### Parameters + +To update one record, pass an object that at least has an `id` property, identifying the ID of the record to update. + +You can pass in the same object any other properties to update. + +### Returns + +The method returns the updated record as an object. + +*** + +## Update Multiple Records + +```ts +const posts = await postModuleService.updatePosts([ + { + id: "123", + name: "My Post", + }, + { + id: "321", + published_at: new Date(), + }, +]) +``` + +### Parameters + +To update multiple records, pass an array of objects. Each object has at least an `id` property, identifying the ID of the record to update. + +You can pass in each object any other properties to update. + +### Returns + +The method returns an array of objects of updated records. + +*** + +## Update Records Matching a Filter + +```ts +const posts = await postModuleService.updatePosts({ + selector: { + name: "My Post", + }, + data: { + published_at: new Date(), + }, +}) +``` + +### Parameters + +To update records that match specified filters, pass as a parameter an object having two properties: + +- `selector`: An object of filters that a record must match to be updated. +- `data`: An object of the properties to update in every record that match the filters in `selector`. + +In the example above, you update the `published_at` property of every post record whose name is `My Post`. + +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). + +### Returns + +The method returns an array of objects of updated records. + +*** + +## Multiple Record Updates with Filters + +```ts +const posts = await postModuleService.updatePosts([ + { + selector: { + name: "My Post", + }, + data: { + published_at: new Date(), + }, + }, + { + selector: { + name: "Another Post", + }, + data: { + metadata: { + external_id: "123", + }, + }, + }, +]) +``` + +### Parameters + +To update records matching different sets of filters, pass an array of objects, each having two properties: + +- `selector`: An object of filters that a record must match to be updated. +- `data`: An object of the properties to update in every record that match the filters in `selector`. + +In the example above, you update the `published_at` property of post records whose name is `My Post`, and update the `metadata` property of post records whose name is `Another Post`. + +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). + +### Returns + +The method returns an array of objects of updated records. + + # listAndCount Method - Service Factory Reference This method retrieves a list of records with the total count. @@ -59953,237 +60163,6 @@ The method returns an array with two items: 2. The second is the total count of records. -# restore Method - Service Factory Reference - -This method restores one or more records of the data model that were [soft-deleted](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/methods/soft-delete/index.html.md). - -## Restore One Record - -```ts -const restoredPosts = await postModuleService.restorePosts("123") -``` - -### Parameters - -To restore one record, pass its ID as a parameter of the method. - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. - -For example, the returned object of the above example is: - -```ts -restoredPosts = { - post_id: ["123"], -} -``` - -*** - -## Restore Multiple Records - -```ts -const restoredPosts = await postModuleService.restorePosts([ - "123", - "321", -]) -``` - -### Parameters - -To restore multiple records, pass an array of IDs as a parameter of the method. - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. - -For example, the returned object of the above example is: - -```ts -restoredPosts = { - post_id: [ - "123", - "321", - ], -} -``` - -*** - -## Restore Records Matching Filters - -```ts -const restoredPosts = await postModuleService.restorePosts({ - name: "My Post", -}) -``` - -### Parameters - -To restore records matching a set of filters, pass an object of fitlers as a parameter of the method. - -Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. - -For example, the returned object of the above example is: - -```ts -restoredPosts = { - post_id: [ - "123", - ], -} -``` - - -# retrieve Method - Service Factory Reference - -This method retrieves one record of the data model by its ID. - -## Retrieve a Record - -```ts -const post = await postModuleService.retrievePost("123") -``` - -### Parameters - -Pass the ID of the record to retrieve. - -### Returns - -The method returns the record as an object. - -*** - -## Retrieve a Record's Relations - -This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). - -```ts -const post = await postModuleService.retrievePost("123", { - relations: ["author"], -}) -``` - -### Parameters - -To retrieve the data model with relations, pass as a second parameter of the method an object with the property `relations`. `relations`'s value is an array of relation names. - -### Returns - -The method returns the record as an object. - -*** - -## Select Properties to Retrieve - -```ts -const post = await postModuleService.retrievePost("123", { - select: ["id", "name"], -}) -``` - -### Parameters - -By default, all of the record's properties are retrieved. To select specific ones, pass in the second object parameter a `select` property. Its value is an array of property names. - -### Returns - -The method returns the record as an object. - - -# softDelete Method - Service Factory Reference - -This method soft deletes one or more records of the data model. - -## Soft Delete One Record - -```ts -const deletedPosts = await postModuleService.softDeletePosts( - "123" -) -``` - -### Parameters - -To soft delete a record, pass its ID as a parameter of the method. - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of soft-deleted records' IDs. - -For example, the returned object of the above example is: - -```ts -deletedPosts = { - post_id: ["123"], -} -``` - -*** - -## Soft Delete Multiple Records - -```ts -const deletedPosts = await postModuleService.softDeletePosts([ - "123", - "321", -]) -``` - -### Parameters - -To soft delete multiple records, pass an array of IDs as a parameter of the method. - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of soft-deleted records' IDs. - -For example, the returned object of the above example is: - -```ts -deletedPosts = { - post_id: [ - "123", - "321", - ], -} -``` - -*** - -## Soft Delete Records Matching Filters - -```ts -const deletedPosts = await postModuleService.softDeletePosts({ - name: "My Post", -}) -``` - -### Parameters - -To soft delete records matching a set of filters, pass an object of filters as a parameter. - -Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of soft-deleted records' IDs. - -For example, the returned object of the above example is: - -```ts -deletedPosts = { - post_id: ["123"], -} -``` - - # Filter Records - Service Factory Reference Many of the service factory's generated methods allow passing filters to perform an operation, such as to update or delete records matching the filters. @@ -60471,128 +60450,149 @@ The following operators are supported by the service factory filtering mechanism |\`$not\`|Inverts the logic of a condition. For example, | -# update Method - Service Factory Reference +# retrieve Method - Service Factory Reference -This method updates one or more records of the data model. +This method retrieves one record of the data model by its ID. -## Update One Record +## Retrieve a Record ```ts -const post = await postModuleService.updatePosts({ - id: "123", +const post = await postModuleService.retrievePost("123") +``` + +### Parameters + +Pass the ID of the record to retrieve. + +### Returns + +The method returns the record as an object. + +*** + +## Retrieve a Record's Relations + +This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). + +```ts +const post = await postModuleService.retrievePost("123", { + relations: ["author"], +}) +``` + +### Parameters + +To retrieve the data model with relations, pass as a second parameter of the method an object with the property `relations`. `relations`'s value is an array of relation names. + +### Returns + +The method returns the record as an object. + +*** + +## Select Properties to Retrieve + +```ts +const post = await postModuleService.retrievePost("123", { + select: ["id", "name"], +}) +``` + +### Parameters + +By default, all of the record's properties are retrieved. To select specific ones, pass in the second object parameter a `select` property. Its value is an array of property names. + +### Returns + +The method returns the record as an object. + + +# restore Method - Service Factory Reference + +This method restores one or more records of the data model that were [soft-deleted](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/methods/soft-delete/index.html.md). + +## Restore One Record + +```ts +const restoredPosts = await postModuleService.restorePosts("123") +``` + +### Parameters + +To restore one record, pass its ID as a parameter of the method. + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. + +For example, the returned object of the above example is: + +```ts +restoredPosts = { + post_id: ["123"], +} +``` + +*** + +## Restore Multiple Records + +```ts +const restoredPosts = await postModuleService.restorePosts([ + "123", + "321", +]) +``` + +### Parameters + +To restore multiple records, pass an array of IDs as a parameter of the method. + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. + +For example, the returned object of the above example is: + +```ts +restoredPosts = { + post_id: [ + "123", + "321", + ], +} +``` + +*** + +## Restore Records Matching Filters + +```ts +const restoredPosts = await postModuleService.restorePosts({ name: "My Post", }) ``` ### Parameters -To update one record, pass an object that at least has an `id` property, identifying the ID of the record to update. - -You can pass in the same object any other properties to update. - -### Returns - -The method returns the updated record as an object. - -*** - -## Update Multiple Records - -```ts -const posts = await postModuleService.updatePosts([ - { - id: "123", - name: "My Post", - }, - { - id: "321", - published_at: new Date(), - }, -]) -``` - -### Parameters - -To update multiple records, pass an array of objects. Each object has at least an `id` property, identifying the ID of the record to update. - -You can pass in each object any other properties to update. - -### Returns - -The method returns an array of objects of updated records. - -*** - -## Update Records Matching a Filter - -```ts -const posts = await postModuleService.updatePosts({ - selector: { - name: "My Post", - }, - data: { - published_at: new Date(), - }, -}) -``` - -### Parameters - -To update records that match specified filters, pass as a parameter an object having two properties: - -- `selector`: An object of filters that a record must match to be updated. -- `data`: An object of the properties to update in every record that match the filters in `selector`. - -In the example above, you update the `published_at` property of every post record whose name is `My Post`. +To restore records matching a set of filters, pass an object of fitlers as a parameter of the method. Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). ### Returns -The method returns an array of objects of updated records. +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. -*** - -## Multiple Record Updates with Filters +For example, the returned object of the above example is: ```ts -const posts = await postModuleService.updatePosts([ - { - selector: { - name: "My Post", - }, - data: { - published_at: new Date(), - }, - }, - { - selector: { - name: "Another Post", - }, - data: { - metadata: { - external_id: "123", - }, - }, - }, -]) +restoredPosts = { + post_id: [ + "123", + ], +} ``` -### Parameters - -To update records matching different sets of filters, pass an array of objects, each having two properties: - -- `selector`: An object of filters that a record must match to be updated. -- `data`: An object of the properties to update in every record that match the filters in `selector`. - -In the example above, you update the `published_at` property of post records whose name is `My Post`, and update the `metadata` property of post records whose name is `Another Post`. - -Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). - -### Returns - -The method returns an array of objects of updated records. - diff --git a/www/apps/book/public/llms.txt b/www/apps/book/public/llms.txt index 99a50dbbde..f2b29f3ce8 100644 --- a/www/apps/book/public/llms.txt +++ b/www/apps/book/public/llms.txt @@ -274,7 +274,7 @@ Businesses of all sizes can use Medusa, from small start ups to large enterprise - [Troubleshooting Guides](https://docs.medusajs.com/resources/troubleshooting/index.html.md): Find solutions for common problems you face during your development. - [Admin Widget Injection Zones](https://docs.medusajs.com/resources/admin-widget-injection-zones/index.html.md): List of zones you can inject admin widgets into. - [Medusa Container Resources](https://docs.medusajs.com/resources/medusa-container-resources/index.html.md): List of resources or dependencies you can resolve from the Medusa container. -- [Events List](https://docs.medusajs.com/resources/events-reference/index.html.md): List of events you can listen to in a subscriber. +- [Events List](https://docs.medusajs.com/resources/references/events/index.html.md): List of events you can listen to in a subscriber. - [Workflows SDK Reference](https://docs.medusajs.com/resources/references/workflows/index.html.md): A reference to the SDK to create workflows in your Medusa application. - [DML Reference](https://docs.medusajs.com/resources/references/data-model/index.html.md): A reference to Medusa's data-modeling language. - [Service Factory Reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md): A reference to the methods generated by the service factory. diff --git a/www/apps/book/utils/redirects.mjs b/www/apps/book/utils/redirects.mjs index bfb4ab2780..76fd5030df 100644 --- a/www/apps/book/utils/redirects.mjs +++ b/www/apps/book/utils/redirects.mjs @@ -281,7 +281,7 @@ const redirects = async () => { }, { source: "/v1/development/events/events-list", - destination: "/resources/events-reference", + destination: "/resources/references/events", permanent: true, }, { diff --git a/www/apps/resources/.content.eslintrc.mjs b/www/apps/resources/.content.eslintrc.mjs index 0262c55946..aaeab75dcc 100644 --- a/www/apps/resources/.content.eslintrc.mjs +++ b/www/apps/resources/.content.eslintrc.mjs @@ -19,12 +19,7 @@ const compat = new FlatCompat({ export default [ { - ignores: [ - "**/references/**/*", - "**/events-reference/**/*", - "**/events/_content/**/*", - "**/events/_content.mdx", - ], + ignores: ["**/references/**/*"], }, { plugins: { diff --git a/www/apps/resources/app/commerce-modules/auth/events/_content.mdx b/www/apps/resources/app/commerce-modules/auth/events/_content.mdx deleted file mode 100644 index 9b645dc463..0000000000 --- a/www/apps/resources/app/commerce-modules/auth/events/_content.mdx +++ /dev/null @@ -1,36 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `auth.password_reset` - - - - - Emitted when a password of a user, customer, or other actor types is reset. - - - - -```ts blockStyle="inline" -{ - entity_id, // The user's identifier, such as their email - token, // The reset token - actor_type // `user`, `customer`, or custom type -} -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/auth/events/page.mdx b/www/apps/resources/app/commerce-modules/auth/events/page.mdx deleted file mode 100644 index 3a571caaba..0000000000 --- a/www/apps/resources/app/commerce-modules/auth/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Auth Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Auth Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/cart/events/_content.mdx b/www/apps/resources/app/commerce-modules/cart/events/_content.mdx deleted file mode 100644 index ddd4bad6a2..0000000000 --- a/www/apps/resources/app/commerce-modules/cart/events/_content.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `cart.created` - - - - - Emitted when a cart is created. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the cart -} -``` - - - - - - - `cart.updated` - - - - - Emitted when a cart is updated. This includes updates to its items, shipping methods, or information. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the cart -} -``` - - - - - - - `cart.region_updated` - - - - - Emitted when the region of a cart is updated. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the cart -} -``` - - - - -
diff --git a/www/apps/resources/app/commerce-modules/cart/events/page.mdx b/www/apps/resources/app/commerce-modules/cart/events/page.mdx deleted file mode 100644 index 5ddc561cf0..0000000000 --- a/www/apps/resources/app/commerce-modules/cart/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Cart Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Cart Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/customer/events/_content.mdx b/www/apps/resources/app/commerce-modules/customer/events/_content.mdx deleted file mode 100644 index 69e43fe809..0000000000 --- a/www/apps/resources/app/commerce-modules/customer/events/_content.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `customer.created` - - - - - Emitted when customers are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the customer -}] -``` - - - - - - - `customer.updated` - - - - - Emitted when customers are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the customer -}] -``` - - - - - - - `customer.deleted` - - - - - Emitted when customers are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the customer -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/customer/events/page.mdx b/www/apps/resources/app/commerce-modules/customer/events/page.mdx deleted file mode 100644 index 35ff76a89e..0000000000 --- a/www/apps/resources/app/commerce-modules/customer/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Customer Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Customer Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/fulfillment/events/_content.mdx b/www/apps/resources/app/commerce-modules/fulfillment/events/_content.mdx deleted file mode 100644 index 16720edd01..0000000000 --- a/www/apps/resources/app/commerce-modules/fulfillment/events/_content.mdx +++ /dev/null @@ -1,55 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `shipment.created` - - - - - Emitted when an admin user creates a shipment. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the shipment -} -``` - - - - - - - `delivery.created` - - - - - Emitted when the admin user marks an order fulfillment as delivered. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the fulfillment -} -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx b/www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx deleted file mode 100644 index f5d4185be2..0000000000 --- a/www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Fulfillment Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Fulfillment Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/order/events/_content.mdx b/www/apps/resources/app/commerce-modules/order/events/_content.mdx deleted file mode 100644 index b5e8c83593..0000000000 --- a/www/apps/resources/app/commerce-modules/order/events/_content.mdx +++ /dev/null @@ -1,272 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `order.updated` - - - - - Emitted when an admin user updates an order's details. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the order -} -``` - - - - - - - `order.placed` - - - - - Emitted when the customer completes a cart and an order is placed. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the order -} -``` - - - - - - - `order.canceled` - - - - - Emitted when an order is canceled. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the order -} -``` - - - - - - - `order.completed` - - - - - Emitted when orders are completed. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the order -}] -``` - - - - - - - `order.archived` - - - - - Emitted when orders are archived. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the order -}] -``` - - - - - - - `order.fulfillment_created` - - - - - Emitted when a fulfillment is created for an order. - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - fulfillment_id, // The ID of the fulfillment -} -``` - - - - - - - `order.fulfillment_canceled` - - - - - Emitted when a fulfillment is canceled for an order. - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - fulfillment_id, // The ID of the fulfillment -} -``` - - - - - - - `order.return_requested` - - - - - Emitted when a return is requested. - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - return_id, // The ID of the return -} -``` - - - - - - - `order.return_received` - - - - - Emitted when a return is received. - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - return_id, // The ID of the return -} -``` - - - - - - - `order.claim_created` - - - - - Emitted when a claim is created. - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - claim_id, // The ID of the claim -} -``` - - - - - - - `order.exchange_created` - - - - - Emitted when an exchange is created. - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - exchange_id, // The ID of the exchange -} -``` - - - - - - - `order.transfer_requested` - - - - - Emitted when an order transfer is requested. This is available from [Medusa v2.0.5+](https://github.com/medusajs/medusa/releases/tag/v2.0.5). - - - - -```ts blockStyle="inline" -{ - order_id, // The ID of the order - exchange_id, // The ID of the exchange -} -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/order/events/page.mdx b/www/apps/resources/app/commerce-modules/order/events/page.mdx deleted file mode 100644 index 0078b870d0..0000000000 --- a/www/apps/resources/app/commerce-modules/order/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Order Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Order Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/payment/events/_content.mdx b/www/apps/resources/app/commerce-modules/payment/events/_content.mdx deleted file mode 100644 index 41316a718c..0000000000 --- a/www/apps/resources/app/commerce-modules/payment/events/_content.mdx +++ /dev/null @@ -1,55 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `payment.captured` - - - - - Emitted when a payment is captured. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the payment -} -``` - - - - - - - `payment.refunded` - - - - - Emitted when a payment is refunded. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the payment -} -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/payment/events/page.mdx b/www/apps/resources/app/commerce-modules/payment/events/page.mdx deleted file mode 100644 index 103449308f..0000000000 --- a/www/apps/resources/app/commerce-modules/payment/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Payment Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Payment Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product-category.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product-category.mdx deleted file mode 100644 index 875f862e8c..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product-category.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product-category.created` - - - - - Emitted when product categories are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the category -}] -``` - - - - - - - `product-category.updated` - - - - - Emitted when product categories are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the category -}] -``` - - - - - - - `product-category.deleted` - - - - - Emitted when product categories are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the category -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product-collection.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product-collection.mdx deleted file mode 100644 index fe9d7b8070..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product-collection.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product-collection.created` - - - - - Emitted when product collections are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the collection -}] -``` - - - - - - - `product-collection.updated` - - - - - Emitted when product collections are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the collection -}] -``` - - - - - - - `product-collection.deleted` - - - - - Emitted when product collections are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the collection -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product-option.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product-option.mdx deleted file mode 100644 index 9d612ec87c..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product-option.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product-option.created` - - - - - Emitted when product options are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the option -}] -``` - - - - - - - `product-option.updated` - - - - - Emitted when product options are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the option -}] -``` - - - - - - - `product-option.deleted` - - - - - Emitted when product options are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the option -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product-tag.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product-tag.mdx deleted file mode 100644 index 8bb8f3dc94..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product-tag.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product-tag.created` - - - - - Emitted when product tags are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the tag -}] -``` - - - - - - - `product-tag.updated` - - - - - Emitted when product tags are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the tag -}] -``` - - - - - - - `product-tag.deleted` - - - - - Emitted when product tags are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the tag -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product-type.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product-type.mdx deleted file mode 100644 index 70f3538eba..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product-type.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product-type.created` - - - - - Emitted when product types are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the type -}] -``` - - - - - - - `product-type.updated` - - - - - Emitted when product types are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the type -}] -``` - - - - - - - `product-type.deleted` - - - - - Emitted when product types are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the type -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product-variant.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product-variant.mdx deleted file mode 100644 index a1e439a633..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product-variant.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product-variant.created` - - - - - Emitted when product variants are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the variant -}] -``` - - - - - - - `product-variant.updated` - - - - - Emitted when product variants are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the variant -}] -``` - - - - - - - `product-variant.deleted` - - - - - Emitted when product variants are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the variant -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/_content/product.mdx b/www/apps/resources/app/commerce-modules/product/events/_content/product.mdx deleted file mode 100644 index 2bb2832732..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/_content/product.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `product.created` - - - - - Emitted when products are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the product -}] -``` - - - - - - - `product.updated` - - - - - Emitted when products are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the product -}] -``` - - - - - - - `product.deleted` - - - - - Emitted when products are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the product -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/product/events/page.mdx b/www/apps/resources/app/commerce-modules/product/events/page.mdx deleted file mode 100644 index ed952532c2..0000000000 --- a/www/apps/resources/app/commerce-modules/product/events/page.mdx +++ /dev/null @@ -1,55 +0,0 @@ -import ProductEvents from "./_content/product.mdx" -import ProductCategoryEvents from "./_content/product-category.mdx" -import ProductCollectionEvents from "./_content/product-collection.mdx" -import ProductOptionEvents from "./_content/product-option.mdx" -import ProductTagEvents from "./_content/product-tag.mdx" -import ProductTypeEvents from "./_content/product-type.mdx" -import ProductVariantEvents from "./_content/product-variant.mdx" - -export const metadata = { - title: `Product Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Product Module. If you use the module outside the Medusa application, these events aren't emitted. - -## Product Events - - - ---- - -## Product Category Events - - - ---- - -## Product Collection Events - - - ---- - -## Product Option Events - - - ---- - -## Product Tag Events - - - ---- - -## Product Type Events - - - ---- - -## Product Variant Events - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/region/events/_content.mdx b/www/apps/resources/app/commerce-modules/region/events/_content.mdx deleted file mode 100644 index 5a40a112fc..0000000000 --- a/www/apps/resources/app/commerce-modules/region/events/_content.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `region.created` - - - - - Emitted when regions are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the region -}] -``` - - - - - - - `region.updated` - - - - - Emitted when regions are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the region -}] -``` - - - - - - - `region.deleted` - - - - - Emitted when regions are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the region -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/region/events/page.mdx b/www/apps/resources/app/commerce-modules/region/events/page.mdx deleted file mode 100644 index 2cf19939d6..0000000000 --- a/www/apps/resources/app/commerce-modules/region/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Region Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Region Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/sales-channel/events/_content.mdx b/www/apps/resources/app/commerce-modules/sales-channel/events/_content.mdx deleted file mode 100644 index fbd45b84a6..0000000000 --- a/www/apps/resources/app/commerce-modules/sales-channel/events/_content.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `sales-channel.created` - - - - - Emitted when sales channels are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the sales channel -}] -``` - - - - - - - `sales-channel.updated` - - - - - Emitted when sales channels are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the sales channel -}] -``` - - - - - - - `sales-channel.deleted` - - - - - Emitted when sales channels are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the sales channel -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx b/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx deleted file mode 100644 index 0de96f43c3..0000000000 --- a/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ -import Content from "./_content.mdx" - -export const metadata = { - title: `Sales Channel Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the Sales Channel Module. If you use the module outside the Medusa application, these events aren't emitted. - - \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/user/events/_content/invite.mdx b/www/apps/resources/app/commerce-modules/user/events/_content/invite.mdx deleted file mode 100644 index 0552b8e732..0000000000 --- a/www/apps/resources/app/commerce-modules/user/events/_content/invite.mdx +++ /dev/null @@ -1,97 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `invite.accepted` - - - - - Emitted when an invite is accepted. - - - - -```ts blockStyle="inline" -{ - id, // The ID of the invite -} -``` - - - - - - - `invite.created` - - - - - Emitted when invites are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the invite -}] -``` - - - - - - - `invite.deleted` - - - - - Emitted when invites are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the invite -}] -``` - - - - - - - `invite.resent` - - - - - Emitted when invites are resent. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the invite -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/user/events/_content/user.mdx b/www/apps/resources/app/commerce-modules/user/events/_content/user.mdx deleted file mode 100644 index 0459869798..0000000000 --- a/www/apps/resources/app/commerce-modules/user/events/_content/user.mdx +++ /dev/null @@ -1,76 +0,0 @@ -import { Table } from "docs-ui" - - - - - Event Name - Description - Payload - - - - - - - `user.created` - - - - - Emitted when users are created. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the user -}] -``` - - - - - - - `user.updated` - - - - - Emitted when users are updated. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the user -}] -``` - - - - - - - `user.deleted` - - - - - Emitted when users are deleted. - - - - -```ts blockStyle="inline" -[{ - id, // The ID of the user -}] -``` - - - - -
\ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/user/events/page.mdx b/www/apps/resources/app/commerce-modules/user/events/page.mdx deleted file mode 100644 index e754a49d49..0000000000 --- a/www/apps/resources/app/commerce-modules/user/events/page.mdx +++ /dev/null @@ -1,20 +0,0 @@ -import InviteEvents from "./_content/invite.mdx" -import UserEvents from "./_content/user.mdx" - -export const metadata = { - title: `User Module Events Reference`, -} - -# {metadata.title} - -This reference shows all the events emitted by the Medusa application related to the User Module. If you use the module outside the Medusa application, these events aren't emitted. - -## Invite Events - - - ---- - -## User Events - - \ No newline at end of file diff --git a/www/apps/resources/app/events-reference/page.mdx b/www/apps/resources/app/events-reference/page.mdx deleted file mode 100644 index 82e0d67811..0000000000 --- a/www/apps/resources/app/events-reference/page.mdx +++ /dev/null @@ -1,126 +0,0 @@ -import { Table } from "docs-ui" -import AuthEvents from "../commerce-modules/auth/events/_content.mdx" -import CartEvents from "../commerce-modules/cart/events/_content.mdx" -import CustomerEvents from "../commerce-modules/customer/events/_content.mdx" -import FulfillmentEvents from "../commerce-modules/fulfillment/events/_content.mdx" -import InviteEvents from "../commerce-modules/user/events/_content/invite.mdx" -import OrderEvents from "../commerce-modules/order/events/_content.mdx" -import PaymentEvents from "../commerce-modules/payment/events/_content.mdx" -import ProductEvents from "../commerce-modules/product/events/_content/product.mdx" -import ProductCategoryEvents from "../commerce-modules/product/events/_content/product-category.mdx" -import ProductCollectionEvents from "../commerce-modules/product/events/_content/product-collection.mdx" -import ProductOptionEvents from "../commerce-modules/product/events/_content/product-option.mdx" -import ProductTagEvents from "../commerce-modules/product/events/_content/product-tag.mdx" -import ProductTypeEvents from "../commerce-modules/product/events/_content/product-type.mdx" -import ProductVariantEvents from "../commerce-modules/product/events/_content/product-variant.mdx" -import RegionEvents from "../commerce-modules/region/events/_content.mdx" -import SalesChannelEvents from "../commerce-modules/sales-channel/events/_content.mdx" -import UserEvents from "../commerce-modules/user/events/_content/user.mdx" - -export const metadata = { - title: `Events Reference`, -} - -# {metadata.title} - -This documentation page includes the list of all events emitted by [Medusa's workflows](../medusa-workflows-reference/page.mdx). - -## Auth Events - - - ---- - -## Cart Events - - - ---- - -## Customer Events - - - ---- - -## Fulfillment Events - - - ---- - -## Invite Events - - - ---- - -## Order Events - - - ---- - -## Payment Events - - - ---- - -## Product Events - - - ---- - -## Product Category Events - - - ---- - -## Product Collection Events - - - ---- - -## Product Option Events - - - ---- - -## Product Tag Events - - - ---- - -## Product Type Events - - - ---- - -## Product Variant Events - - - ---- - -## Region Events - - - ---- - -## Sales Channel Events - - - ---- - -## User Events - - diff --git a/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx b/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx index eae71aa331..225c297174 100644 --- a/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx +++ b/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx @@ -2124,7 +2124,7 @@ You can confirm that the loyalty points were deducted either by sending a reques You've now implement a loyalty points system in Medusa. There's still more that you can implement based on your use case: -- Add loyalty points on registration or other events. Refer to the [Events Reference](../../../events-reference/page.mdx) for a full list of available events you can listen to. +- Add loyalty points on registration or other events. Refer to the [Events Reference](/references/events) for a full list of available events you can listen to. - Show the customer their loyalty point usage history. This will require adding another data model in the Loyalty Module that records the usage history. You can create records of that data model when an order that has a loyalty promotion is placed, then customize the storefront to show a new page for loyalty points history. - Customize the Medusa Admin to show a new page or [UI Route](!docs!/learn/fundamentals/admin/ui-routes) for loyalty points information and analytics. diff --git a/www/apps/resources/app/integrations/guides/contentful/page.mdx b/www/apps/resources/app/integrations/guides/contentful/page.mdx index a7e5027d83..2a49bbc949 100644 --- a/www/apps/resources/app/integrations/guides/contentful/page.mdx +++ b/www/apps/resources/app/integrations/guides/contentful/page.mdx @@ -3116,7 +3116,7 @@ You've now integrated Contentful with Medusa and supported localized product det 1. Add support for other data types, such as product categories or collections. - Refer to the data model references for each [Commerce Module](../../../commerce-modules/page.mdx) to figure out the content types you need to create in Contentful. 2. Listen to other product events and update the Contentful entries accordingly. - - Refer to the [Events Reference](../../../events-reference/page.mdx) for details on all events emitted in Medusa. + - Refer to the [Events Reference](/references/events) for details on all events emitted in Medusa. 3. Add localization for the entire Next.js Starter Storefront. You can either: - Create content types in Contentful for different sections in the storefront, then use them to retrieve the localized content; - Or use the approaches recommended in the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/routing/internationalization). diff --git a/www/apps/resources/app/integrations/guides/resend/page.mdx b/www/apps/resources/app/integrations/guides/resend/page.mdx index 9ca1d988e6..cb14f2a39d 100644 --- a/www/apps/resources/app/integrations/guides/resend/page.mdx +++ b/www/apps/resources/app/integrations/guides/resend/page.mdx @@ -1385,7 +1385,7 @@ If you check the inbox of the email address you specified in the shipping addres ## Next Steps -You've now integrated Medusa with Resend. You can add more templates for other emails, such as customer registration confirmation, user invites, and more. Check out the [Events Reference](../../../events-reference/page.mdx) for a list of all events that the Medusa application emits. +You've now integrated Medusa with Resend. You can add more templates for other emails, such as customer registration confirmation, user invites, and more. Check out the [Events Reference](/references/events) for a list of all events that the Medusa application emits. If you're new to Medusa, check out the [main documentation](!docs!/learn), where you'll get a more in-depth learning of all the concepts you've used in this guide and more. diff --git a/www/apps/resources/app/integrations/guides/sanity/page.mdx b/www/apps/resources/app/integrations/guides/sanity/page.mdx index 21d2f0a5a4..c71b3a9f0d 100644 --- a/www/apps/resources/app/integrations/guides/sanity/page.mdx +++ b/www/apps/resources/app/integrations/guides/sanity/page.mdx @@ -915,7 +915,7 @@ Subscribers are useful when you want to perform an action that isn't an integral -Learn more about events and subscribers in [this documentation](!docs!/learn/fundamentals/events-and-subscribers). You can also find the list of emitted events in [this reference](../../../events-reference/page.mdx). +Learn more about events and subscribers in [this documentation](!docs!/learn/fundamentals/events-and-subscribers). You can also find the list of emitted events in [this reference](/references/events). diff --git a/www/apps/resources/app/nextjs-starter/guides/revalidate-cache/page.mdx b/www/apps/resources/app/nextjs-starter/guides/revalidate-cache/page.mdx index dd65c3a41d..d0073ddf91 100644 --- a/www/apps/resources/app/nextjs-starter/guides/revalidate-cache/page.mdx +++ b/www/apps/resources/app/nextjs-starter/guides/revalidate-cache/page.mdx @@ -20,7 +20,7 @@ You're free to choose the approach that works for your use case, custom requirem -Refer to the [Events Reference](../../../events-reference/page.mdx) for a full list of events that the Medusa application emits. +Refer to the [Events Reference](/references/events) for a full list of events that the Medusa application emits. diff --git a/www/apps/resources/app/recipes/commerce-automation/page.mdx b/www/apps/resources/app/recipes/commerce-automation/page.mdx index 7ce9ed50d8..f22c632dab 100644 --- a/www/apps/resources/app/recipes/commerce-automation/page.mdx +++ b/www/apps/resources/app/recipes/commerce-automation/page.mdx @@ -37,7 +37,7 @@ You can use the Notification Module to send notifications when an action is trig -The [Events reference](../../events-reference/page.mdx) shows an extensive list of events triggered for each Commerce Module. +The [Events reference](/references/events) shows an extensive list of events triggered for each Commerce Module. @@ -92,7 +92,7 @@ To handle events within an order flow and automate actions, create a subscriber. icon: AcademicCapSolid, }, { - href: "/events-reference", + href: "/references/events", title: "Events Reference", text: "Check out triggered events by each Commerce Module.", icon: AcademicCapSolid, @@ -162,7 +162,7 @@ For example, to group customers with over twenty orders: icon: AcademicCapSolid, }, { - href: "/events-reference", + href: "/references/events", title: "Events Reference", text: "Check out triggered events by each Commerce Module.", icon: AcademicCapSolid, diff --git a/www/apps/resources/components/MDXComponents/index.tsx b/www/apps/resources/components/MDXComponents/index.tsx index 85dedcae7f..f3ed22bf28 100644 --- a/www/apps/resources/components/MDXComponents/index.tsx +++ b/www/apps/resources/components/MDXComponents/index.tsx @@ -7,6 +7,7 @@ import { SourceCodeLink, CodeTabs, CodeTab, + Table, } from "docs-ui" import { CommerceModuleSections } from "../CommerceModuleSections" @@ -19,6 +20,7 @@ const MDXComponents: MDXComponentsType = { SourceCodeLink, CodeTabs, CodeTab, + Table, } export default MDXComponents diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs index 84b25d74c3..c9f1defdea 100644 --- a/www/apps/resources/generated/edit-dates.mjs +++ b/www/apps/resources/generated/edit-dates.mjs @@ -110,7 +110,7 @@ export const generatedEditDates = { "app/medusa-workflows-reference/page.mdx": "2025-01-20T08:21:29.962Z", "app/nextjs-starter/page.mdx": "2025-02-26T11:37:47.137Z", "app/recipes/b2b/page.mdx": "2025-04-17T08:48:38.369Z", - "app/recipes/commerce-automation/page.mdx": "2025-04-17T08:48:37.663Z", + "app/recipes/commerce-automation/page.mdx": "2025-05-01T15:33:47.062Z", "app/recipes/digital-products/examples/standard/page.mdx": "2025-04-24T15:41:05.364Z", "app/recipes/digital-products/page.mdx": "2025-04-17T08:29:01.230Z", "app/recipes/ecommerce/page.mdx": "2025-02-26T12:20:52.092Z", @@ -199,7 +199,6 @@ export const generatedEditDates = { "app/infrastructure-modules/notification/sendgrid/page.mdx": "2025-03-27T17:39:10.840Z", "app/commerce-modules/api-key/concepts/page.mdx": "2024-10-07T13:59:37.529Z", "app/infrastructure-modules/workflow-engine/page.mdx": "2025-04-17T08:29:00.881Z", - "app/_events-reference/page.mdx": "2024-07-03T19:27:13+03:00", "app/infrastructure-modules/cache/page.mdx": "2025-04-17T08:29:00.741Z", "app/infrastructure-modules/file/s3/page.mdx": "2025-02-11T16:57:46.709Z", "app/commerce-modules/api-key/_events/page.mdx": "2024-07-03T19:27:13+03:00", @@ -228,7 +227,7 @@ export const generatedEditDates = { "references/core_flows/Order/functions/core_flows.Order.updateOrderEditAddItemValidationStep/page.mdx": "2024-08-20T00:10:59.065Z", "references/core_flows/Order/functions/core_flows.Order.updateOrderEditAddItemWorkflow/page.mdx": "2024-08-20T00:10:59.105Z", "references/core_flows/core_flows.Order/page.mdx": "2025-04-23T16:21:19.028Z", - "references/modules/core_flows/page.mdx": "2025-04-23T16:21:18.087Z", + "references/modules/core_flows/page.mdx": "2025-05-01T15:18:36.397Z", "references/types/types.HttpTypes/page.mdx": "2025-04-11T09:04:46.305Z", "app/troubleshooting/medusa-admin/no-widget-route/page.mdx": "2025-03-11T08:57:17.255Z", "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.createProviderIdentities/page.mdx": "2025-04-11T09:04:43.772Z", @@ -248,25 +247,25 @@ export const generatedEditDates = { "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.listAuthIdentities/page.mdx": "2025-04-11T09:04:43.744Z", "references/auth/interfaces/auth.FilterableAuthIdentityProps/page.mdx": "2024-12-23T13:57:04.094Z", "references/auth/interfaces/auth.IAuthModuleService/page.mdx": "2024-11-25T17:49:28.823Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.completeCartWorkflow/page.mdx": "2025-04-23T16:21:18.297Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.completeCartWorkflow/page.mdx": "2025-05-01T15:18:36.623Z", "references/core_flows/Cart/core_flows.Cart.Workflows_Cart/page.mdx": "2025-03-04T13:33:40.472Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.createRemoteLinkStep/page.mdx": "2025-04-11T09:04:35.926Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.dismissRemoteLinkStep/page.mdx": "2025-04-11T09:04:35.929Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.updateRemoteLinksStep/page.mdx": "2025-04-11T09:04:35.941Z", - "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.batchLinksWorkflow/page.mdx": "2025-04-23T16:21:18.391Z", - "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.createLinksWorkflow/page.mdx": "2025-04-23T16:21:18.393Z", - "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.dismissLinksWorkflow/page.mdx": "2025-04-23T16:21:18.395Z", - "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.updateLinksWorkflow/page.mdx": "2025-04-23T16:21:18.398Z", + "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.batchLinksWorkflow/page.mdx": "2025-05-01T15:18:36.716Z", + "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.createLinksWorkflow/page.mdx": "2025-05-01T15:18:36.718Z", + "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.dismissLinksWorkflow/page.mdx": "2025-05-01T15:18:36.720Z", + "references/core_flows/Common/Workflows_Common/functions/core_flows.Common.Workflows_Common.updateLinksWorkflow/page.mdx": "2025-05-01T15:18:36.723Z", "references/core_flows/Common/core_flows.Common.Steps_Common/page.mdx": "2024-12-23T12:30:23.689Z", "references/core_flows/Common/core_flows.Common.Workflows_Common/page.mdx": "2024-12-23T12:30:23.702Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createFulfillmentStep/page.mdx": "2025-04-11T09:04:36.222Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createReturnFulfillmentStep/page.mdx": "2025-04-11T09:04:36.246Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.updateFulfillmentStep/page.mdx": "2025-01-13T18:05:49.603Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.upsertShippingOptionsStep/page.mdx": "2025-04-11T09:04:36.337Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createFulfillmentWorkflow/page.mdx": "2025-04-23T16:21:18.864Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createReturnFulfillmentWorkflow/page.mdx": "2025-04-23T16:21:18.871Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createShipmentWorkflow/page.mdx": "2025-04-23T16:21:18.882Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateFulfillmentWorkflow/page.mdx": "2025-04-23T16:21:18.917Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createFulfillmentWorkflow/page.mdx": "2025-05-01T15:18:37.214Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createReturnFulfillmentWorkflow/page.mdx": "2025-05-01T15:18:37.221Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createShipmentWorkflow/page.mdx": "2025-05-01T15:18:37.233Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateFulfillmentWorkflow/page.mdx": "2025-05-01T15:18:37.270Z", "references/core_flows/Fulfillment/core_flows.Fulfillment.Steps_Fulfillment/page.mdx": "2024-12-23T12:30:23.835Z", "references/core_flows/Fulfillment/core_flows.Fulfillment.Workflows_Fulfillment/page.mdx": "2024-12-23T12:30:23.905Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createCompleteReturnStep/page.mdx": "2025-04-11T09:04:37.117Z", @@ -275,111 +274,111 @@ export const generatedEditDates = { "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createOrderExchangesStep/page.mdx": "2025-04-11T09:04:37.029Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createReturnsStep/page.mdx": "2025-04-11T09:04:37.146Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.previewOrderChangeStep/page.mdx": "2025-04-11T09:04:37.071Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrderChangesStep/page.mdx": "2025-04-23T16:21:19.340Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginClaimOrderWorkflow/page.mdx": "2025-04-23T16:21:19.423Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginExchangeOrderWorkflow/page.mdx": "2025-04-23T16:21:19.868Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginOrderEditOrderWorkflow/page.mdx": "2025-04-23T16:21:20.134Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginReceiveReturnWorkflow/page.mdx": "2025-04-23T16:21:20.386Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginReturnOrderWorkflow/page.mdx": "2025-04-23T16:21:20.405Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrderChangesStep/page.mdx": "2025-05-01T15:18:37.614Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginClaimOrderWorkflow/page.mdx": "2025-05-01T15:18:37.706Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginExchangeOrderWorkflow/page.mdx": "2025-05-01T15:18:38.162Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginOrderEditOrderWorkflow/page.mdx": "2025-05-01T15:18:38.449Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginReceiveReturnWorkflow/page.mdx": "2025-05-01T15:18:38.706Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginReturnOrderWorkflow/page.mdx": "2025-05-01T15:18:38.726Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderClaimValidationStep/page.mdx": "2025-04-11T09:04:37.400Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderEditValidationStep/page.mdx": "2025-04-11T09:04:38.789Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderExchangeValidationStep/page.mdx": "2025-04-11T09:04:38.253Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelClaimValidateOrderStep/page.mdx": "2025-04-23T16:21:19.440Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelExchangeValidateOrder/page.mdx": "2025-04-23T16:21:19.885Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderFulfillmentValidateOrder/page.mdx": "2025-04-11T09:04:37.319Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderFulfillmentWorkflow/page.mdx": "2025-04-23T16:21:19.396Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderFulfillmentWorkflow/page.mdx": "2025-05-01T15:18:37.676Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReceiveReturnValidationStep/page.mdx": "2025-04-11T09:04:39.395Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelRequestReturnValidationStep/page.mdx": "2025-04-11T09:04:39.419Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmClaimRequestValidationStep/page.mdx": "2025-04-11T09:04:37.588Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmClaimRequestWorkflow/page.mdx": "2025-04-23T16:21:19.542Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmClaimRequestWorkflow/page.mdx": "2025-05-01T15:18:37.830Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmExchangeRequestValidationStep/page.mdx": "2025-04-11T09:04:38.284Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmExchangeRequestWorkflow/page.mdx": "2025-04-23T16:21:19.911Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmExchangeRequestWorkflow/page.mdx": "2025-05-01T15:18:38.207Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmOrderEditRequestValidationStep/page.mdx": "2025-04-11T09:04:38.808Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmOrderEditRequestWorkflow/page.mdx": "2025-04-23T16:21:20.167Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmOrderEditRequestWorkflow/page.mdx": "2025-05-01T15:18:38.482Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmReceiveReturnValidationStep/page.mdx": "2025-04-11T09:04:39.455Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmReturnReceiveWorkflow/page.mdx": "2025-04-23T16:21:20.482Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmReturnReceiveWorkflow/page.mdx": "2025-05-01T15:18:38.783Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmReturnRequestValidationStep/page.mdx": "2025-04-11T09:04:39.503Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmReturnRequestWorkflow/page.mdx": "2025-04-23T16:21:20.532Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.confirmReturnRequestWorkflow/page.mdx": "2025-05-01T15:18:38.810Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createClaimShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:37.638Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createClaimShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:19.568Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createClaimShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:37.856Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createExchangeShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:38.334Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createExchangeShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:19.936Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderChangeWorkflow/page.mdx": "2025-04-23T16:21:19.774Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createExchangeShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.233Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderChangeWorkflow/page.mdx": "2025-05-01T15:18:38.059Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderEditShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:38.856Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderEditShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.191Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderFulfillmentWorkflow/page.mdx": "2025-04-23T16:21:19.745Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderShipmentWorkflow/page.mdx": "2025-04-23T16:21:19.839Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrdersWorkflow/page.mdx": "2025-04-23T16:21:19.824Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderEditShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.507Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderFulfillmentWorkflow/page.mdx": "2025-05-01T15:18:38.028Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderShipmentWorkflow/page.mdx": "2025-05-01T15:18:38.131Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrdersWorkflow/page.mdx": "2025-05-01T15:18:38.114Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createReturnShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:39.584Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createReturnShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.573Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createReturnShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.855Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.dismissItemReturnRequestValidationStep/page.mdx": "2025-04-11T09:04:39.627Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.dismissItemReturnRequestWorkflow/page.mdx": "2025-04-23T16:21:20.594Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.dismissItemReturnRequestWorkflow/page.mdx": "2025-05-01T15:18:38.878Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.exchangeAddNewItemValidationStep/page.mdx": "2025-04-11T09:04:38.385Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.exchangeRequestItemReturnValidationStep/page.mdx": "2025-04-23T16:21:19.973Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.getOrdersListWorkflow/page.mdx": "2025-04-23T16:21:20.100Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.getOrdersListWorkflow/page.mdx": "2025-05-01T15:18:38.414Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimAddNewItemValidationStep/page.mdx": "2025-04-11T09:04:37.433Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimAddNewItemWorkflow/page.mdx": "2025-04-23T16:21:19.466Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimAddNewItemWorkflow/page.mdx": "2025-05-01T15:18:37.753Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimItemValidationStep/page.mdx": "2025-04-11T09:04:37.482Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimItemWorkflow/page.mdx": "2025-04-23T16:21:19.491Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimItemWorkflow/page.mdx": "2025-05-01T15:18:37.777Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimRequestItemReturnValidationStep/page.mdx": "2025-04-11T09:04:37.537Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimRequestItemReturnWorkflow/page.mdx": "2025-04-23T16:21:19.518Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderClaimRequestItemReturnWorkflow/page.mdx": "2025-05-01T15:18:37.805Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderEditAddNewItemValidationStep/page.mdx": "2025-04-11T09:04:38.900Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderEditAddNewItemWorkflow/page.mdx": "2025-04-23T16:21:20.215Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderEditAddNewItemWorkflow/page.mdx": "2025-05-01T15:18:38.530Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderEditUpdateItemQuantityValidationStep/page.mdx": "2025-04-11T09:04:38.995Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderEditUpdateItemQuantityWorkflow/page.mdx": "2025-04-23T16:21:20.237Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderExchangeAddNewItemWorkflow/page.mdx": "2025-04-23T16:21:19.962Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderExchangeRequestItemReturnWorkflow/page.mdx": "2025-04-23T16:21:19.990Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderEditUpdateItemQuantityWorkflow/page.mdx": "2025-05-01T15:18:38.553Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderExchangeAddNewItemWorkflow/page.mdx": "2025-05-01T15:18:38.259Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderExchangeRequestItemReturnWorkflow/page.mdx": "2025-05-01T15:18:38.288Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.receiveItemReturnRequestValidationStep/page.mdx": "2025-04-11T09:04:39.683Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.receiveItemReturnRequestWorkflow/page.mdx": "2025-04-23T16:21:20.621Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeAddItemClaimActionWorkflow/page.mdx": "2025-04-23T16:21:19.593Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.receiveItemReturnRequestWorkflow/page.mdx": "2025-05-01T15:18:38.906Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeAddItemClaimActionWorkflow/page.mdx": "2025-05-01T15:18:37.880Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeClaimAddItemActionValidationStep/page.mdx": "2025-04-11T09:04:37.694Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeClaimItemActionValidationStep/page.mdx": "2025-04-11T09:04:37.744Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeClaimShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:37.784Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeClaimShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:19.638Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeClaimShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:37.925Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeExchangeItemActionValidationStep/page.mdx": "2025-04-11T09:04:38.489Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeExchangeShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:38.530Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeExchangeShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.033Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemClaimActionWorkflow/page.mdx": "2025-04-23T16:21:19.617Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemExchangeActionWorkflow/page.mdx": "2025-04-23T16:21:20.013Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemOrderEditActionWorkflow/page.mdx": "2025-04-23T16:21:20.260Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeExchangeShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.340Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemClaimActionWorkflow/page.mdx": "2025-05-01T15:18:37.905Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemExchangeActionWorkflow/page.mdx": "2025-05-01T15:18:38.316Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemOrderEditActionWorkflow/page.mdx": "2025-05-01T15:18:38.576Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemReceiveReturnActionValidationStep/page.mdx": "2025-04-11T09:04:39.734Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemReceiveReturnActionWorkflow/page.mdx": "2025-04-23T16:21:20.646Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemReturnActionWorkflow/page.mdx": "2025-04-23T16:21:20.670Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemReceiveReturnActionWorkflow/page.mdx": "2025-05-01T15:18:38.932Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeItemReturnActionWorkflow/page.mdx": "2025-05-01T15:18:38.957Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeOrderEditItemActionValidationStep/page.mdx": "2025-04-11T09:04:39.042Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeOrderEditShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:39.082Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeOrderEditShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.277Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeOrderEditShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.595Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeReturnItemActionValidationStep/page.mdx": "2025-04-11T09:04:39.785Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeReturnShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:39.825Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeReturnShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.691Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.removeReturnShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.979Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestItemReturnValidationStep/page.mdx": "2025-04-11T09:04:39.866Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestItemReturnWorkflow/page.mdx": "2025-04-23T16:21:20.711Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestItemReturnWorkflow/page.mdx": "2025-05-01T15:18:39.000Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestOrderEditRequestValidationStep/page.mdx": "2025-04-11T09:04:39.127Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestOrderEditRequestWorkflow/page.mdx": "2025-04-23T16:21:20.300Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestOrderEditRequestWorkflow/page.mdx": "2025-05-01T15:18:38.617Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimAddItemValidationStep/page.mdx": "2025-04-11T09:04:37.834Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimAddItemWorkflow/page.mdx": "2025-04-23T16:21:19.665Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimAddItemWorkflow/page.mdx": "2025-05-01T15:18:37.950Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimItemValidationStep/page.mdx": "2025-04-11T09:04:37.917Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimItemWorkflow/page.mdx": "2025-04-23T16:21:19.691Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimItemWorkflow/page.mdx": "2025-05-01T15:18:37.974Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:37.960Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:19.713Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateClaimShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:37.996Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateExchangeAddItemValidationStep/page.mdx": "2025-04-11T09:04:38.578Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateExchangeAddItemWorkflow/page.mdx": "2025-04-23T16:21:20.057Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateExchangeAddItemWorkflow/page.mdx": "2025-05-01T15:18:38.366Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateExchangeShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:38.619Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateExchangeShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.078Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderChangesWorkflow/page.mdx": "2025-04-23T16:21:20.897Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateExchangeShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.391Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderChangesWorkflow/page.mdx": "2025-05-01T15:18:39.182Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditAddItemValidationStep/page.mdx": "2025-04-11T09:04:39.176Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditAddItemWorkflow/page.mdx": "2025-04-23T16:21:20.322Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditAddItemWorkflow/page.mdx": "2025-05-01T15:18:38.641Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditItemQuantityValidationStep/page.mdx": "2025-04-11T09:04:39.223Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditItemQuantityWorkflow/page.mdx": "2025-04-23T16:21:20.345Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditItemQuantityWorkflow/page.mdx": "2025-05-01T15:18:38.663Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:39.260Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.364Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderEditShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:38.683Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReceiveItemReturnRequestValidationStep/page.mdx": "2025-04-11T09:04:39.918Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReceiveItemReturnRequestWorkflow/page.mdx": "2025-04-23T16:21:20.737Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReceiveItemReturnRequestWorkflow/page.mdx": "2025-05-01T15:18:39.026Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateRequestItemReturnValidationStep/page.mdx": "2025-04-11T09:04:39.969Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateRequestItemReturnWorkflow/page.mdx": "2025-04-23T16:21:20.762Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateRequestItemReturnWorkflow/page.mdx": "2025-05-01T15:18:39.053Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReturnShippingMethodValidationStep/page.mdx": "2025-04-11T09:04:40.083Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReturnShippingMethodWorkflow/page.mdx": "2025-04-23T16:21:20.803Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReturnShippingMethodWorkflow/page.mdx": "2025-05-01T15:18:39.095Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReturnValidationStep/page.mdx": "2025-04-11T09:04:40.037Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReturnWorkflow/page.mdx": "2025-04-23T16:21:20.782Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateReturnWorkflow/page.mdx": "2025-05-01T15:18:39.073Z", "references/core_flows/Order/core_flows.Order.Steps_Order/page.mdx": "2025-04-23T16:21:19.028Z", "references/core_flows/Order/core_flows.Order.Workflows_Order/page.mdx": "2025-04-23T16:21:19.362Z", "references/core_flows/Shipping_Options/Steps_Shipping_Options/functions/core_flows.Shipping_Options.Steps_Shipping_Options.listShippingOptionsForContextStep/page.mdx": "2025-04-11T09:04:41.407Z", @@ -632,39 +631,39 @@ export const generatedEditDates = { "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.setTaxLinesForItemsStep/page.mdx": "2025-01-17T16:43:22.421Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.updateCartsStep/page.mdx": "2025-04-11T09:04:35.629Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateCartPaymentsStep/page.mdx": "2025-04-11T09:04:35.679Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.addToCartWorkflow/page.mdx": "2025-04-23T16:21:18.294Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.confirmVariantInventoryWorkflow/page.mdx": "2025-04-23T16:21:18.301Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.createCartWorkflow/page.mdx": "2025-04-23T16:21:18.321Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.createPaymentCollectionForCartWorkflow/page.mdx": "2025-04-23T16:21:18.333Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWorkflow/page.mdx": "2025-04-23T16:21:18.344Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateCartWorkflow/page.mdx": "2025-04-23T16:21:18.360Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateLineItemInCartWorkflow/page.mdx": "2025-04-23T16:21:18.367Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateTaxLinesWorkflow/page.mdx": "2025-04-23T16:21:18.373Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.addToCartWorkflow/page.mdx": "2025-05-01T15:18:36.619Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.confirmVariantInventoryWorkflow/page.mdx": "2025-05-01T15:18:36.627Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.createCartWorkflow/page.mdx": "2025-05-01T15:18:36.648Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.createPaymentCollectionForCartWorkflow/page.mdx": "2025-05-01T15:18:36.658Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWorkflow/page.mdx": "2025-05-01T15:18:36.668Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateCartWorkflow/page.mdx": "2025-05-01T15:18:36.684Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateLineItemInCartWorkflow/page.mdx": "2025-05-01T15:18:36.691Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateTaxLinesWorkflow/page.mdx": "2025-05-01T15:18:36.698Z", "references/core_flows/Cart/core_flows.Cart.Steps_Cart/page.mdx": "2025-02-11T11:36:38.987Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.useRemoteQueryStep/page.mdx": "2025-01-13T17:30:23.158Z", - "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createShippingOptionRulesStep/page.mdx": "2025-04-23T16:21:18.809Z", - "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.deleteShippingOptionRulesStep/page.mdx": "2025-04-23T16:21:18.817Z", + "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createShippingOptionRulesStep/page.mdx": "2025-05-01T14:14:04.304Z", + "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.deleteShippingOptionRulesStep/page.mdx": "2025-05-01T15:18:37.166Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrderClaimStep/page.mdx": "2025-01-17T16:43:22.988Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrderExchangeStep/page.mdx": "2025-01-17T16:43:23.041Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrderReturnStep/page.mdx": "2025-01-17T16:43:23.074Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrdersStep/page.mdx": "2025-04-11T09:04:36.797Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderTaxLinesWorkflow/page.mdx": "2025-04-23T16:21:20.923Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderTaxLinesWorkflow/page.mdx": "2025-05-01T15:18:39.208Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.createPriceListPricesStep/page.mdx": "2025-04-11T09:04:40.588Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.createPriceListsStep/page.mdx": "2025-04-11T09:04:40.594Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.updatePriceListPricesStep/page.mdx": "2025-04-11T09:04:40.611Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.batchPriceListPricesWorkflow/page.mdx": "2025-04-23T16:21:21.067Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.createPriceListPricesWorkflow/page.mdx": "2025-04-23T16:21:21.072Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.createPriceListsWorkflow/page.mdx": "2025-04-23T16:21:21.077Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.updatePriceListPricesWorkflow/page.mdx": "2025-04-23T16:21:21.087Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.updatePriceListsWorkflow/page.mdx": "2025-04-23T16:21:21.089Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.batchPriceListPricesWorkflow/page.mdx": "2025-05-01T15:18:39.403Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.createPriceListPricesWorkflow/page.mdx": "2025-05-01T15:18:39.407Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.createPriceListsWorkflow/page.mdx": "2025-05-01T15:18:39.412Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.updatePriceListPricesWorkflow/page.mdx": "2025-05-01T15:18:39.419Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.updatePriceListsWorkflow/page.mdx": "2025-05-01T15:18:39.422Z", "references/core_flows/Price_List/core_flows.Price_List.Steps_Price_List/page.mdx": "2024-12-23T12:30:26.076Z", "references/core_flows/Price_List/core_flows.Price_List.Workflows_Price_List/page.mdx": "2024-12-23T12:30:26.103Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.addRulesToPromotionsStep/page.mdx": "2025-04-11T09:04:41.123Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.removeRulesFromPromotionsStep/page.mdx": "2025-01-13T18:05:52.283Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.updatePromotionRulesStep/page.mdx": "2025-04-11T09:04:41.151Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.createPromotionRulesWorkflow/page.mdx": "2025-04-23T16:21:21.362Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.deletePromotionRulesWorkflow/page.mdx": "2025-04-23T16:21:21.371Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionRulesWorkflow/page.mdx": "2025-04-23T16:21:21.379Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.createPromotionRulesWorkflow/page.mdx": "2025-05-01T15:18:39.754Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.deletePromotionRulesWorkflow/page.mdx": "2025-05-01T15:18:39.762Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionRulesWorkflow/page.mdx": "2025-05-01T15:18:39.769Z", "references/core_flows/Promotion/core_flows.Promotion.Steps_Promotion/page.mdx": "2024-12-23T12:30:26.321Z", "references/core_flows/Promotion/core_flows.Promotion.Workflows_Promotion/page.mdx": "2024-12-23T12:30:26.343Z", "references/core_flows/core_flows.Steps_Cart/page.mdx": "2024-12-09T13:21:37.605Z", @@ -719,41 +718,41 @@ export const generatedEditDates = { "references/cart/interfaces/cart.LineItemAdjustmentDTO/page.mdx": "2025-04-11T09:04:43.840Z", "references/cart/interfaces/cart.LineItemTaxLineDTO/page.mdx": "2025-04-11T09:04:43.862Z", "references/cart/interfaces/cart.UpdateLineItemWithSelectorDTO/page.mdx": "2025-04-11T09:04:43.987Z", - "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.createCartsStep/page.mdx": "2025-04-23T16:21:18.159Z", + "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.createCartsStep/page.mdx": "2025-05-01T15:18:36.473Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.createLineItemsStep/page.mdx": "2025-04-11T09:04:35.478Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.getActionsToComputeFromPromotionsStep/page.mdx": "2025-04-11T09:04:35.520Z", - "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.retrieveCartStep/page.mdx": "2025-04-23T16:21:18.225Z", + "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.retrieveCartStep/page.mdx": "2025-05-01T15:18:36.543Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.updateLineItemsStep/page.mdx": "2025-04-11T09:04:35.657Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateCartShippingOptionsStep/page.mdx": "2025-04-11T09:04:35.692Z", "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.validateExistingPaymentCollectionStep/page.mdx": "2025-04-11T09:04:35.832Z", "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.createCustomersStep/page.mdx": "2025-04-11T09:04:35.983Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.createCustomerAccountWorkflow/page.mdx": "2025-04-23T16:21:18.429Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.createCustomersWorkflow/page.mdx": "2025-04-23T16:21:18.432Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.deleteCustomersWorkflow/page.mdx": "2025-04-23T16:21:18.440Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.updateCustomersWorkflow/page.mdx": "2025-04-23T16:21:18.455Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.createCustomerAccountWorkflow/page.mdx": "2025-05-01T15:18:36.756Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.createCustomersWorkflow/page.mdx": "2025-05-01T15:18:36.759Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.deleteCustomersWorkflow/page.mdx": "2025-05-01T15:18:36.764Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.updateCustomersWorkflow/page.mdx": "2025-05-01T15:18:36.779Z", "references/core_flows/Customer/core_flows.Customer.Steps_Customer/page.mdx": "2024-12-23T12:30:23.718Z", "references/core_flows/Customer/core_flows.Customer.Workflows_Customer/page.mdx": "2024-12-23T12:30:23.763Z", - "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.acceptInviteWorkflow/page.mdx": "2025-04-23T16:21:18.985Z", - "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.createInvitesWorkflow/page.mdx": "2025-04-23T16:21:18.987Z", - "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.deleteInvitesWorkflow/page.mdx": "2025-04-23T16:21:18.989Z", + "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.acceptInviteWorkflow/page.mdx": "2025-05-01T15:18:37.339Z", + "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.createInvitesWorkflow/page.mdx": "2025-05-01T15:18:37.342Z", + "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.deleteInvitesWorkflow/page.mdx": "2025-05-01T15:18:37.344Z", "references/core_flows/Invite/core_flows.Invite.Workflows_Invite/page.mdx": "2024-12-23T12:30:24.034Z", "references/core_flows/Line_Item/Steps_Line_Item/functions/core_flows.Line_Item.Steps_Line_Item.listLineItemsStep/page.mdx": "2025-04-11T09:04:36.677Z", "references/core_flows/Line_Item/Steps_Line_Item/functions/core_flows.Line_Item.Steps_Line_Item.updateLineItemsStepWithSelector/page.mdx": "2025-04-11T09:04:36.705Z", "references/core_flows/Line_Item/core_flows.Line_Item.Steps_Line_Item/page.mdx": "2024-12-23T12:30:24.048Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductsWorkflow/page.mdx": "2025-04-23T16:21:21.298Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductsWorkflow/page.mdx": "2025-05-01T15:18:39.692Z", "references/core_flows/Product/core_flows.Product.Workflows_Product/page.mdx": "2025-01-13T17:30:25.740Z", - "references/core_flows/Region/Workflows_Region/functions/core_flows.Region.Workflows_Region.createRegionsWorkflow/page.mdx": "2025-04-23T16:21:21.398Z", - "references/core_flows/Region/Workflows_Region/functions/core_flows.Region.Workflows_Region.deleteRegionsWorkflow/page.mdx": "2025-04-23T16:21:21.401Z", - "references/core_flows/Region/Workflows_Region/functions/core_flows.Region.Workflows_Region.updateRegionsWorkflow/page.mdx": "2025-04-23T16:21:21.405Z", + "references/core_flows/Region/Workflows_Region/functions/core_flows.Region.Workflows_Region.createRegionsWorkflow/page.mdx": "2025-05-01T15:18:39.788Z", + "references/core_flows/Region/Workflows_Region/functions/core_flows.Region.Workflows_Region.deleteRegionsWorkflow/page.mdx": "2025-05-01T15:18:39.790Z", + "references/core_flows/Region/Workflows_Region/functions/core_flows.Region.Workflows_Region.updateRegionsWorkflow/page.mdx": "2025-05-01T15:18:39.794Z", "references/core_flows/Region/core_flows.Region.Workflows_Region/page.mdx": "2024-12-23T12:30:26.377Z", - "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.createSalesChannelsWorkflow/page.mdx": "2025-04-23T16:21:21.466Z", - "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.deleteSalesChannelsWorkflow/page.mdx": "2025-04-23T16:21:21.468Z", - "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.updateSalesChannelsWorkflow/page.mdx": "2025-04-23T16:21:21.471Z", + "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.createSalesChannelsWorkflow/page.mdx": "2025-05-01T15:18:39.863Z", + "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.deleteSalesChannelsWorkflow/page.mdx": "2025-05-01T15:18:39.865Z", + "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.updateSalesChannelsWorkflow/page.mdx": "2025-05-01T15:18:39.868Z", "references/core_flows/Sales_Channel/core_flows.Sales_Channel.Workflows_Sales_Channel/page.mdx": "2024-12-23T12:30:26.435Z", - "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.createUserAccountWorkflow/page.mdx": "2025-04-23T16:21:21.588Z", - "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.createUsersWorkflow/page.mdx": "2025-04-23T16:21:21.591Z", - "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.deleteUsersWorkflow/page.mdx": "2025-04-23T16:21:21.593Z", - "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.updateUsersWorkflow/page.mdx": "2025-04-23T16:21:21.598Z", + "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.createUserAccountWorkflow/page.mdx": "2025-05-01T15:18:39.988Z", + "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.createUsersWorkflow/page.mdx": "2025-05-01T15:18:39.990Z", + "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.deleteUsersWorkflow/page.mdx": "2025-05-01T15:18:39.992Z", + "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.updateUsersWorkflow/page.mdx": "2025-05-01T15:18:39.997Z", "references/core_flows/User/core_flows.User.Workflows_User/page.mdx": "2024-12-23T12:30:26.574Z", "references/core_flows/core_flows.Steps_Customer/page.mdx": "2024-12-09T13:21:38.061Z", "references/core_flows/core_flows.Steps_Line_Item/page.mdx": "2024-12-09T13:21:39.005Z", @@ -790,12 +789,12 @@ export const generatedEditDates = { "references/types/HttpTypes/interfaces/types.HttpTypes.AdminTaxRegionDeleteResponse/page.mdx": "2024-12-09T13:21:34.725Z", "references/types/HttpTypes/types/types.HttpTypes.DeleteResponse/page.mdx": "2024-12-09T13:21:33.545Z", "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.validateRefundStep/page.mdx": "2025-04-11T09:04:40.478Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.addOrderTransactionStep/page.mdx": "2025-04-23T16:21:19.030Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.addOrderTransactionStep/page.mdx": "2025-05-01T15:18:37.386Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.archiveOrdersStep/page.mdx": "2025-04-11T09:04:36.761Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.completeOrdersStep/page.mdx": "2025-04-11T09:04:36.903Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createOrdersStep/page.mdx": "2025-04-11T09:04:36.982Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.setOrderTaxLinesForItemsStep/page.mdx": "2025-04-11T09:04:37.167Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.archiveOrderWorkflow/page.mdx": "2025-04-23T16:21:19.384Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.archiveOrderWorkflow/page.mdx": "2025-05-01T15:18:37.664Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginClaimOrderValidationStep/page.mdx": "2025-04-11T09:04:37.351Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginOrderEditValidationStep/page.mdx": "2025-04-11T09:04:38.746Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginOrderExchangeValidationStep/page.mdx": "2025-04-11T09:04:38.205Z", @@ -803,13 +802,13 @@ export const generatedEditDates = { "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.beginReturnOrderValidationStep/page.mdx": "2025-04-11T09:04:39.347Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnValidateOrder/page.mdx": "2025-04-11T09:04:39.429Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelValidateOrder/page.mdx": "2025-04-11T09:04:37.336Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.completeOrderWorkflow/page.mdx": "2025-04-23T16:21:19.729Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.completeOrderWorkflow/page.mdx": "2025-05-01T15:18:38.012Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createFulfillmentValidateOrder/page.mdx": "2025-04-11T09:04:38.038Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createShipmentValidateOrder/page.mdx": "2025-04-11T09:04:38.168Z", - "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.refundPaymentWorkflow/page.mdx": "2025-04-23T16:21:20.982Z", + "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.refundPaymentWorkflow/page.mdx": "2025-05-01T15:18:39.322Z", "references/core_flows/Payment/core_flows.Payment.Workflows_Payment/page.mdx": "2025-01-07T12:54:17.428Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.updatePromotionsStep/page.mdx": "2025-04-11T09:04:41.156Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionsWorkflow/page.mdx": "2025-04-23T16:21:21.382Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionsWorkflow/page.mdx": "2025-05-01T15:18:39.772Z", "references/core_flows/core_flows.Workflows_Payment/page.mdx": "2024-12-09T13:21:48.376Z", "references/core_flows/interfaces/core_flows.SetOrderTaxLinesForItemsStepInput/page.mdx": "2025-04-11T09:04:42.017Z", "references/fulfillment/interfaces/fulfillment.OrderSummaryDTO/page.mdx": "2025-02-24T10:48:36.235Z", @@ -843,8 +842,8 @@ export const generatedEditDates = { "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.authenticate/page.mdx": "2025-01-07T12:54:18.941Z", "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.validateCallback/page.mdx": "2025-01-07T12:54:18.948Z", "references/auth/interfaces/auth.AuthenticationResponse/page.mdx": "2024-12-09T13:21:36.233Z", - "references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2025-03-12T12:28:32.072Z", - "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.refreshInviteTokensWorkflow/page.mdx": "2025-04-23T16:21:18.992Z", + "references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2025-05-01T15:18:45.919Z", + "references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.refreshInviteTokensWorkflow/page.mdx": "2025-05-01T15:18:37.347Z", "references/types/CommonTypes/types/types.CommonTypes.BatchMethodResponse/page.mdx": "2024-12-09T13:21:32.849Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminAddReturnShipping/page.mdx": "2025-04-11T09:04:47.540Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaimAddInboundShipping/page.mdx": "2024-12-09T13:21:33.433Z", @@ -886,7 +885,7 @@ export const generatedEditDates = { "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.updateAuthIdentities/page.mdx": "2025-04-11T09:04:43.757Z", "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.updateProvider/page.mdx": "2024-12-09T13:21:36.245Z", "references/auth/IAuthModuleService/methods/auth.IAuthModuleService.updateProviderIdentities/page.mdx": "2025-04-11T09:04:43.777Z", - "references/core_flows/Auth/Workflows_Auth/functions/core_flows.Auth.Workflows_Auth.generateResetPasswordTokenWorkflow/page.mdx": "2025-04-23T16:21:18.136Z", + "references/core_flows/Auth/Workflows_Auth/functions/core_flows.Auth.Workflows_Auth.generateResetPasswordTokenWorkflow/page.mdx": "2025-05-01T15:18:36.450Z", "references/core_flows/Auth/core_flows.Auth.Workflows_Auth/page.mdx": "2024-12-23T12:30:23.507Z", "references/core_flows/core_flows.Workflows_Auth/page.mdx": "2024-12-09T13:21:37.265Z", "references/types/CommonTypes/types/types.CommonTypes.TransformObjectMethodToAsync/page.mdx": "2024-12-23T12:30:28.073Z", @@ -930,9 +929,9 @@ export const generatedEditDates = { "references/core_flows/Auth/core_flows.Auth.Steps_Auth/page.mdx": "2024-12-23T12:30:23.503Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createOrderClaimItemsFromActionsStep/page.mdx": "2025-04-11T09:04:36.809Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createOrderExchangeItemsFromActionsStep/page.mdx": "2025-04-11T09:04:37.037Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrderChangeActionsStep/page.mdx": "2025-04-23T16:21:19.327Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderChangeActionsWorkflow/page.mdx": "2025-04-23T16:21:19.760Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderChangeActionsWorkflow/page.mdx": "2025-04-23T16:21:20.883Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrderChangeActionsStep/page.mdx": "2025-05-01T15:18:37.600Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderChangeActionsWorkflow/page.mdx": "2025-05-01T15:18:38.044Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderChangeActionsWorkflow/page.mdx": "2025-05-01T15:18:39.168Z", "references/core_flows/core_flows.Auth/page.mdx": "2024-12-23T12:30:23.503Z", "references/core_flows/core_flows.Steps_Auth/page.mdx": "2024-12-09T13:21:37.257Z", "references/core_flows/types/core_flows.CreateOrderClaimItemsFromActionsInput/page.mdx": "2025-04-11T09:04:41.976Z", @@ -954,13 +953,13 @@ export const generatedEditDates = { "references/api_key/types/api_key.Query/page.mdx": "2024-09-17T00:10:59.563Z", "references/api_key/types/api_key.ReadonlyPrimary/page.mdx": "2024-09-17T00:10:59.567Z", "references/api_key/types/api_key.Scalar/page.mdx": "2024-09-17T00:10:59.567Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.removeCustomerAccountWorkflow/page.mdx": "2025-04-23T16:21:18.443Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.markFulfillmentAsDeliveredWorkflow/page.mdx": "2025-04-23T16:21:18.911Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.removeCustomerAccountWorkflow/page.mdx": "2025-05-01T15:18:36.766Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.markFulfillmentAsDeliveredWorkflow/page.mdx": "2025-05-01T15:18:37.263Z", "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.validateFulfillmentDeliverabilityStep/page.mdx": "2025-01-20T08:25:20.588Z", "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.validateInventoryLevelsDelete/page.mdx": "2025-01-20T08:25:20.641Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.markOrderFulfillmentAsDeliveredWorkflow/page.mdx": "2025-04-23T16:21:20.110Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.markOrderFulfillmentAsDeliveredWorkflow/page.mdx": "2025-05-01T15:18:38.424Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.orderFulfillmentDeliverablilityValidationStep/page.mdx": "2025-04-11T09:04:38.713Z", - "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.removeUserAccountWorkflow/page.mdx": "2025-04-23T16:21:21.595Z", + "references/core_flows/User/Workflows_User/functions/core_flows.User.Workflows_User.removeUserAccountWorkflow/page.mdx": "2025-05-01T15:18:39.994Z", "references/core_flows/types/core_flows.RemoveCustomerAccountWorkflowInput/page.mdx": "2024-12-09T13:21:50.344Z", "references/core_flows/types/core_flows.RemoveUserAccountWorkflowInput/page.mdx": "2024-12-09T13:21:55.016Z", "references/customer/ICustomerModuleService/methods/customer.ICustomerModuleService.createCustomerAddresses/page.mdx": "2025-04-11T09:04:44.667Z", @@ -1056,53 +1055,53 @@ export const generatedEditDates = { "references/cart/types/cart.JoinerRelationship/page.mdx": "2024-12-09T13:21:36.889Z", "references/core_flows/Api_Key/Steps_Api_Key/functions/core_flows.Api_Key.Steps_Api_Key.revokeApiKeysStep/page.mdx": "2025-04-11T09:04:35.334Z", "references/core_flows/Api_Key/Steps_Api_Key/functions/core_flows.Api_Key.Steps_Api_Key.updateApiKeysStep/page.mdx": "2025-04-11T09:04:35.339Z", - "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.revokeApiKeysWorkflow/page.mdx": "2025-04-23T16:21:18.125Z", - "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.updateApiKeysWorkflow/page.mdx": "2025-04-23T16:21:18.130Z", + "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.revokeApiKeysWorkflow/page.mdx": "2025-05-01T15:18:36.438Z", + "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.updateApiKeysWorkflow/page.mdx": "2025-05-01T15:18:36.443Z", "references/core_flows/Api_Key/core_flows.Api_Key.Steps_Api_Key/page.mdx": "2024-12-23T12:30:23.467Z", "references/core_flows/Api_Key/core_flows.Api_Key.Workflows_Api_Key/page.mdx": "2024-12-23T12:30:23.484Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.findOrCreateCustomerStep/page.mdx": "2025-01-13T17:30:22.980Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.getLineItemActionsStep/page.mdx": "2025-04-11T09:04:35.532Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.createCustomerAddressesWorkflow/page.mdx": "2025-04-23T16:21:18.426Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.deleteCustomerAddressesWorkflow/page.mdx": "2025-04-23T16:21:18.437Z", - "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.updateCustomerAddressesWorkflow/page.mdx": "2025-04-23T16:21:18.449Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.bulkCreateDeleteLevelsWorkflow/page.mdx": "2025-04-23T16:21:18.956Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.deleteInventoryLevelsWorkflow/page.mdx": "2025-04-23T16:21:18.969Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.createCustomerAddressesWorkflow/page.mdx": "2025-05-01T15:18:36.753Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.deleteCustomerAddressesWorkflow/page.mdx": "2025-05-01T15:18:36.762Z", + "references/core_flows/Customer/Workflows_Customer/functions/core_flows.Customer.Workflows_Customer.updateCustomerAddressesWorkflow/page.mdx": "2025-05-01T15:18:36.774Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.bulkCreateDeleteLevelsWorkflow/page.mdx": "2025-05-01T15:18:37.310Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.deleteInventoryLevelsWorkflow/page.mdx": "2025-05-01T15:18:37.323Z", "references/core_flows/Inventory/core_flows.Inventory.Steps_Inventory/page.mdx": "2024-12-23T12:30:23.988Z", "references/core_flows/Inventory/core_flows.Inventory.Workflows_Inventory/page.mdx": "2025-01-13T17:30:23.526Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.createOrderLineItemsStep/page.mdx": "2025-04-23T16:21:19.201Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateReturnsStep/page.mdx": "2025-04-23T16:21:19.312Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.addOrderLineItemsWorkflow/page.mdx": "2025-04-23T16:21:19.370Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderWorkflow/page.mdx": "2025-04-23T16:21:19.404Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createCollectionsWorkflow/page.mdx": "2025-04-23T16:21:21.217Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductOptionsWorkflow/page.mdx": "2025-04-23T16:21:21.221Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductTagsWorkflow/page.mdx": "2025-04-23T16:21:21.225Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductTypesWorkflow/page.mdx": "2025-04-23T16:21:21.228Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductVariantsWorkflow/page.mdx": "2025-04-23T16:21:21.234Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductsWorkflow/page.mdx": "2025-04-23T16:21:21.243Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteCollectionsWorkflow/page.mdx": "2025-04-23T16:21:21.245Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductOptionsWorkflow/page.mdx": "2025-04-23T16:21:21.248Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductTagsWorkflow/page.mdx": "2025-04-23T16:21:21.253Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductTypesWorkflow/page.mdx": "2025-04-23T16:21:21.250Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductVariantsWorkflow/page.mdx": "2025-04-23T16:21:21.256Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductsWorkflow/page.mdx": "2025-04-23T16:21:21.258Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateCollectionsWorkflow/page.mdx": "2025-04-23T16:21:21.263Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductOptionsWorkflow/page.mdx": "2025-04-23T16:21:21.268Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductTagsWorkflow/page.mdx": "2025-04-23T16:21:21.276Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductTypesWorkflow/page.mdx": "2025-04-23T16:21:21.271Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductVariantsWorkflow/page.mdx": "2025-04-23T16:21:21.282Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.createCampaignsWorkflow/page.mdx": "2025-04-23T16:21:21.360Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.createPromotionsWorkflow/page.mdx": "2025-04-23T16:21:21.366Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.deleteCampaignsWorkflow/page.mdx": "2025-04-23T16:21:21.368Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.deletePromotionsWorkflow/page.mdx": "2025-04-23T16:21:21.372Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updateCampaignsWorkflow/page.mdx": "2025-04-23T16:21:21.376Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateReturnsStep/page.mdx": "2025-05-01T15:18:37.585Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.addOrderLineItemsWorkflow/page.mdx": "2025-05-01T15:18:37.648Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderWorkflow/page.mdx": "2025-05-01T15:18:37.684Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createCollectionsWorkflow/page.mdx": "2025-05-01T15:18:39.550Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductOptionsWorkflow/page.mdx": "2025-05-01T15:18:39.558Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductTagsWorkflow/page.mdx": "2025-05-01T15:18:39.562Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductTypesWorkflow/page.mdx": "2025-05-01T15:18:39.565Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductVariantsWorkflow/page.mdx": "2025-05-01T15:18:39.571Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.createProductsWorkflow/page.mdx": "2025-05-01T15:18:39.579Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteCollectionsWorkflow/page.mdx": "2025-05-01T15:18:39.582Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductOptionsWorkflow/page.mdx": "2025-05-01T15:18:39.584Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductTagsWorkflow/page.mdx": "2025-05-01T15:18:39.589Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductTypesWorkflow/page.mdx": "2025-05-01T15:18:39.587Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductVariantsWorkflow/page.mdx": "2025-05-01T15:18:39.591Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.deleteProductsWorkflow/page.mdx": "2025-05-01T15:18:39.593Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateCollectionsWorkflow/page.mdx": "2025-05-01T15:18:39.598Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductOptionsWorkflow/page.mdx": "2025-05-01T15:18:39.602Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductTagsWorkflow/page.mdx": "2025-05-01T15:18:39.609Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductTypesWorkflow/page.mdx": "2025-05-01T15:18:39.605Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.updateProductVariantsWorkflow/page.mdx": "2025-05-01T15:18:39.667Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.createCampaignsWorkflow/page.mdx": "2025-05-01T15:18:39.751Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.createPromotionsWorkflow/page.mdx": "2025-05-01T15:18:39.758Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.deleteCampaignsWorkflow/page.mdx": "2025-05-01T15:18:39.760Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.deletePromotionsWorkflow/page.mdx": "2025-05-01T15:18:39.764Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updateCampaignsWorkflow/page.mdx": "2025-05-01T15:18:39.767Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.createTaxRateRulesStep/page.mdx": "2025-04-11T09:04:41.501Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.createTaxRatesStep/page.mdx": "2025-04-11T09:04:41.507Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.updateTaxRatesStep/page.mdx": "2025-04-11T09:04:41.547Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.createTaxRateRulesWorkflow/page.mdx": "2025-04-23T16:21:21.559Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.createTaxRatesWorkflow/page.mdx": "2025-04-23T16:21:21.562Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.createTaxRateRulesWorkflow/page.mdx": "2025-05-01T15:18:39.957Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.createTaxRatesWorkflow/page.mdx": "2025-05-01T15:18:39.959Z", "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.maybeListTaxRateRuleIdsStep/page.mdx": "2025-01-20T08:25:22.551Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.setTaxRateRulesWorkflow/page.mdx": "2025-04-23T16:21:21.572Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.updateTaxRatesWorkflow/page.mdx": "2025-04-23T16:21:21.577Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.setTaxRateRulesWorkflow/page.mdx": "2025-05-01T15:18:39.970Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.updateTaxRatesWorkflow/page.mdx": "2025-05-01T15:18:39.976Z", "references/core_flows/Tax/core_flows.Tax.Steps_Tax/page.mdx": "2025-01-07T12:54:18.172Z", "references/core_flows/Tax/core_flows.Tax.Workflows_Tax/page.mdx": "2024-12-23T12:30:26.543Z", "references/core_flows/core_flows.Steps_Api_Key/page.mdx": "2024-12-09T13:21:37.221Z", @@ -1702,9 +1701,9 @@ export const generatedEditDates = { "references/core_flows/Api_Key/Steps_Api_Key/functions/core_flows.Api_Key.Steps_Api_Key.deleteApiKeysStep/page.mdx": "2025-01-13T17:30:22.900Z", "references/core_flows/Api_Key/Steps_Api_Key/functions/core_flows.Api_Key.Steps_Api_Key.linkSalesChannelsToApiKeyStep/page.mdx": "2025-01-13T18:05:49.177Z", "references/core_flows/Api_Key/Steps_Api_Key/functions/core_flows.Api_Key.Steps_Api_Key.validateSalesChannelsExistStep/page.mdx": "2025-01-13T17:30:22.912Z", - "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.createApiKeysWorkflow/page.mdx": "2025-04-23T16:21:18.113Z", - "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.deleteApiKeysWorkflow/page.mdx": "2025-04-23T16:21:18.117Z", - "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.linkSalesChannelsToApiKeyWorkflow/page.mdx": "2025-04-23T16:21:18.120Z", + "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.createApiKeysWorkflow/page.mdx": "2025-05-01T15:18:36.426Z", + "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.deleteApiKeysWorkflow/page.mdx": "2025-05-01T15:18:36.430Z", + "references/core_flows/Api_Key/Workflows_Api_Key/functions/core_flows.Api_Key.Workflows_Api_Key.linkSalesChannelsToApiKeyWorkflow/page.mdx": "2025-05-01T15:18:36.433Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.addShippingMethodToCartStep/page.mdx": "2025-04-11T09:04:35.417Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.confirmInventoryStep/page.mdx": "2025-03-04T13:33:40.224Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.createLineItemAdjustmentsStep/page.mdx": "2025-03-04T13:33:40.246Z", @@ -1720,10 +1719,10 @@ export const generatedEditDates = { "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.removeShippingMethodAdjustmentsStep/page.mdx": "2025-01-13T17:30:23.012Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.removeShippingMethodFromCartStep/page.mdx": "2025-01-13T17:30:23.013Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.reserveInventoryStep/page.mdx": "2025-04-11T09:04:35.568Z", - "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.updateCartPromotionsStep/page.mdx": "2025-04-23T16:21:18.229Z", + "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.updateCartPromotionsStep/page.mdx": "2025-05-01T15:18:36.548Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateVariantPricesStep/page.mdx": "2025-01-13T18:05:49.333Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.refreshPaymentCollectionForCartWorkflow/page.mdx": "2025-04-23T16:21:18.353Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateCartPromotionsWorkflow/page.mdx": "2025-04-23T16:21:18.363Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.refreshPaymentCollectionForCartWorkflow/page.mdx": "2025-05-01T15:18:36.677Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.updateCartPromotionsWorkflow/page.mdx": "2025-05-01T15:18:36.687Z", "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.createCustomerAddressesStep/page.mdx": "2025-04-11T09:04:35.986Z", "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.deleteCustomerAddressesStep/page.mdx": "2025-01-13T17:30:23.181Z", "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.deleteCustomersStep/page.mdx": "2025-01-13T17:30:23.183Z", @@ -1731,134 +1730,134 @@ export const generatedEditDates = { "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.maybeUnsetDefaultShippingAddressesStep/page.mdx": "2025-01-13T17:30:23.186Z", "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.updateCustomerAddressesStep/page.mdx": "2025-04-11T09:04:36.011Z", "references/core_flows/Customer/Steps_Customer/functions/core_flows.Customer.Steps_Customer.updateCustomersStep/page.mdx": "2025-04-11T09:04:36.018Z", - "references/core_flows/Customer_Group/Steps_Customer_Group/functions/core_flows.Customer_Group.Steps_Customer_Group.createCustomerGroupsStep/page.mdx": "2025-04-23T16:21:18.459Z", + "references/core_flows/Customer_Group/Steps_Customer_Group/functions/core_flows.Customer_Group.Steps_Customer_Group.createCustomerGroupsStep/page.mdx": "2025-05-01T15:18:36.783Z", "references/core_flows/Customer_Group/Steps_Customer_Group/functions/core_flows.Customer_Group.Steps_Customer_Group.deleteCustomerGroupStep/page.mdx": "2025-01-13T17:30:23.255Z", "references/core_flows/Customer_Group/Steps_Customer_Group/functions/core_flows.Customer_Group.Steps_Customer_Group.linkCustomersToCustomerGroupStep/page.mdx": "2025-01-13T18:05:49.505Z", "references/core_flows/Customer_Group/Steps_Customer_Group/functions/core_flows.Customer_Group.Steps_Customer_Group.updateCustomerGroupsStep/page.mdx": "2025-04-11T09:04:36.117Z", - "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.createCustomerGroupsWorkflow/page.mdx": "2025-04-23T16:21:18.477Z", - "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.deleteCustomerGroupsWorkflow/page.mdx": "2025-04-23T16:21:18.474Z", - "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.linkCustomersToCustomerGroupWorkflow/page.mdx": "2025-04-23T16:21:18.482Z", - "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.updateCustomerGroupsWorkflow/page.mdx": "2025-04-23T16:21:18.471Z", + "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.createCustomerGroupsWorkflow/page.mdx": "2025-05-01T15:18:36.801Z", + "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.deleteCustomerGroupsWorkflow/page.mdx": "2025-05-01T15:18:36.798Z", + "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.linkCustomersToCustomerGroupWorkflow/page.mdx": "2025-05-01T15:18:36.805Z", + "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.updateCustomerGroupsWorkflow/page.mdx": "2025-05-01T15:18:36.796Z", "references/core_flows/Defaults/Steps_Defaults/functions/core_flows.Defaults.Steps_Defaults.createDefaultStoreStep/page.mdx": "2025-01-13T17:30:23.305Z", - "references/core_flows/Defaults/Workflows_Defaults/functions/core_flows.Defaults.Workflows_Defaults.createDefaultsWorkflow/page.mdx": "2025-04-23T16:21:18.488Z", + "references/core_flows/Defaults/Workflows_Defaults/functions/core_flows.Defaults.Workflows_Defaults.createDefaultsWorkflow/page.mdx": "2025-05-01T15:18:36.811Z", "references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx": "2025-04-23T16:21:18.753Z", "references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx": "2025-04-23T16:21:18.755Z", - "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx": "2025-04-23T16:21:18.760Z", - "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx": "2025-04-23T16:21:18.758Z", + "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx": "2025-05-01T15:18:37.114Z", + "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx": "2025-05-01T15:18:37.112Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.cancelFulfillmentStep/page.mdx": "2025-01-13T17:30:23.344Z", - "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createFulfillmentSets/page.mdx": "2025-04-23T16:21:18.778Z", - "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createServiceZonesStep/page.mdx": "2025-04-23T16:21:18.806Z", - "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createShippingOptionsPriceSetsStep/page.mdx": "2025-04-23T16:21:18.766Z", - "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createShippingProfilesStep/page.mdx": "2025-04-23T16:21:18.813Z", + "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createFulfillmentSets/page.mdx": "2025-05-01T15:18:37.125Z", + "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createServiceZonesStep/page.mdx": "2025-05-01T15:18:37.153Z", + "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createShippingOptionsPriceSetsStep/page.mdx": "2025-05-01T15:18:37.120Z", + "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createShippingProfilesStep/page.mdx": "2025-05-01T15:18:37.161Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.deleteFulfillmentSetsStep/page.mdx": "2025-01-13T17:30:23.386Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.deleteServiceZonesStep/page.mdx": "2025-01-13T17:30:23.387Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.deleteShippingOptionsStep/page.mdx": "2025-01-13T17:30:23.392Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.setShippingOptionsPricesStep/page.mdx": "2025-01-13T18:05:49.596Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.updateShippingProfilesStep/page.mdx": "2025-04-11T09:04:36.323Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.validateShipmentStep/page.mdx": "2025-01-13T17:30:23.408Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.batchShippingOptionRulesWorkflow/page.mdx": "2025-04-23T16:21:18.853Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.cancelFulfillmentWorkflow/page.mdx": "2025-04-23T16:21:18.857Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createServiceZonesWorkflow/page.mdx": "2025-04-23T16:21:18.876Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createShippingOptionsWorkflow/page.mdx": "2025-04-23T16:21:18.890Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createShippingProfilesWorkflow/page.mdx": "2025-04-23T16:21:18.894Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.deleteFulfillmentSetsWorkflow/page.mdx": "2025-04-23T16:21:18.896Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.deleteServiceZonesWorkflow/page.mdx": "2025-04-23T16:21:18.897Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.deleteShippingOptionsWorkflow/page.mdx": "2025-04-23T16:21:18.899Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateServiceZonesWorkflow/page.mdx": "2025-04-23T16:21:18.923Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateShippingOptionsWorkflow/page.mdx": "2025-04-23T16:21:18.926Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateShippingProfilesWorkflow/page.mdx": "2025-04-23T16:21:18.931Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.batchShippingOptionRulesWorkflow/page.mdx": "2025-05-01T15:18:37.202Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.cancelFulfillmentWorkflow/page.mdx": "2025-05-01T15:18:37.207Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createServiceZonesWorkflow/page.mdx": "2025-05-01T15:18:37.227Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createShippingOptionsWorkflow/page.mdx": "2025-05-01T15:18:37.240Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.createShippingProfilesWorkflow/page.mdx": "2025-05-01T15:18:37.245Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.deleteFulfillmentSetsWorkflow/page.mdx": "2025-05-01T15:18:37.247Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.deleteServiceZonesWorkflow/page.mdx": "2025-05-01T15:18:37.249Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.deleteShippingOptionsWorkflow/page.mdx": "2025-05-01T15:18:37.251Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateServiceZonesWorkflow/page.mdx": "2025-05-01T15:18:37.276Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateShippingOptionsWorkflow/page.mdx": "2025-05-01T15:18:37.278Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.updateShippingProfilesWorkflow/page.mdx": "2025-05-01T15:18:37.282Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.adjustInventoryLevelsStep/page.mdx": "2025-04-11T09:04:36.525Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.attachInventoryItemToVariants/page.mdx": "2025-01-13T17:30:23.510Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.createInventoryItemsStep/page.mdx": "2025-04-11T09:04:36.533Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.createInventoryLevelsStep/page.mdx": "2025-04-11T09:04:36.537Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.deleteInventoryItemStep/page.mdx": "2025-01-13T17:30:23.517Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.deleteInventoryLevelsStep/page.mdx": "2025-01-13T17:30:23.519Z", - "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.updateInventoryItemsStep/page.mdx": "2025-04-23T16:21:18.945Z", + "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.updateInventoryItemsStep/page.mdx": "2025-05-01T15:18:37.298Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.updateInventoryLevelsStep/page.mdx": "2025-04-11T09:04:36.558Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.validateInventoryItemsForCreate/page.mdx": "2025-01-13T17:30:23.525Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.validateInventoryLocationsStep/page.mdx": "2025-01-13T17:30:23.524Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.createInventoryItemsWorkflow/page.mdx": "2025-04-23T16:21:18.959Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.createInventoryLevelsWorkflow/page.mdx": "2025-04-23T16:21:18.961Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.deleteInventoryItemWorkflow/page.mdx": "2025-04-23T16:21:18.963Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.updateInventoryItemsWorkflow/page.mdx": "2025-04-23T16:21:18.971Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.updateInventoryLevelsWorkflow/page.mdx": "2025-04-23T16:21:18.973Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.createInventoryItemsWorkflow/page.mdx": "2025-05-01T15:18:37.313Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.createInventoryLevelsWorkflow/page.mdx": "2025-05-01T15:18:37.315Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.deleteInventoryItemWorkflow/page.mdx": "2025-05-01T15:18:37.317Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.updateInventoryItemsWorkflow/page.mdx": "2025-05-01T15:18:37.325Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.updateInventoryLevelsWorkflow/page.mdx": "2025-05-01T15:18:37.328Z", "references/core_flows/Invite/Steps_Invite/functions/core_flows.Invite.Steps_Invite.createInviteStep/page.mdx": "2025-04-11T09:04:36.620Z", "references/core_flows/Invite/Steps_Invite/functions/core_flows.Invite.Steps_Invite.deleteInvitesStep/page.mdx": "2025-01-13T17:30:23.561Z", "references/core_flows/Invite/Steps_Invite/functions/core_flows.Invite.Steps_Invite.refreshInviteTokensStep/page.mdx": "2025-04-11T09:04:36.627Z", "references/core_flows/Invite/Steps_Invite/functions/core_flows.Invite.Steps_Invite.validateTokenStep/page.mdx": "2025-01-13T17:30:23.565Z", "references/core_flows/Line_Item/Steps_Line_Item/functions/core_flows.Line_Item.Steps_Line_Item.deleteLineItemsStep/page.mdx": "2025-01-13T17:30:23.577Z", - "references/core_flows/Line_Item/Workflows_Line_Item/functions/core_flows.Line_Item.Workflows_Line_Item.deleteLineItemsWorkflow/page.mdx": "2025-04-23T16:21:19.021Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrderChangeStep/page.mdx": "2025-04-23T16:21:19.049Z", + "references/core_flows/Line_Item/Workflows_Line_Item/functions/core_flows.Line_Item.Workflows_Line_Item.deleteLineItemsWorkflow/page.mdx": "2025-05-01T15:18:37.377Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrderChangeStep/page.mdx": "2025-05-01T15:18:37.404Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.cancelOrderFulfillmentStep/page.mdx": "2025-01-13T18:05:49.821Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.declineOrderChangeStep/page.mdx": "2025-04-23T16:21:19.233Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteClaimsStep/page.mdx": "2025-04-23T16:21:19.179Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteExchangesStep/page.mdx": "2025-04-23T16:21:19.257Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderChangeActionsStep/page.mdx": "2025-04-23T16:21:19.235Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderChangesStep/page.mdx": "2025-04-23T16:21:19.237Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderLineItems/page.mdx": "2025-04-23T16:21:19.234Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderShippingMethods/page.mdx": "2025-04-23T16:21:19.238Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteReturnsStep/page.mdx": "2025-04-23T16:21:19.309Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.declineOrderChangeStep/page.mdx": "2025-05-01T15:18:37.492Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteClaimsStep/page.mdx": "2025-05-01T15:18:37.440Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteExchangesStep/page.mdx": "2025-05-01T15:18:37.517Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderChangeActionsStep/page.mdx": "2025-05-01T15:18:37.494Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderChangesStep/page.mdx": "2025-05-01T15:18:37.496Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderLineItems/page.mdx": "2025-05-01T15:18:37.493Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteOrderShippingMethods/page.mdx": "2025-05-01T15:18:37.496Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.deleteReturnsStep/page.mdx": "2025-05-01T15:18:37.581Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.registerOrderFulfillmentStep/page.mdx": "2025-01-13T18:05:49.913Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.registerOrderShipmentStep/page.mdx": "2025-01-13T17:30:23.699Z", - "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrderShippingMethodsStep/page.mdx": "2025-04-23T16:21:19.361Z", + "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrderShippingMethodsStep/page.mdx": "2025-05-01T15:18:37.638Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateReturnItemsStep/page.mdx": "2025-01-13T18:05:49.942Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderClaimWorkflow/page.mdx": "2025-04-23T16:21:19.435Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderEditWorkflow/page.mdx": "2025-04-23T16:21:20.144Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderExchangeWorkflow/page.mdx": "2025-04-23T16:21:19.880Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderChangeWorkflow/page.mdx": "2025-04-23T16:21:19.387Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderClaimWorkflow/page.mdx": "2025-04-23T16:21:19.442Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderExchangeWorkflow/page.mdx": "2025-04-23T16:21:19.887Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnReceiveWorkflow/page.mdx": "2025-04-23T16:21:20.417Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnRequestWorkflow/page.mdx": "2025-04-23T16:21:20.428Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnWorkflow/page.mdx": "2025-04-23T16:21:20.433Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createAndCompleteReturnOrderWorkflow/page.mdx": "2025-04-23T16:21:20.548Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderClaimWorkflow/page.mdx": "2025-05-01T15:18:37.719Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderEditWorkflow/page.mdx": "2025-05-01T15:18:38.458Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelBeginOrderExchangeWorkflow/page.mdx": "2025-05-01T15:18:38.173Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderChangeWorkflow/page.mdx": "2025-05-01T15:18:37.666Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderClaimWorkflow/page.mdx": "2025-05-01T15:18:37.727Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderExchangeWorkflow/page.mdx": "2025-05-01T15:18:38.182Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnReceiveWorkflow/page.mdx": "2025-05-01T15:18:38.738Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnRequestWorkflow/page.mdx": "2025-05-01T15:18:38.752Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelReturnWorkflow/page.mdx": "2025-05-01T15:18:38.757Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createAndCompleteReturnOrderWorkflow/page.mdx": "2025-05-01T15:18:38.827Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createCompleteReturnValidationStep/page.mdx": "2025-02-11T11:36:42.766Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderPaymentCollectionWorkflow/page.mdx": "2025-04-23T16:21:19.830Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.declineOrderChangeWorkflow/page.mdx": "2025-04-23T16:21:19.841Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.deleteOrderChangeActionsWorkflow/page.mdx": "2025-04-23T16:21:19.843Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.deleteOrderChangeWorkflow/page.mdx": "2025-04-23T16:21:19.845Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.deleteOrderPaymentCollections/page.mdx": "2025-04-23T16:21:19.850Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.getOrderDetailWorkflow/page.mdx": "2025-04-23T16:21:20.081Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.markPaymentCollectionAsPaid/page.mdx": "2025-04-23T16:21:20.114Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.receiveAndCompleteReturnOrderWorkflow/page.mdx": "2025-04-23T16:21:20.599Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderPaymentCollectionWorkflow/page.mdx": "2025-05-01T15:18:38.121Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.declineOrderChangeWorkflow/page.mdx": "2025-05-01T15:18:38.133Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.deleteOrderChangeActionsWorkflow/page.mdx": "2025-05-01T15:18:38.135Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.deleteOrderChangeWorkflow/page.mdx": "2025-05-01T15:18:38.137Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.deleteOrderPaymentCollections/page.mdx": "2025-05-01T15:18:38.142Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.getOrderDetailWorkflow/page.mdx": "2025-05-01T15:18:38.394Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.markPaymentCollectionAsPaid/page.mdx": "2025-05-01T15:18:38.429Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.receiveAndCompleteReturnOrderWorkflow/page.mdx": "2025-05-01T15:18:38.884Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.receiveCompleteReturnValidationStep/page.mdx": "2025-04-11T09:04:39.668Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.throwUnlessPaymentCollectionNotPaid/page.mdx": "2025-01-20T08:25:21.432Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.throwUnlessStatusIsNotPaid/page.mdx": "2025-04-23T16:21:19.847Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.throwUnlessStatusIsNotPaid/page.mdx": "2025-05-01T15:18:38.139Z", "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.authorizePaymentSessionStep/page.mdx": "2025-02-11T11:36:43.569Z", "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.cancelPaymentStep/page.mdx": "2025-01-13T17:30:25.526Z", "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.capturePaymentStep/page.mdx": "2025-01-17T16:43:24.790Z", "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.refundPaymentStep/page.mdx": "2025-01-17T16:43:24.798Z", - "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.capturePaymentWorkflow/page.mdx": "2025-04-23T16:21:20.968Z", - "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.createPaymentSessionStep/page.mdx": "2025-04-23T16:21:21.002Z", + "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.capturePaymentWorkflow/page.mdx": "2025-05-01T15:18:39.308Z", + "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.createPaymentSessionStep/page.mdx": "2025-05-01T15:18:39.342Z", "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.createRefundReasonStep/page.mdx": "2025-04-11T09:04:40.525Z", - "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.deletePaymentSessionsStep/page.mdx": "2025-04-23T16:21:21.005Z", + "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.deletePaymentSessionsStep/page.mdx": "2025-05-01T15:18:39.346Z", "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.deleteRefundReasonsStep/page.mdx": "2025-01-13T17:30:25.585Z", "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.updatePaymentCollectionStep/page.mdx": "2025-04-11T09:04:40.543Z", - "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.updateRefundReasonsStep/page.mdx": "2025-04-23T16:21:21.016Z", + "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.updateRefundReasonsStep/page.mdx": "2025-05-01T15:18:39.357Z", "references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.validateDeletedPaymentSessionsStep/page.mdx": "2025-01-13T17:30:25.592Z", - "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.createPaymentSessionsWorkflow/page.mdx": "2025-04-23T16:21:21.024Z", - "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.createRefundReasonsWorkflow/page.mdx": "2025-04-23T16:21:21.027Z", - "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.deletePaymentSessionsWorkflow/page.mdx": "2025-04-23T16:21:21.029Z", - "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.updateRefundReasonsWorkflow/page.mdx": "2025-04-23T16:21:21.032Z", + "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.createPaymentSessionsWorkflow/page.mdx": "2025-05-01T15:18:39.365Z", + "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.createRefundReasonsWorkflow/page.mdx": "2025-05-01T15:18:39.368Z", + "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.deletePaymentSessionsWorkflow/page.mdx": "2025-05-01T15:18:39.370Z", + "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.updateRefundReasonsWorkflow/page.mdx": "2025-05-01T15:18:39.372Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.deletePriceListsStep/page.mdx": "2025-01-13T17:30:25.617Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.getExistingPriceListsPriceIdsStep/page.mdx": "2025-01-13T17:30:25.619Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.removePriceListPricesStep/page.mdx": "2025-01-13T17:30:25.620Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.updatePriceListsStep/page.mdx": "2025-04-11T09:04:40.617Z", - "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.validatePriceListsStep/page.mdx": "2025-04-23T16:21:21.060Z", + "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.validatePriceListsStep/page.mdx": "2025-05-01T15:18:39.397Z", "references/core_flows/Price_List/Steps_Price_List/functions/core_flows.Price_List.Steps_Price_List.validateVariantPriceLinksStep/page.mdx": "2025-01-13T17:30:25.630Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.deletePriceListsWorkflow/page.mdx": "2025-04-23T16:21:21.079Z", - "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.removePriceListPricesWorkflow/page.mdx": "2025-04-23T16:21:21.083Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.deletePriceListsWorkflow/page.mdx": "2025-05-01T15:18:39.414Z", + "references/core_flows/Price_List/Workflows_Price_List/functions/core_flows.Price_List.Workflows_Price_List.removePriceListPricesWorkflow/page.mdx": "2025-05-01T15:18:39.416Z", "references/core_flows/Pricing/Steps_Pricing/functions/core_flows.Pricing.Steps_Pricing.createPricePreferencesStep/page.mdx": "2025-04-11T09:04:40.679Z", "references/core_flows/Pricing/Steps_Pricing/functions/core_flows.Pricing.Steps_Pricing.createPriceSetsStep/page.mdx": "2025-04-11T09:04:40.675Z", "references/core_flows/Pricing/Steps_Pricing/functions/core_flows.Pricing.Steps_Pricing.deletePricePreferencesStep/page.mdx": "2025-01-13T17:30:25.659Z", "references/core_flows/Pricing/Steps_Pricing/functions/core_flows.Pricing.Steps_Pricing.updatePricePreferencesAsArrayStep/page.mdx": "2025-04-11T09:04:40.686Z", "references/core_flows/Pricing/Steps_Pricing/functions/core_flows.Pricing.Steps_Pricing.updatePricePreferencesStep/page.mdx": "2025-04-11T09:04:40.690Z", "references/core_flows/Pricing/Steps_Pricing/functions/core_flows.Pricing.Steps_Pricing.updatePriceSetsStep/page.mdx": "2025-04-11T09:04:40.696Z", - "references/core_flows/Pricing/Workflows_Pricing/functions/core_flows.Pricing.Workflows_Pricing.createPricePreferencesWorkflow/page.mdx": "2025-04-23T16:21:21.111Z", - "references/core_flows/Pricing/Workflows_Pricing/functions/core_flows.Pricing.Workflows_Pricing.deletePricePreferencesWorkflow/page.mdx": "2025-04-23T16:21:21.114Z", - "references/core_flows/Pricing/Workflows_Pricing/functions/core_flows.Pricing.Workflows_Pricing.updatePricePreferencesWorkflow/page.mdx": "2025-04-23T16:21:21.117Z", + "references/core_flows/Pricing/Workflows_Pricing/functions/core_flows.Pricing.Workflows_Pricing.createPricePreferencesWorkflow/page.mdx": "2025-05-01T15:18:39.439Z", + "references/core_flows/Pricing/Workflows_Pricing/functions/core_flows.Pricing.Workflows_Pricing.deletePricePreferencesWorkflow/page.mdx": "2025-05-01T15:18:39.441Z", + "references/core_flows/Pricing/Workflows_Pricing/functions/core_flows.Pricing.Workflows_Pricing.updatePricePreferencesWorkflow/page.mdx": "2025-05-01T15:18:39.443Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.batchLinkProductsToCollectionStep/page.mdx": "2025-01-13T18:05:52.101Z", - "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.createCollectionsStep/page.mdx": "2025-04-23T16:21:21.127Z", + "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.createCollectionsStep/page.mdx": "2025-05-01T15:18:39.454Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.createProductOptionsStep/page.mdx": "2025-04-11T09:04:40.737Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.createProductTagsStep/page.mdx": "2025-04-11T09:04:40.811Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.createProductTypesStep/page.mdx": "2025-04-11T09:04:40.815Z", @@ -1883,19 +1882,19 @@ export const generatedEditDates = { "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.updateProductVariantsStep/page.mdx": "2025-04-11T09:04:40.800Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.updateProductsStep/page.mdx": "2025-04-11T09:04:40.784Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.waitConfirmationProductImportStep/page.mdx": "2025-01-13T17:30:25.739Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchLinkProductsToCategoryWorkflow/page.mdx": "2025-04-23T16:21:21.208Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchLinkProductsToCollectionWorkflow/page.mdx": "2025-04-23T16:21:21.201Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchProductVariantsWorkflow/page.mdx": "2025-04-23T16:21:21.206Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchProductsWorkflow/page.mdx": "2025-04-23T16:21:21.213Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx": "2025-04-23T16:21:21.286Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx": "2025-04-23T16:21:21.289Z", - "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.upsertVariantPricesWorkflow/page.mdx": "2025-04-23T16:21:21.301Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchLinkProductsToCategoryWorkflow/page.mdx": "2025-05-01T15:18:39.542Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchLinkProductsToCollectionWorkflow/page.mdx": "2025-05-01T15:18:39.530Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchProductVariantsWorkflow/page.mdx": "2025-05-01T15:18:39.539Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchProductsWorkflow/page.mdx": "2025-05-01T15:18:39.546Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx": "2025-05-01T15:18:39.680Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx": "2025-05-01T15:18:39.683Z", + "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.upsertVariantPricesWorkflow/page.mdx": "2025-05-01T15:18:39.696Z", "references/core_flows/Product_Category/Steps_Product_Category/functions/core_flows.Product_Category.Steps_Product_Category.createProductCategoriesStep/page.mdx": "2025-04-11T09:04:41.068Z", "references/core_flows/Product_Category/Steps_Product_Category/functions/core_flows.Product_Category.Steps_Product_Category.deleteProductCategoriesStep/page.mdx": "2025-01-13T17:30:25.836Z", "references/core_flows/Product_Category/Steps_Product_Category/functions/core_flows.Product_Category.Steps_Product_Category.updateProductCategoriesStep/page.mdx": "2025-04-11T09:04:41.084Z", - "references/core_flows/Product_Category/Workflows_Product_Category/functions/core_flows.Product_Category.Workflows_Product_Category.createProductCategoriesWorkflow/page.mdx": "2025-04-23T16:21:21.321Z", - "references/core_flows/Product_Category/Workflows_Product_Category/functions/core_flows.Product_Category.Workflows_Product_Category.deleteProductCategoriesWorkflow/page.mdx": "2025-04-23T16:21:21.323Z", - "references/core_flows/Product_Category/Workflows_Product_Category/functions/core_flows.Product_Category.Workflows_Product_Category.updateProductCategoriesWorkflow/page.mdx": "2025-04-23T16:21:21.330Z", + "references/core_flows/Product_Category/Workflows_Product_Category/functions/core_flows.Product_Category.Workflows_Product_Category.createProductCategoriesWorkflow/page.mdx": "2025-05-01T15:18:39.714Z", + "references/core_flows/Product_Category/Workflows_Product_Category/functions/core_flows.Product_Category.Workflows_Product_Category.deleteProductCategoriesWorkflow/page.mdx": "2025-05-01T15:18:39.717Z", + "references/core_flows/Product_Category/Workflows_Product_Category/functions/core_flows.Product_Category.Workflows_Product_Category.updateProductCategoriesWorkflow/page.mdx": "2025-05-01T15:18:39.723Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.addCampaignPromotionsStep/page.mdx": "2025-02-11T11:36:44.164Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.createCampaignsStep/page.mdx": "2025-04-11T09:04:41.127Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.createPromotionsStep/page.mdx": "2025-04-11T09:04:41.132Z", @@ -1903,8 +1902,8 @@ export const generatedEditDates = { "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.deletePromotionsStep/page.mdx": "2025-01-13T17:30:25.862Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.removeCampaignPromotionsStep/page.mdx": "2025-02-11T11:36:44.182Z", "references/core_flows/Promotion/Steps_Promotion/functions/core_flows.Promotion.Steps_Promotion.updateCampaignsStep/page.mdx": "2025-04-11T09:04:41.149Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.addOrRemoveCampaignPromotionsWorkflow/page.mdx": "2025-04-23T16:21:21.354Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.batchPromotionRulesWorkflow/page.mdx": "2025-04-23T16:21:21.357Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.addOrRemoveCampaignPromotionsWorkflow/page.mdx": "2025-05-01T15:18:39.746Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.batchPromotionRulesWorkflow/page.mdx": "2025-05-01T15:18:39.748Z", "references/core_flows/Region/Steps_Region/functions/core_flows.Region.Steps_Region.createRegionsStep/page.mdx": "2025-04-11T09:04:41.230Z", "references/core_flows/Region/Steps_Region/functions/core_flows.Region.Steps_Region.deleteRegionsStep/page.mdx": "2025-01-13T17:30:25.910Z", "references/core_flows/Region/Steps_Region/functions/core_flows.Region.Steps_Region.updateRegionsStep/page.mdx": "2025-04-11T09:04:41.239Z", @@ -1912,16 +1911,16 @@ export const generatedEditDates = { "references/core_flows/Reservation/Steps_Reservation/functions/core_flows.Reservation.Steps_Reservation.deleteReservationsByLineItemsStep/page.mdx": "2025-01-13T17:30:25.927Z", "references/core_flows/Reservation/Steps_Reservation/functions/core_flows.Reservation.Steps_Reservation.deleteReservationsStep/page.mdx": "2025-01-13T17:30:25.928Z", "references/core_flows/Reservation/Steps_Reservation/functions/core_flows.Reservation.Steps_Reservation.updateReservationsStep/page.mdx": "2025-04-11T09:04:41.277Z", - "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.createReservationsWorkflow/page.mdx": "2025-04-23T16:21:21.417Z", - "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.deleteReservationsByLineItemsWorkflow/page.mdx": "2025-04-23T16:21:21.421Z", - "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.deleteReservationsWorkflow/page.mdx": "2025-04-23T16:21:21.419Z", - "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.updateReservationsWorkflow/page.mdx": "2025-04-23T16:21:21.425Z", + "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.createReservationsWorkflow/page.mdx": "2025-05-01T15:18:39.806Z", + "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.deleteReservationsByLineItemsWorkflow/page.mdx": "2025-05-01T15:18:39.816Z", + "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.deleteReservationsWorkflow/page.mdx": "2025-05-01T15:18:39.808Z", + "references/core_flows/Reservation/Workflows_Reservation/functions/core_flows.Reservation.Workflows_Reservation.updateReservationsWorkflow/page.mdx": "2025-05-01T15:18:39.825Z", "references/core_flows/Return_Reason/Steps_Return_Reason/functions/core_flows.Return_Reason.Steps_Return_Reason.createReturnReasonsStep/page.mdx": "2025-04-11T09:04:41.309Z", "references/core_flows/Return_Reason/Steps_Return_Reason/functions/core_flows.Return_Reason.Steps_Return_Reason.deleteReturnReasonStep/page.mdx": "2025-01-13T17:30:25.948Z", "references/core_flows/Return_Reason/Steps_Return_Reason/functions/core_flows.Return_Reason.Steps_Return_Reason.updateReturnReasonsStep/page.mdx": "2025-04-11T09:04:41.317Z", - "references/core_flows/Return_Reason/Workflows_Return_Reason/functions/core_flows.Return_Reason.Workflows_Return_Reason.createReturnReasonsWorkflow/page.mdx": "2025-04-23T16:21:21.437Z", - "references/core_flows/Return_Reason/Workflows_Return_Reason/functions/core_flows.Return_Reason.Workflows_Return_Reason.deleteReturnReasonsWorkflow/page.mdx": "2025-04-23T16:21:21.439Z", - "references/core_flows/Return_Reason/Workflows_Return_Reason/functions/core_flows.Return_Reason.Workflows_Return_Reason.updateReturnReasonsWorkflow/page.mdx": "2025-04-23T16:21:21.444Z", + "references/core_flows/Return_Reason/Workflows_Return_Reason/functions/core_flows.Return_Reason.Workflows_Return_Reason.createReturnReasonsWorkflow/page.mdx": "2025-05-01T15:18:39.838Z", + "references/core_flows/Return_Reason/Workflows_Return_Reason/functions/core_flows.Return_Reason.Workflows_Return_Reason.deleteReturnReasonsWorkflow/page.mdx": "2025-05-01T15:18:39.840Z", + "references/core_flows/Return_Reason/Workflows_Return_Reason/functions/core_flows.Return_Reason.Workflows_Return_Reason.updateReturnReasonsWorkflow/page.mdx": "2025-05-01T15:18:39.844Z", "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.associateLocationsWithSalesChannelsStep/page.mdx": "2025-01-13T17:30:25.974Z", "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.associateProductsWithSalesChannelsStep/page.mdx": "2025-01-13T17:30:25.964Z", "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.createDefaultSalesChannelStep/page.mdx": "2025-01-13T18:05:52.383Z", @@ -1930,33 +1929,33 @@ export const generatedEditDates = { "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.detachLocationsFromSalesChannelsStep/page.mdx": "2025-01-13T17:30:25.977Z", "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.detachProductsFromSalesChannelsStep/page.mdx": "2025-01-13T17:30:25.971Z", "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.updateSalesChannelsStep/page.mdx": "2025-04-11T09:04:41.362Z", - "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.linkProductsToSalesChannelWorkflow/page.mdx": "2025-04-23T16:21:21.463Z", + "references/core_flows/Sales_Channel/Workflows_Sales_Channel/functions/core_flows.Sales_Channel.Workflows_Sales_Channel.linkProductsToSalesChannelWorkflow/page.mdx": "2025-05-01T15:18:39.861Z", "references/core_flows/Shipping_Profile/Steps_Shipping_Profile/functions/core_flows.Shipping_Profile.Steps_Shipping_Profile.deleteShippingProfilesStep/page.mdx": "2025-01-13T17:30:25.996Z", - "references/core_flows/Shipping_Profile/Workflows_Shipping_Profile/functions/core_flows.Shipping_Profile.Workflows_Shipping_Profile.deleteShippingProfileWorkflow/page.mdx": "2025-04-23T16:21:21.486Z", - "references/core_flows/Stock_Location/Steps_Stock_Location/functions/core_flows.Stock_Location.Steps_Stock_Location.createStockLocations/page.mdx": "2025-04-23T16:21:21.490Z", + "references/core_flows/Shipping_Profile/Workflows_Shipping_Profile/functions/core_flows.Shipping_Profile.Workflows_Shipping_Profile.deleteShippingProfileWorkflow/page.mdx": "2025-05-01T15:18:39.884Z", + "references/core_flows/Stock_Location/Steps_Stock_Location/functions/core_flows.Stock_Location.Steps_Stock_Location.createStockLocations/page.mdx": "2025-05-01T15:18:39.888Z", "references/core_flows/Stock_Location/Steps_Stock_Location/functions/core_flows.Stock_Location.Steps_Stock_Location.deleteStockLocationsStep/page.mdx": "2025-01-13T17:30:26.004Z", "references/core_flows/Stock_Location/Steps_Stock_Location/functions/core_flows.Stock_Location.Steps_Stock_Location.updateStockLocationsStep/page.mdx": "2025-04-11T09:04:41.433Z", - "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.createLocationFulfillmentSetWorkflow/page.mdx": "2025-04-23T16:21:21.497Z", - "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.createStockLocationsWorkflow/page.mdx": "2025-04-23T16:21:21.501Z", - "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.deleteStockLocationsWorkflow/page.mdx": "2025-04-23T16:21:21.503Z", - "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.linkSalesChannelsToStockLocationWorkflow/page.mdx": "2025-04-23T16:21:21.505Z", - "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.updateStockLocationsWorkflow/page.mdx": "2025-04-23T16:21:21.509Z", + "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.createLocationFulfillmentSetWorkflow/page.mdx": "2025-05-01T15:18:39.895Z", + "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.createStockLocationsWorkflow/page.mdx": "2025-05-01T15:18:39.900Z", + "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.deleteStockLocationsWorkflow/page.mdx": "2025-05-01T15:18:39.902Z", + "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.linkSalesChannelsToStockLocationWorkflow/page.mdx": "2025-05-01T15:18:39.904Z", + "references/core_flows/Stock_Location/Workflows_Stock_Location/functions/core_flows.Stock_Location.Workflows_Stock_Location.updateStockLocationsWorkflow/page.mdx": "2025-05-01T15:18:39.908Z", "references/core_flows/Store/Steps_Store/functions/core_flows.Store.Steps_Store.createStoresStep/page.mdx": "2025-04-11T09:04:41.466Z", "references/core_flows/Store/Steps_Store/functions/core_flows.Store.Steps_Store.deleteStoresStep/page.mdx": "2025-01-13T17:30:26.026Z", "references/core_flows/Store/Steps_Store/functions/core_flows.Store.Steps_Store.updateStoresStep/page.mdx": "2025-04-11T09:04:41.473Z", - "references/core_flows/Store/Workflows_Store/functions/core_flows.Store.Workflows_Store.createStoresWorkflow/page.mdx": "2025-04-23T16:21:21.519Z", - "references/core_flows/Store/Workflows_Store/functions/core_flows.Store.Workflows_Store.deleteStoresWorkflow/page.mdx": "2025-04-23T16:21:21.521Z", - "references/core_flows/Store/Workflows_Store/functions/core_flows.Store.Workflows_Store.updateStoresWorkflow/page.mdx": "2025-04-23T16:21:21.523Z", + "references/core_flows/Store/Workflows_Store/functions/core_flows.Store.Workflows_Store.createStoresWorkflow/page.mdx": "2025-05-01T15:18:39.917Z", + "references/core_flows/Store/Workflows_Store/functions/core_flows.Store.Workflows_Store.deleteStoresWorkflow/page.mdx": "2025-05-01T15:18:39.919Z", + "references/core_flows/Store/Workflows_Store/functions/core_flows.Store.Workflows_Store.updateStoresWorkflow/page.mdx": "2025-05-01T15:18:39.923Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.createTaxRegionsStep/page.mdx": "2025-04-11T09:04:41.494Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.deleteTaxRateRulesStep/page.mdx": "2025-01-13T17:30:26.048Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.deleteTaxRatesStep/page.mdx": "2025-01-13T17:30:26.049Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.deleteTaxRegionsStep/page.mdx": "2025-01-13T17:30:26.043Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.listTaxRateIdsStep/page.mdx": "2025-01-13T18:05:52.483Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.listTaxRateRuleIdsStep/page.mdx": "2025-01-13T18:05:52.486Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.createTaxRegionsWorkflow/page.mdx": "2025-04-23T16:21:21.564Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.deleteTaxRateRulesWorkflow/page.mdx": "2025-04-23T16:21:21.566Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.deleteTaxRatesWorkflow/page.mdx": "2025-04-23T16:21:21.567Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.deleteTaxRegionsWorkflow/page.mdx": "2025-04-23T16:21:21.569Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.createTaxRegionsWorkflow/page.mdx": "2025-05-01T15:18:39.962Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.deleteTaxRateRulesWorkflow/page.mdx": "2025-05-01T15:18:39.964Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.deleteTaxRatesWorkflow/page.mdx": "2025-05-01T15:18:39.966Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.deleteTaxRegionsWorkflow/page.mdx": "2025-05-01T15:18:39.967Z", "references/core_flows/User/Steps_User/functions/core_flows.User.Steps_User.createUsersStep/page.mdx": "2025-04-11T09:04:41.603Z", "references/core_flows/User/Steps_User/functions/core_flows.User.Steps_User.deleteUsersStep/page.mdx": "2025-01-13T17:30:26.110Z", "references/core_flows/User/Steps_User/functions/core_flows.User.Steps_User.updateUsersStep/page.mdx": "2025-04-11T09:04:41.611Z", @@ -2061,8 +2060,8 @@ export const generatedEditDates = { "references/region/interfaces/region.MessageAggregatorFormat/page.mdx": "2024-12-09T13:22:04.016Z", "references/region/interfaces/region.PaymentProviderDTO/page.mdx": "2024-12-09T13:22:03.956Z", "references/region/interfaces/region.RegionDTO/page.mdx": "2024-12-09T13:22:03.956Z", - "references/region_models/variables/region_models.Country/page.mdx": "2024-12-23T13:57:10.099Z", - "references/region_models/variables/region_models.Region/page.mdx": "2024-12-23T13:57:10.101Z", + "references/region_models/variables/region_models.Country/page.mdx": "2025-05-01T15:18:48.389Z", + "references/region_models/variables/region_models.Region/page.mdx": "2025-05-01T15:18:48.391Z", "references/sales_channel/IMessageAggregator/methods/sales_channel.IMessageAggregator.getMessages/page.mdx": "2025-04-11T09:04:52.240Z", "references/sales_channel/interfaces/sales_channel.IModuleService/page.mdx": "2024-12-09T13:22:04.060Z", "references/sales_channel/interfaces/sales_channel.MessageAggregatorFormat/page.mdx": "2024-12-09T13:22:04.104Z", @@ -2121,7 +2120,7 @@ export const generatedEditDates = { "app/commerce-modules/order/order-change/page.mdx": "2024-10-09T09:59:40.745Z", "app/commerce-modules/payment/links-to-other-modules/page.mdx": "2025-04-17T15:53:23.515Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.useQueryGraphStep/page.mdx": "2025-04-11T09:04:35.943Z", - "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.processPaymentWorkflow/page.mdx": "2025-04-23T16:21:20.970Z", + "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.processPaymentWorkflow/page.mdx": "2025-05-01T15:18:39.311Z", "references/helper_steps/functions/helper_steps.useQueryGraphStep/page.mdx": "2025-01-17T16:43:22.242Z", "references/order/IOrderModuleService/methods/order.IOrderModuleService.undoLastChange/page.mdx": "2025-04-11T09:04:50.317Z", "references/order/interfaces/order.CreateOrderLineItemAdjustmentDTO/page.mdx": "2024-12-09T13:22:01.264Z", @@ -2162,7 +2161,7 @@ export const generatedEditDates = { "references/types/ModulesSdkTypes/types/types.ModulesSdkTypes.ModuleProvider/page.mdx": "2024-12-09T13:21:35.373Z", "references/user/interfaces/user.FindConfig/page.mdx": "2025-02-11T11:36:54.693Z", "references/workflows/types/workflows.StepFunction/page.mdx": "2025-04-23T07:49:56.555Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.addShippingMethodToCartWorkflow/page.mdx": "2025-04-23T16:21:18.289Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.addShippingMethodToCartWorkflow/page.mdx": "2025-05-01T15:18:36.614Z", "references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.validateFulfillmentData/page.mdx": "2025-01-17T16:43:27.199Z", "references/fulfillment/interfaces/fulfillment.IFulfillmentModuleService/page.mdx": "2024-12-17T16:57:25.097Z", "references/types/CommonTypes/interfaces/types.CommonTypes.RequestQueryFields/page.mdx": "2024-12-09T13:21:32.865Z", @@ -2624,8 +2623,8 @@ export const generatedEditDates = { "references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx": "2025-01-13T18:05:59.452Z", "references/js_sdk/store/classes/js_sdk.store.Store/page.mdx": "2024-11-25T17:49:55.112Z", "references/modules/js_sdk/page.mdx": "2024-10-22T15:09:52.263Z", - "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.validateInventoryDeleteStep/page.mdx": "2025-04-23T16:21:18.941Z", - "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.updateTaxRegionsWorkflow/page.mdx": "2025-04-23T16:21:21.579Z", + "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.validateInventoryDeleteStep/page.mdx": "2025-05-01T15:18:37.293Z", + "references/core_flows/Tax/Workflows_Tax/functions/core_flows.Tax.Workflows_Tax.updateTaxRegionsWorkflow/page.mdx": "2025-05-01T15:18:39.978Z", "references/core_flows/interfaces/core_flows.ValidateInventoryDeleteStepInput/page.mdx": "2024-12-09T13:21:50.648Z", "references/core_flows/types/core_flows.UseQueryGraphStepInput/page.mdx": "2024-12-09T13:21:50.232Z", "references/helper_steps/types/helper_steps.UseQueryGraphStepInput/page.mdx": "2024-12-09T13:21:56.600Z", @@ -3028,7 +3027,7 @@ export const generatedEditDates = { "references/types/interfaces/types.BaseProductVariantParams/page.mdx": "2025-01-13T18:05:55.128Z", "references/workflows/types/workflows.UnwrapWorkflowInputDataType/page.mdx": "2025-01-13T17:30:31.439Z", "references/core_flows/Customer_Group/Steps_Customer_Group/functions/core_flows.Customer_Group.Steps_Customer_Group.linkCustomerGroupsToCustomerStep/page.mdx": "2025-01-13T18:05:49.504Z", - "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.linkCustomerGroupsToCustomerWorkflow/page.mdx": "2025-04-23T16:21:18.479Z", + "references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.linkCustomerGroupsToCustomerWorkflow/page.mdx": "2025-05-01T15:18:36.803Z", "references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.batchCustomerGroups/page.mdx": "2025-04-11T09:04:54.008Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminBatchPriceListPrice/page.mdx": "2024-12-09T13:21:34.169Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminBatchProductResponse/page.mdx": "2025-04-11T09:04:47.346Z", @@ -3140,7 +3139,7 @@ export const generatedEditDates = { "references/product/interfaces/product.FilterableProductProps/page.mdx": "2025-02-24T10:48:41.629Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminBatchProductVariantRequest/page.mdx": "2024-12-09T13:21:34.309Z", "references/types/WorkflowTypes/ProductWorkflow/interfaces/types.WorkflowTypes.ProductWorkflow.ExportProductsDTO/page.mdx": "2025-02-11T11:36:51.281Z", - "app/integrations/guides/sanity/page.mdx": "2025-04-17T08:28:58.986Z", + "app/integrations/guides/sanity/page.mdx": "2025-05-01T15:33:37.747Z", "references/api_key/types/api_key.FindConfigOrder/page.mdx": "2024-11-25T17:49:28.715Z", "references/auth/types/auth.FindConfigOrder/page.mdx": "2024-11-25T17:49:28.887Z", "references/cart/types/cart.FindConfigOrder/page.mdx": "2024-11-25T17:49:29.455Z", @@ -3300,14 +3299,14 @@ export const generatedEditDates = { "references/pricing/types/pricing.FilterQueryProperties/page.mdx": "2024-12-23T12:30:30.704Z", "references/product/types/product.Constructor/page.mdx": "2024-12-09T13:22:03.168Z", "references/product/types/product.FilterQueryProperties/page.mdx": "2024-12-23T12:30:30.868Z", - "references/product_models/variables/product_models.Product/page.mdx": "2025-01-27T11:43:58.899Z", - "references/product_models/variables/product_models.ProductCategory/page.mdx": "2025-01-27T11:43:58.849Z", - "references/product_models/variables/product_models.ProductCollection/page.mdx": "2025-01-27T11:43:58.854Z", - "references/product_models/variables/product_models.ProductOption/page.mdx": "2025-01-27T11:43:58.870Z", - "references/product_models/variables/product_models.ProductOptionValue/page.mdx": "2024-12-23T13:57:10.043Z", - "references/product_models/variables/product_models.ProductTag/page.mdx": "2025-01-27T11:43:58.874Z", - "references/product_models/variables/product_models.ProductType/page.mdx": "2025-01-27T11:43:58.878Z", - "references/product_models/variables/product_models.ProductVariant/page.mdx": "2025-01-27T11:43:58.885Z", + "references/product_models/variables/product_models.Product/page.mdx": "2025-05-01T15:18:48.357Z", + "references/product_models/variables/product_models.ProductCategory/page.mdx": "2025-05-01T15:18:48.307Z", + "references/product_models/variables/product_models.ProductCollection/page.mdx": "2025-05-01T15:18:48.312Z", + "references/product_models/variables/product_models.ProductOption/page.mdx": "2025-05-01T15:18:48.327Z", + "references/product_models/variables/product_models.ProductOptionValue/page.mdx": "2025-05-01T15:18:48.322Z", + "references/product_models/variables/product_models.ProductTag/page.mdx": "2025-05-01T15:18:48.332Z", + "references/product_models/variables/product_models.ProductType/page.mdx": "2025-05-01T15:18:48.336Z", + "references/product_models/variables/product_models.ProductVariant/page.mdx": "2025-05-01T15:18:48.343Z", "references/promotion/types/promotion.Constructor/page.mdx": "2024-12-09T13:22:03.708Z", "references/promotion/types/promotion.FilterQueryProperties/page.mdx": "2024-12-23T12:30:31.117Z", "references/region/types/region.Constructor/page.mdx": "2024-12-09T13:22:03.944Z", @@ -3320,7 +3319,7 @@ export const generatedEditDates = { "references/types/DmlTypes/types/types.DmlTypes.IsNullableRelation/page.mdx": "2024-12-09T13:21:32.993Z", "references/types/types/types.FilterQueryProperties/page.mdx": "2024-12-23T12:30:28.129Z", "references/cart/types/cart.FilterQuery/page.mdx": "2024-11-27T16:33:41.056Z", - "references/currency_models/variables/currency_models.Currency/page.mdx": "2024-12-23T08:25:04.624Z", + "references/currency_models/variables/currency_models.Currency/page.mdx": "2025-05-01T15:18:46.211Z", "references/customer/types/customer.FilterQuery/page.mdx": "2024-11-27T16:33:52.327Z", "references/fulfillment/types/fulfillment.FilterQuery/page.mdx": "2024-11-27T16:33:52.465Z", "references/inventory_next/types/inventory_next.FilterQuery/page.mdx": "2024-11-27T16:33:52.961Z", @@ -3336,9 +3335,8 @@ export const generatedEditDates = { "references/types/DmlTypes/types/types.DmlTypes.InferForeignKeys/page.mdx": "2025-02-24T10:48:36.703Z", "references/types/DmlTypes/types/types.DmlTypes.InferSchemaFields/page.mdx": "2025-01-13T18:05:54.083Z", "references/types/interfaces/types.BaseRepositoryService/page.mdx": "2024-12-09T13:21:32.969Z", - "app/events-reference/page.mdx": "2024-12-31T09:44:06.198Z", - "references/auth_models/variables/auth_models.AuthIdentity/page.mdx": "2024-12-23T13:57:08.092Z", - "references/auth_models/variables/auth_models.ProviderIdentity/page.mdx": "2024-12-23T13:57:08.094Z", + "references/auth_models/variables/auth_models.AuthIdentity/page.mdx": "2025-05-01T15:18:46.149Z", + "references/auth_models/variables/auth_models.ProviderIdentity/page.mdx": "2025-05-01T15:18:46.153Z", "references/dml/entity/DmlEntity/methods/dml.entity.DmlEntity.checks/page.mdx": "2024-12-09T13:21:55.464Z", "references/helper_steps/functions/helper_steps.validatePresenceOfStep/page.mdx": "2025-01-13T17:30:22.855Z", "references/js_sdk/admin/Order/methods/js_sdk.admin.Order.update/page.mdx": "2025-04-11T09:04:55.384Z", @@ -3347,13 +3345,13 @@ export const generatedEditDates = { "references/order/types/order.OrderChangeType/page.mdx": "2025-01-07T12:54:23.927Z", "references/pricing/interfaces/pricing.RuleWithOperator/page.mdx": "2025-01-13T17:30:30.399Z", "references/pricing/types/pricing.PricingRuleOperatorValues/page.mdx": "2024-12-04T18:29:34.568Z", - "references/pricing_models/variables/pricing_models.Price/page.mdx": "2025-02-24T10:48:47.362Z", - "references/pricing_models/variables/pricing_models.PriceList/page.mdx": "2025-02-24T10:48:47.341Z", - "references/pricing_models/variables/pricing_models.PriceListRule/page.mdx": "2025-02-24T10:48:47.334Z", - "references/pricing_models/variables/pricing_models.PricePreference/page.mdx": "2025-02-24T10:48:47.342Z", - "references/pricing_models/variables/pricing_models.PriceRule/page.mdx": "2025-02-24T10:48:47.347Z", - "references/pricing_models/variables/pricing_models.PriceSet/page.mdx": "2025-02-24T10:48:47.351Z", - "references/product_models/variables/product_models.ProductImage/page.mdx": "2025-01-27T11:43:58.859Z", + "references/pricing_models/variables/pricing_models.Price/page.mdx": "2025-05-01T15:18:48.296Z", + "references/pricing_models/variables/pricing_models.PriceList/page.mdx": "2025-05-01T15:18:48.286Z", + "references/pricing_models/variables/pricing_models.PriceListRule/page.mdx": "2025-05-01T15:18:48.282Z", + "references/pricing_models/variables/pricing_models.PricePreference/page.mdx": "2025-05-01T15:18:48.287Z", + "references/pricing_models/variables/pricing_models.PriceRule/page.mdx": "2025-05-01T15:18:48.289Z", + "references/pricing_models/variables/pricing_models.PriceSet/page.mdx": "2025-05-01T15:18:48.292Z", + "references/product_models/variables/product_models.ProductImage/page.mdx": "2025-05-01T15:18:48.317Z", "references/types/DmlTypes/types/types.DmlTypes.CheckConstraint/page.mdx": "2025-01-13T18:05:54.113Z", "references/types/DmlTypes/types/types.DmlTypes.InferCheckConstraintsProperties/page.mdx": "2025-01-13T18:05:54.110Z", "references/types/DmlTypes/types/types.DmlTypes.InferSchemaProperties/page.mdx": "2025-01-13T18:05:54.102Z", @@ -3361,8 +3359,8 @@ export const generatedEditDates = { "references/types/HttpTypes/interfaces/types.HttpTypes.OrderAddress/page.mdx": "2024-12-09T13:21:33.949Z", "references/types/WorkflowTypes/OrderWorkflow/types/types.WorkflowTypes.OrderWorkflow.UpdateOrderShippingAddressWorkflowInput/page.mdx": "2025-01-13T17:30:29.222Z", "references/types/WorkflowTypes/OrderWorkflow/types/types.WorkflowTypes.OrderWorkflow.UpdateOrderWorkflowInput/page.mdx": "2025-02-11T11:36:51.229Z", - "references/user_models/variables/user_models.Invite/page.mdx": "2024-12-23T08:25:10.396Z", - "references/user_models/variables/user_models.User/page.mdx": "2024-12-23T08:25:10.396Z", + "references/user_models/variables/user_models.Invite/page.mdx": "2025-05-01T15:18:48.416Z", + "references/user_models/variables/user_models.User/page.mdx": "2025-05-01T15:18:48.417Z", "references/utils/PricingUtils/enums/utils.PricingUtils.PriceListStatus/page.mdx": "2024-12-04T18:29:19.807Z", "references/utils/PricingUtils/enums/utils.PricingUtils.PriceListType/page.mdx": "2024-12-04T18:29:19.808Z", "references/utils/PricingUtils/enums/utils.PricingUtils.PricingRuleOperator/page.mdx": "2024-12-04T18:29:19.806Z", @@ -3372,22 +3370,22 @@ export const generatedEditDates = { "references/order/interfaces/order.CreateOrderChangeActionDTO/page.mdx": "2024-12-09T13:22:01.316Z", "references/types/DmlTypes/types/types.DmlTypes.EntityIndex/page.mdx": "2025-01-13T18:05:54.118Z", "references/types/DmlTypes/types/types.DmlTypes.InferIndexableProperties/page.mdx": "2025-01-13T18:05:54.109Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.transferCartCustomerWorkflow/page.mdx": "2025-04-23T16:21:18.355Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.transferCartCustomerWorkflow/page.mdx": "2025-05-01T15:18:36.680Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.validatePresenceOfStep/page.mdx": "2025-01-13T17:30:23.159Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.buildPriceSet/page.mdx": "2024-12-10T14:54:58.496Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.registerOrderChangesStep/page.mdx": "2025-01-13T17:30:23.749Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.updateOrdersStep/page.mdx": "2025-04-11T09:04:37.245Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.acceptOrderTransferValidationStep/page.mdx": "2025-04-11T09:04:40.225Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.acceptOrderTransferWorkflow/page.mdx": "2025-04-23T16:21:20.826Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderTransferRequestWorkflow/page.mdx": "2025-04-23T16:21:20.841Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.acceptOrderTransferWorkflow/page.mdx": "2025-05-01T15:18:39.118Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelOrderTransferRequestWorkflow/page.mdx": "2025-05-01T15:18:39.128Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.cancelTransferOrderRequestValidationStep/page.mdx": "2025-04-11T09:04:40.272Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderWorkflow/page.mdx": "2025-04-23T16:21:19.809Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.declineOrderTransferRequestWorkflow/page.mdx": "2025-04-23T16:21:20.852Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderWorkflow/page.mdx": "2025-05-01T15:18:38.097Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.declineOrderTransferRequestWorkflow/page.mdx": "2025-05-01T15:18:39.138Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.declineTransferOrderRequestValidationStep/page.mdx": "2025-04-11T09:04:40.292Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestOrderTransferValidationStep/page.mdx": "2025-04-11T09:04:40.179Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestOrderTransferWorkflow/page.mdx": "2025-04-23T16:21:20.874Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.requestOrderTransferWorkflow/page.mdx": "2025-05-01T15:18:39.159Z", "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderValidationStep/page.mdx": "2025-04-11T09:04:40.310Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderWorkflow/page.mdx": "2025-04-23T16:21:20.920Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.updateOrderWorkflow/page.mdx": "2025-05-01T15:18:39.205Z", "references/core_flows/Sales_Channel/Steps_Sales_Channel/functions/core_flows.Sales_Channel.Steps_Sales_Channel.canDeleteSalesChannelsOrThrowStep/page.mdx": "2025-01-13T18:05:52.393Z", "references/core_flows/Steps_Api_Key/functions/core_flows.Steps_Api_Key.createApiKeysStep/page.mdx": "2024-12-09T13:21:37.225Z", "references/core_flows/Steps_Api_Key/functions/core_flows.Steps_Api_Key.deleteApiKeysStep/page.mdx": "2024-12-09T13:21:37.225Z", @@ -5533,21 +5531,21 @@ export const generatedEditDates = { "references/workflows/classes/workflows.WorkflowResponse/page.mdx": "2025-04-11T09:04:53.140Z", "references/workflows/interfaces/workflows.ApplyStepOptions/page.mdx": "2025-01-13T17:30:31.420Z", "references/workflows/types/workflows.WorkflowData/page.mdx": "2024-12-23T13:57:08.059Z", - "app/integrations/guides/resend/page.mdx": "2025-04-25T08:04:32.434Z", - "references/api_key_models/variables/api_key_models.ApiKey/page.mdx": "2024-12-23T08:25:00.296Z", + "app/integrations/guides/resend/page.mdx": "2025-05-01T15:33:31.147Z", + "references/api_key_models/variables/api_key_models.ApiKey/page.mdx": "2025-05-01T15:18:46.147Z", "references/cart/ICartModuleService/methods/cart.ICartModuleService.updateShippingMethods/page.mdx": "2025-04-11T09:04:44.258Z", "references/cart/interfaces/cart.UpdateShippingMethodDTO/page.mdx": "2024-12-10T14:54:57.530Z", - "references/cart_models/variables/cart_models.Address/page.mdx": "2024-12-23T08:25:00.477Z", - "references/cart_models/variables/cart_models.Cart/page.mdx": "2025-04-11T09:04:53.254Z", - "references/cart_models/variables/cart_models.LineItem/page.mdx": "2025-04-11T09:04:53.296Z", - "references/cart_models/variables/cart_models.LineItemAdjustment/page.mdx": "2025-04-11T09:04:53.270Z", - "references/cart_models/variables/cart_models.LineItemTaxLine/page.mdx": "2025-04-11T09:04:53.278Z", - "references/cart_models/variables/cart_models.ShippingMethod/page.mdx": "2025-04-11T09:04:53.320Z", - "references/cart_models/variables/cart_models.ShippingMethodAdjustment/page.mdx": "2025-03-04T13:33:56.918Z", - "references/cart_models/variables/cart_models.ShippingMethodTaxLine/page.mdx": "2025-03-04T13:33:56.923Z", + "references/cart_models/variables/cart_models.Address/page.mdx": "2025-05-01T15:18:46.162Z", + "references/cart_models/variables/cart_models.Cart/page.mdx": "2025-05-01T15:18:46.174Z", + "references/cart_models/variables/cart_models.LineItem/page.mdx": "2025-05-01T15:18:46.196Z", + "references/cart_models/variables/cart_models.LineItemAdjustment/page.mdx": "2025-05-01T15:18:46.183Z", + "references/cart_models/variables/cart_models.LineItemTaxLine/page.mdx": "2025-05-01T15:18:46.187Z", + "references/cart_models/variables/cart_models.ShippingMethod/page.mdx": "2025-05-01T15:18:46.209Z", + "references/cart_models/variables/cart_models.ShippingMethodAdjustment/page.mdx": "2025-05-01T15:18:46.199Z", + "references/cart_models/variables/cart_models.ShippingMethodTaxLine/page.mdx": "2025-05-01T15:18:46.202Z", "references/dml/Property/methods/dml.Property.autoincrement/page.mdx": "2024-12-10T14:55:08.589Z", "references/dml/dml.Property/page.mdx": "2024-12-10T14:55:08.588Z", - "references/sales_channel_models/variables/sales_channel_models.SalesChannel/page.mdx": "2024-12-23T08:25:10.052Z", + "references/sales_channel_models/variables/sales_channel_models.SalesChannel/page.mdx": "2025-05-01T15:18:48.392Z", "references/store/types/store.Constructor/page.mdx": "2024-12-10T14:55:13.404Z", "references/store/types/store.ExpandScalar/page.mdx": "2024-12-10T14:55:13.391Z", "references/store/types/store.FilterQuery/page.mdx": "2024-12-10T14:55:13.397Z", @@ -5560,8 +5558,8 @@ export const generatedEditDates = { "references/store/types/store.Query/page.mdx": "2024-12-10T14:55:13.390Z", "references/store/types/store.ReadonlyPrimary/page.mdx": "2024-12-10T14:55:13.392Z", "references/store/types/store.Scalar/page.mdx": "2024-12-10T14:55:13.391Z", - "references/store_models/variables/store_models.Store/page.mdx": "2024-12-23T13:57:10.110Z", - "references/store_models/variables/store_models.StoreCurrency/page.mdx": "2024-12-23T13:57:10.109Z", + "references/store_models/variables/store_models.Store/page.mdx": "2025-05-01T15:18:48.400Z", + "references/store_models/variables/store_models.StoreCurrency/page.mdx": "2025-05-01T15:18:48.398Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StoreCalculatedPrice/page.mdx": "2025-03-04T13:33:51.211Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StorePrice/page.mdx": "2024-12-10T14:54:56.154Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StorePriceRule/page.mdx": "2024-12-10T14:54:56.155Z", @@ -5577,16 +5575,16 @@ export const generatedEditDates = { "app/recipes/commerce-automation/restock-notification/page.mdx": "2025-04-17T08:48:39.058Z", "app/integrations/guides/shipstation/page.mdx": "2025-02-26T11:21:46.879Z", "app/nextjs-starter/guides/customize-stripe/page.mdx": "2024-12-25T14:48:55.877Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflow/page.mdx": "2025-04-23T16:21:18.339Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflow/page.mdx": "2025-05-01T15:18:36.664Z", "references/core_flows/Cart/Workflows_Cart/variables/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflowId/page.mdx": "2024-12-17T16:57:22.044Z", "references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.calculateShippingOptionsPricesStep/page.mdx": "2025-04-11T09:04:36.188Z", "references/core_flows/Fulfillment/Steps_Fulfillment/variables/core_flows.Fulfillment.Steps_Fulfillment.calculateShippingOptionsPricesStepId/page.mdx": "2024-12-17T16:57:22.226Z", - "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.calculateShippingOptionsPricesWorkflow/page.mdx": "2025-04-23T16:21:18.855Z", + "references/core_flows/Fulfillment/Workflows_Fulfillment/functions/core_flows.Fulfillment.Workflows_Fulfillment.calculateShippingOptionsPricesWorkflow/page.mdx": "2025-05-01T15:18:37.205Z", "references/core_flows/Fulfillment/Workflows_Fulfillment/variables/core_flows.Fulfillment.Workflows_Fulfillment.calculateShippingOptionsPricesWorkflowId/page.mdx": "2024-12-17T16:57:22.302Z", - "references/customer_models/variables/customer_models.Customer/page.mdx": "2024-12-23T13:57:08.140Z", - "references/customer_models/variables/customer_models.CustomerAddress/page.mdx": "2024-12-23T13:57:08.132Z", - "references/customer_models/variables/customer_models.CustomerGroup/page.mdx": "2024-12-23T13:57:08.137Z", - "references/customer_models/variables/customer_models.CustomerGroupCustomer/page.mdx": "2024-12-23T08:25:04.665Z", + "references/customer_models/variables/customer_models.Customer/page.mdx": "2025-05-01T15:18:46.224Z", + "references/customer_models/variables/customer_models.CustomerAddress/page.mdx": "2025-05-01T15:18:46.215Z", + "references/customer_models/variables/customer_models.CustomerGroup/page.mdx": "2025-05-01T15:18:46.221Z", + "references/customer_models/variables/customer_models.CustomerGroupCustomer/page.mdx": "2025-05-01T15:18:46.218Z", "references/dml/Property_Configuration_Methods/methods/dml.Property_Configuration_Methods.computed/page.mdx": "2024-12-17T16:57:24.854Z", "references/dml/Property_Types/methods/dml.Property_Types.float/page.mdx": "2024-12-17T16:57:24.842Z", "references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.calculateShippingOptionsPrices/page.mdx": "2025-04-11T09:04:45.482Z", @@ -5605,28 +5603,28 @@ export const generatedEditDates = { "references/fulfillment/interfaces/fulfillment.ShippingMethodTaxLineDTO/page.mdx": "2024-12-23T13:57:04.500Z", "references/fulfillment/interfaces/fulfillment.TaxLineDTO/page.mdx": "2024-12-17T16:57:24.925Z", "references/fulfillment/types/fulfillment.CalculatedShippingOptionPrice/page.mdx": "2025-01-17T16:43:27.044Z", - "references/inventory_next_models/variables/inventory_next_models.InventoryItem/page.mdx": "2024-12-23T13:57:08.228Z", - "references/inventory_next_models/variables/inventory_next_models.InventoryLevel/page.mdx": "2024-12-23T13:57:08.231Z", - "references/inventory_next_models/variables/inventory_next_models.ReservationItem/page.mdx": "2024-12-23T13:57:08.234Z", - "references/payment_models/variables/payment_models.Capture/page.mdx": "2025-01-17T16:43:32.888Z", - "references/payment_models/variables/payment_models.Payment/page.mdx": "2025-01-17T16:43:32.913Z", - "references/payment_models/variables/payment_models.PaymentCollection/page.mdx": "2025-01-17T16:43:32.894Z", - "references/payment_models/variables/payment_models.PaymentProvider/page.mdx": "2025-01-17T16:43:32.898Z", - "references/payment_models/variables/payment_models.PaymentSession/page.mdx": "2025-01-17T16:43:32.904Z", - "references/payment_models/variables/payment_models.Refund/page.mdx": "2025-01-17T16:43:32.919Z", - "references/payment_models/variables/payment_models.RefundReason/page.mdx": "2025-01-17T16:43:32.915Z", - "references/promotion_models/variables/promotion_models.ApplicationMethod/page.mdx": "2025-02-24T10:48:47.496Z", - "references/promotion_models/variables/promotion_models.Campaign/page.mdx": "2025-02-24T10:48:47.506Z", - "references/promotion_models/variables/promotion_models.CampaignBudget/page.mdx": "2025-01-17T16:43:33.007Z", - "references/promotion_models/variables/promotion_models.Promotion/page.mdx": "2025-02-24T10:48:47.534Z", - "references/promotion_models/variables/promotion_models.PromotionRule/page.mdx": "2025-02-24T10:48:47.524Z", - "references/promotion_models/variables/promotion_models.PromotionRuleValue/page.mdx": "2025-02-24T10:48:47.512Z", - "references/stock_location_next_models/variables/stock_location_next_models.StockLocation/page.mdx": "2025-01-07T12:54:31.098Z", - "references/stock_location_next_models/variables/stock_location_next_models.StockLocationAddress/page.mdx": "2025-01-07T12:54:31.095Z", - "references/tax_models/variables/tax_models.TaxProvider/page.mdx": "2024-12-23T13:57:10.114Z", - "references/tax_models/variables/tax_models.TaxRate/page.mdx": "2024-12-23T13:57:10.119Z", - "references/tax_models/variables/tax_models.TaxRateRule/page.mdx": "2024-12-23T13:57:10.116Z", - "references/tax_models/variables/tax_models.TaxRegion/page.mdx": "2024-12-23T13:57:10.125Z", + "references/inventory_next_models/variables/inventory_next_models.InventoryItem/page.mdx": "2025-05-01T15:18:46.346Z", + "references/inventory_next_models/variables/inventory_next_models.InventoryLevel/page.mdx": "2025-05-01T15:18:46.350Z", + "references/inventory_next_models/variables/inventory_next_models.ReservationItem/page.mdx": "2025-05-01T15:18:46.353Z", + "references/payment_models/variables/payment_models.Capture/page.mdx": "2025-05-01T15:18:48.254Z", + "references/payment_models/variables/payment_models.Payment/page.mdx": "2025-05-01T15:18:48.273Z", + "references/payment_models/variables/payment_models.PaymentCollection/page.mdx": "2025-05-01T15:18:48.259Z", + "references/payment_models/variables/payment_models.PaymentProvider/page.mdx": "2025-05-01T15:18:48.262Z", + "references/payment_models/variables/payment_models.PaymentSession/page.mdx": "2025-05-01T15:18:48.267Z", + "references/payment_models/variables/payment_models.Refund/page.mdx": "2025-05-01T15:18:48.279Z", + "references/payment_models/variables/payment_models.RefundReason/page.mdx": "2025-05-01T15:18:48.275Z", + "references/promotion_models/variables/promotion_models.ApplicationMethod/page.mdx": "2025-05-01T15:18:48.365Z", + "references/promotion_models/variables/promotion_models.Campaign/page.mdx": "2025-05-01T15:18:48.371Z", + "references/promotion_models/variables/promotion_models.CampaignBudget/page.mdx": "2025-05-01T15:18:48.367Z", + "references/promotion_models/variables/promotion_models.Promotion/page.mdx": "2025-05-01T15:18:48.386Z", + "references/promotion_models/variables/promotion_models.PromotionRule/page.mdx": "2025-05-01T15:18:48.380Z", + "references/promotion_models/variables/promotion_models.PromotionRuleValue/page.mdx": "2025-05-01T15:18:48.374Z", + "references/stock_location_next_models/variables/stock_location_next_models.StockLocation/page.mdx": "2025-05-01T15:18:48.396Z", + "references/stock_location_next_models/variables/stock_location_next_models.StockLocationAddress/page.mdx": "2025-05-01T15:18:48.395Z", + "references/tax_models/variables/tax_models.TaxProvider/page.mdx": "2025-05-01T15:18:48.403Z", + "references/tax_models/variables/tax_models.TaxRate/page.mdx": "2025-05-01T15:18:48.409Z", + "references/tax_models/variables/tax_models.TaxRateRule/page.mdx": "2025-05-01T15:18:48.405Z", + "references/tax_models/variables/tax_models.TaxRegion/page.mdx": "2025-05-01T15:18:48.414Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StoreShippingOptionResponse/page.mdx": "2025-04-11T09:04:47.640Z", "references/types/HttpTypes/types/types.HttpTypes.StoreCalculateShippingOptionPrice/page.mdx": "2025-04-11T09:04:47.634Z", "references/types/WorkflowTypes/FulfillmentWorkflow/types/types.WorkflowTypes.FulfillmentWorkflow.CalculateShippingOptionsPricesWorkflowInput/page.mdx": "2024-12-17T16:57:21.219Z", @@ -5647,18 +5645,18 @@ export const generatedEditDates = { "references/core_flows/interfaces/core_flows.GetItemTaxLinesStepInput/page.mdx": "2025-04-11T09:04:43.408Z", "references/dml/entity/types/dml.entity.DMLEntitySchemaBuilder/page.mdx": "2025-01-13T18:05:57.748Z", "references/fulfillment/interfaces/fulfillment.OrderCreditLineDTO/page.mdx": "2025-04-11T09:04:45.734Z", - "references/fulfillment_models/variables/fulfillment_models.Fulfillment/page.mdx": "2024-12-23T13:57:08.194Z", - "references/fulfillment_models/variables/fulfillment_models.FulfillmentAddress/page.mdx": "2024-12-23T12:30:31.975Z", - "references/fulfillment_models/variables/fulfillment_models.FulfillmentItem/page.mdx": "2024-12-23T13:57:08.179Z", - "references/fulfillment_models/variables/fulfillment_models.FulfillmentLabel/page.mdx": "2024-12-23T13:57:08.182Z", - "references/fulfillment_models/variables/fulfillment_models.FulfillmentProvider/page.mdx": "2024-12-23T12:30:31.982Z", - "references/fulfillment_models/variables/fulfillment_models.FulfillmentSet/page.mdx": "2024-12-23T13:57:08.185Z", - "references/fulfillment_models/variables/fulfillment_models.GeoZone/page.mdx": "2024-12-23T13:57:08.199Z", - "references/fulfillment_models/variables/fulfillment_models.ServiceZone/page.mdx": "2024-12-23T13:57:08.208Z", - "references/fulfillment_models/variables/fulfillment_models.ShippingOption/page.mdx": "2024-12-23T13:57:08.220Z", - "references/fulfillment_models/variables/fulfillment_models.ShippingOptionRule/page.mdx": "2024-12-23T13:57:08.211Z", - "references/fulfillment_models/variables/fulfillment_models.ShippingOptionType/page.mdx": "2024-12-23T13:57:08.214Z", - "references/fulfillment_models/variables/fulfillment_models.ShippingProfile/page.mdx": "2024-12-23T13:57:08.223Z", + "references/fulfillment_models/variables/fulfillment_models.Fulfillment/page.mdx": "2025-05-01T15:18:46.307Z", + "references/fulfillment_models/variables/fulfillment_models.FulfillmentAddress/page.mdx": "2025-05-01T15:18:46.289Z", + "references/fulfillment_models/variables/fulfillment_models.FulfillmentItem/page.mdx": "2025-05-01T15:18:46.293Z", + "references/fulfillment_models/variables/fulfillment_models.FulfillmentLabel/page.mdx": "2025-05-01T15:18:46.296Z", + "references/fulfillment_models/variables/fulfillment_models.FulfillmentProvider/page.mdx": "2025-05-01T15:18:46.297Z", + "references/fulfillment_models/variables/fulfillment_models.FulfillmentSet/page.mdx": "2025-05-01T15:18:46.299Z", + "references/fulfillment_models/variables/fulfillment_models.GeoZone/page.mdx": "2025-05-01T15:18:46.313Z", + "references/fulfillment_models/variables/fulfillment_models.ServiceZone/page.mdx": "2025-05-01T15:18:46.324Z", + "references/fulfillment_models/variables/fulfillment_models.ShippingOption/page.mdx": "2025-05-01T15:18:46.338Z", + "references/fulfillment_models/variables/fulfillment_models.ShippingOptionRule/page.mdx": "2025-05-01T15:18:46.327Z", + "references/fulfillment_models/variables/fulfillment_models.ShippingOptionType/page.mdx": "2025-05-01T15:18:46.330Z", + "references/fulfillment_models/variables/fulfillment_models.ShippingProfile/page.mdx": "2025-05-01T15:18:46.341Z", "references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.listFulfillmentOptions/page.mdx": "2025-04-11T09:04:54.266Z", "references/order/interfaces/order.OrderCreditLineDTO/page.mdx": "2025-04-11T09:04:49.593Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminFulfillmentProviderOption/page.mdx": "2024-12-23T12:30:28.566Z", @@ -5699,14 +5697,6 @@ export const generatedEditDates = { "references/core_flows/core_flows.User/page.mdx": "2024-12-23T12:30:26.564Z", "references/order/types/order.ReturnStatus/page.mdx": "2024-12-23T12:30:30.107Z", "references/fulfillment_models/types/fulfillment_models.ServiceZoneSchema/page.mdx": "2024-12-23T12:30:32.004Z", - "app/commerce-modules/auth/events/page.mdx": "2024-12-23T16:16:08.118Z", - "app/commerce-modules/cart/events/page.mdx": "2024-12-23T16:16:47.466Z", - "app/commerce-modules/customer/events/page.mdx": "2024-12-23T16:17:25.414Z", - "app/commerce-modules/order/events/page.mdx": "2024-12-23T16:19:41.929Z", - "app/commerce-modules/product/events/page.mdx": "2024-12-23T16:25:05.789Z", - "app/commerce-modules/region/events/page.mdx": "2024-12-23T16:26:13.661Z", - "app/commerce-modules/sales-channel/events/page.mdx": "2024-12-23T16:27:10.675Z", - "app/commerce-modules/user/events/page.mdx": "2024-12-23T16:28:41.193Z", "app/commerce-modules/api-key/admin-widget-zones/page.mdx": "2024-12-24T08:29:56.989Z", "app/commerce-modules/auth/admin-widget-zones/page.mdx": "2024-12-24T08:11:08.250Z", "app/commerce-modules/customer/admin-widget-zones/page.mdx": "2024-12-24T08:07:15.439Z", @@ -5724,11 +5714,9 @@ export const generatedEditDates = { "app/commerce-modules/user/admin-widget-zones/page.mdx": "2024-12-24T08:48:14.186Z", "app/commerce-modules/currency/links-to-other-modules/page.mdx": "2025-04-17T15:40:23.148Z", "app/commerce-modules/customer/links-to-other-modules/page.mdx": "2025-04-17T15:43:54.168Z", - "app/commerce-modules/fulfillment/events/page.mdx": "2024-12-31T09:37:49.253Z", - "app/commerce-modules/payment/events/page.mdx": "2024-12-31T09:41:56.582Z", - "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.refundPaymentsStep/page.mdx": "2025-04-23T16:21:20.959Z", + "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.refundPaymentsStep/page.mdx": "2025-05-01T15:18:39.299Z", "references/core_flows/Payment/Steps_Payment/variables/core_flows.Payment.Steps_Payment.refundPaymentsStepId/page.mdx": "2025-01-07T12:54:17.422Z", - "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.refundPaymentsWorkflow/page.mdx": "2025-04-23T16:21:20.993Z", + "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.refundPaymentsWorkflow/page.mdx": "2025-05-01T15:18:39.333Z", "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.validatePaymentsRefundStep/page.mdx": "2025-01-20T08:25:22.137Z", "references/core_flows/Payment/Workflows_Payment/variables/core_flows.Payment.Workflows_Payment.refundPaymentsWorkflowId/page.mdx": "2025-01-07T12:54:17.465Z", "references/core_flows/Tax/Steps_Tax/functions/core_flows.Tax.Steps_Tax.updateTaxRegionsStep/page.mdx": "2025-04-11T09:04:41.551Z", @@ -5784,7 +5772,7 @@ export const generatedEditDates = { "app/commerce-modules/tax/workflows/page.mdx": "2025-01-09T13:41:46.504Z", "app/commerce-modules/user/js-sdk/page.mdx": "2025-01-09T12:55:02.289Z", "app/commerce-modules/user/workflows/page.mdx": "2025-01-09T13:41:46.302Z", - "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.batchInventoryItemLevelsWorkflow/page.mdx": "2025-04-23T16:21:18.954Z", + "references/core_flows/Inventory/Workflows_Inventory/functions/core_flows.Inventory.Workflows_Inventory.batchInventoryItemLevelsWorkflow/page.mdx": "2025-05-01T15:18:37.307Z", "references/core_flows/Inventory/Workflows_Inventory/variables/core_flows.Inventory.Workflows_Inventory.batchInventoryItemLevelsWorkflowId/page.mdx": "2025-01-13T17:30:23.527Z", "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.validateProductInputStep/page.mdx": "2025-02-24T10:48:32.070Z", "references/core_flows/interfaces/core_flows.BatchInventoryItemLevelsWorkflowInput/page.mdx": "2025-01-13T17:30:26.219Z", @@ -5839,13 +5827,13 @@ export const generatedEditDates = { "references/utils/PromotionUtils/enums/utils.PromotionUtils.PromotionStatus/page.mdx": "2025-01-17T16:43:30.762Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateAndReturnShippingMethodsDataStep/page.mdx": "2025-03-04T13:33:40.459Z", "references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateCartStep/page.mdx": "2025-04-11T09:04:35.708Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.refreshCartItemsWorkflow/page.mdx": "2025-04-23T16:21:18.348Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.refreshCartShippingMethodsWorkflow/page.mdx": "2025-04-23T16:21:18.351Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrUpdateOrderPaymentCollectionWorkflow/page.mdx": "2025-04-23T16:21:19.751Z", - "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.deleteRefundReasonsWorkflow/page.mdx": "2025-04-23T16:21:21.033Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.refreshCartItemsWorkflow/page.mdx": "2025-05-01T15:18:36.672Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.refreshCartShippingMethodsWorkflow/page.mdx": "2025-05-01T15:18:36.675Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrUpdateOrderPaymentCollectionWorkflow/page.mdx": "2025-05-01T15:18:38.034Z", + "references/core_flows/Payment_Collection/Workflows_Payment_Collection/functions/core_flows.Payment_Collection.Workflows_Payment_Collection.deleteRefundReasonsWorkflow/page.mdx": "2025-05-01T15:18:39.374Z", "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.getVariantAvailabilityStep/page.mdx": "2025-01-17T16:43:24.975Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionsStatusWorkflow/page.mdx": "2025-04-23T16:21:21.386Z", - "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionsValidationStep/page.mdx": "2025-04-23T16:21:21.383Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionsStatusWorkflow/page.mdx": "2025-05-01T15:18:39.777Z", + "references/core_flows/Promotion/Workflows_Promotion/functions/core_flows.Promotion.Workflows_Promotion.updatePromotionsValidationStep/page.mdx": "2025-05-01T15:18:39.773Z", "references/core_flows/interfaces/core_flows.ValidateCartStepInput/page.mdx": "2025-04-11T09:04:41.757Z", "references/core_flows/interfaces/core_flows.ValidateProductInputStepInput/page.mdx": "2025-02-24T10:48:34.257Z", "references/core_flows/types/core_flows.ThrowUnlessPaymentCollectionNotePaidInput/page.mdx": "2025-01-17T16:43:25.819Z", @@ -5854,17 +5842,17 @@ export const generatedEditDates = { "app/plugins/guides/wishlist/page.mdx": "2025-04-17T08:48:07.059Z", "app/plugins/page.mdx": "2025-02-26T11:39:25.709Z", "app/admin-components/components/data-table/page.mdx": "2025-03-03T14:55:58.556Z", - "references/order_models/variables/order_models.Order/page.mdx": "2025-01-27T11:43:58.788Z", - "references/order_models/variables/order_models.OrderAddress/page.mdx": "2025-01-27T11:43:58.773Z", + "references/order_models/variables/order_models.Order/page.mdx": "2025-05-01T15:18:48.245Z", + "references/order_models/variables/order_models.OrderAddress/page.mdx": "2025-05-01T15:18:48.229Z", "references/order_models/variables/order_models.OrderChange/page.mdx": "2025-01-27T11:43:58.781Z", "references/order_models/variables/order_models.OrderChangeAction/page.mdx": "2025-01-27T11:43:58.780Z", "references/order_models/variables/order_models.OrderClaim/page.mdx": "2025-01-27T11:43:58.775Z", "references/order_models/variables/order_models.OrderClaimItem/page.mdx": "2025-01-27T11:43:58.774Z", "references/order_models/variables/order_models.OrderClaimItemImage/page.mdx": "2025-01-27T11:43:58.774Z", - "references/order_models/variables/order_models.OrderCreditLine/page.mdx": "2025-02-24T10:48:47.233Z", + "references/order_models/variables/order_models.OrderCreditLine/page.mdx": "2025-05-01T15:18:48.233Z", "references/order_models/variables/order_models.OrderExchange/page.mdx": "2025-01-27T11:43:58.778Z", "references/order_models/variables/order_models.OrderExchangeItem/page.mdx": "2025-01-27T11:43:58.777Z", - "references/order_models/variables/order_models.OrderItem/page.mdx": "2025-01-27T11:43:58.783Z", + "references/order_models/variables/order_models.OrderItem/page.mdx": "2025-05-01T15:18:48.240Z", "references/order_models/variables/order_models.OrderLineItem/page.mdx": "2025-01-27T11:43:58.780Z", "references/order_models/variables/order_models.OrderLineItemAdjustment/page.mdx": "2025-01-27T11:43:58.779Z", "references/order_models/variables/order_models.OrderLineItemTaxLine/page.mdx": "2025-01-27T11:43:58.779Z", @@ -5872,11 +5860,11 @@ export const generatedEditDates = { "references/order_models/variables/order_models.OrderShippingMethod/page.mdx": "2025-01-27T11:43:58.792Z", "references/order_models/variables/order_models.OrderShippingMethodAdjustment/page.mdx": "2025-01-27T11:43:58.791Z", "references/order_models/variables/order_models.OrderShippingMethodTaxLine/page.mdx": "2025-01-27T11:43:58.791Z", - "references/order_models/variables/order_models.OrderSummary/page.mdx": "2025-01-27T11:43:58.786Z", + "references/order_models/variables/order_models.OrderSummary/page.mdx": "2025-05-01T15:18:48.243Z", "references/order_models/variables/order_models.OrderTransaction/page.mdx": "2025-01-27T11:43:58.792Z", "references/order_models/variables/order_models.Return/page.mdx": "2025-01-27T11:43:58.790Z", "references/order_models/variables/order_models.ReturnItem/page.mdx": "2025-01-27T11:43:58.789Z", - "references/order_models/variables/order_models.ReturnReason/page.mdx": "2025-01-27T11:43:58.790Z", + "references/order_models/variables/order_models.ReturnReason/page.mdx": "2025-05-01T15:18:48.246Z", "references/payment/IPaymentModuleService/methods/payment.IPaymentModuleService.listAndCountPaymentMethods/page.mdx": "2025-04-11T09:04:50.994Z", "references/payment/IPaymentModuleService/methods/payment.IPaymentModuleService.listPaymentMethods/page.mdx": "2025-04-11T09:04:50.992Z", "references/payment/interfaces/payment.FilterablePaymentMethodProps/page.mdx": "2025-02-24T10:48:40.957Z", @@ -5911,7 +5899,7 @@ export const generatedEditDates = { "references/payment/types/payment.PaymentAccountHolderDTO/page.mdx": "2025-02-11T11:36:52.944Z", "references/payment/types/payment.PaymentAddressDTO/page.mdx": "2025-02-11T11:36:52.940Z", "references/payment/types/payment.PaymentCustomerDTO/page.mdx": "2025-02-11T11:36:52.942Z", - "references/payment_models/variables/payment_models.AccountHolder/page.mdx": "2025-02-11T11:36:58.765Z", + "references/payment_models/variables/payment_models.AccountHolder/page.mdx": "2025-05-01T15:18:48.250Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StoreCartAddPromotion/page.mdx": "2025-02-11T11:36:48.999Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StoreCartPromotion/page.mdx": "2025-02-11T11:36:48.986Z", "references/types/HttpTypes/interfaces/types.HttpTypes.StoreCartRemovePromotion/page.mdx": "2025-02-11T11:36:48.998Z", @@ -6021,9 +6009,9 @@ export const generatedEditDates = { "app/examples/guides/quote-management/page.mdx": "2025-04-17T08:50:17.061Z", "references/cart/interfaces/cart.CartCreditLineDTO/page.mdx": "2025-03-04T13:33:48.207Z", "references/cart/interfaces/cart.UpdateLineItemWithoutSelectorDTO/page.mdx": "2025-03-04T13:33:48.254Z", - "references/cart_models/variables/cart_models.CreditLine/page.mdx": "2025-04-11T09:04:53.262Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.createCartCreditLinesWorkflow/page.mdx": "2025-04-23T16:21:18.304Z", - "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.deleteCartCreditLinesWorkflow/page.mdx": "2025-04-23T16:21:18.336Z", + "references/cart_models/variables/cart_models.CreditLine/page.mdx": "2025-05-01T15:18:46.179Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.createCartCreditLinesWorkflow/page.mdx": "2025-05-01T15:18:36.631Z", + "references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.deleteCartCreditLinesWorkflow/page.mdx": "2025-05-01T15:18:36.661Z", "references/core_flows/Cart/Workflows_Cart/variables/core_flows.Cart.Workflows_Cart.createCartCreditLinesWorkflowId/page.mdx": "2025-03-04T13:33:40.515Z", "references/core_flows/Cart/Workflows_Cart/variables/core_flows.Cart.Workflows_Cart.deleteCartCreditLinesWorkflowId/page.mdx": "2025-03-04T13:33:40.574Z", "references/fulfillment/interfaces/fulfillment.CartCreditLineDTO/page.mdx": "2025-03-04T13:33:48.973Z", @@ -6054,7 +6042,7 @@ export const generatedEditDates = { "references/modules/file_service/page.mdx": "2025-03-17T15:24:03.025Z", "references/modules/notification_service/page.mdx": "2025-03-17T15:24:05.164Z", "references/notification_service/interfaces/notification_service.INotificationModuleService/page.mdx": "2025-04-11T09:04:49.209Z", - "app/nextjs-starter/guides/revalidate-cache/page.mdx": "2025-04-18T15:24:15.919Z", + "app/nextjs-starter/guides/revalidate-cache/page.mdx": "2025-05-01T15:33:42.490Z", "app/storefront-development/cart/totals/page.mdx": "2025-03-27T14:47:14.252Z", "app/storefront-development/checkout/order-confirmation/page.mdx": "2025-03-27T14:29:45.669Z", "app/how-to-tutorials/tutorials/product-reviews/page.mdx": "2025-04-28T15:58:01.411Z", @@ -6071,7 +6059,7 @@ export const generatedEditDates = { "app/integrations/guides/algolia/page.mdx": "2025-04-17T08:29:01.433Z", "app/integrations/guides/magento/page.mdx": "2025-04-17T08:29:01.500Z", "app/js-sdk/auth/overview/page.mdx": "2025-03-28T08:05:32.622Z", - "app/how-to-tutorials/tutorials/loyalty-points/page.mdx": "2025-04-17T14:31:04.727Z", + "app/how-to-tutorials/tutorials/loyalty-points/page.mdx": "2025-05-01T15:33:24.035Z", "references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.plugin/page.mdx": "2025-04-11T09:04:55.084Z", "references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.createAddress/page.mdx": "2025-04-11T09:04:54.010Z", "references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.deleteAddress/page.mdx": "2025-04-11T09:04:54.015Z", @@ -6193,10 +6181,10 @@ export const generatedEditDates = { "references/core_flows/Draft_Order/core_flows.Draft_Order.Workflows_Draft_Order/page.mdx": "2025-04-24T08:23:52.496Z", "references/core_flows/Order/Steps_Order/functions/core_flows.Order.Steps_Order.registerOrderDeliveryStep/page.mdx": "2025-04-23T16:21:19.273Z", "references/core_flows/Order/Steps_Order/variables/core_flows.Order.Steps_Order.registerOrderDeliveryStepId/page.mdx": "2025-04-23T16:21:19.272Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderCreditLinesWorkflow/page.mdx": "2025-04-23T16:21:19.790Z", - "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.validateOrderCreditLinesStep/page.mdx": "2025-04-23T16:21:19.783Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.createOrderCreditLinesWorkflow/page.mdx": "2025-05-01T15:18:38.077Z", + "references/core_flows/Order/Workflows_Order/functions/core_flows.Order.Workflows_Order.validateOrderCreditLinesStep/page.mdx": "2025-05-01T15:18:38.067Z", "references/core_flows/Order/Workflows_Order/variables/core_flows.Order.Workflows_Order.createOrderCreditLinesWorkflowId/page.mdx": "2025-04-23T16:21:19.783Z", - "references/core_flows/Order/functions/core_flows.Order.fetchShippingOptionForOrderWorkflow/page.mdx": "2025-04-23T16:21:20.928Z", + "references/core_flows/Order/functions/core_flows.Order.fetchShippingOptionForOrderWorkflow/page.mdx": "2025-05-01T15:18:39.228Z", "references/core_flows/Order/variables/core_flows.Order.fetchShippingOptionsForOrderWorkflowId/page.mdx": "2025-04-23T16:21:20.924Z", "references/core_flows/core_flows.Draft_Order/page.mdx": "2025-04-23T16:21:18.488Z", "references/core_flows/interfaces/core_flows.CancelDraftOrderEditWorkflowInput/page.mdx": "2025-04-24T08:23:57.279Z", @@ -6207,7 +6195,7 @@ export const generatedEditDates = { "references/core_flows/types/core_flows.FetchShippingOptionForOrderWorkflowOutput/page.mdx": "2025-04-23T16:21:21.821Z", "references/core_flows/types/core_flows.RequestDraftOrderEditWorkflowInput/page.mdx": "2025-04-23T16:21:21.717Z", "references/core_flows/types/core_flows.GenerateProductCsvStepOutput/page.mdx": "2025-04-23T16:21:22.403Z", - "references/core_flows/Draft_Order/Workflows_Draft_Order/functions/core_flows.Draft_Order.Workflows_Draft_Order.convertDraftOrderStep/page.mdx": "2025-04-24T08:23:52.683Z", + "references/core_flows/Draft_Order/Workflows_Draft_Order/functions/core_flows.Draft_Order.Workflows_Draft_Order.convertDraftOrderStep/page.mdx": "2025-05-01T15:18:36.914Z", "references/core_flows/Draft_Order/Workflows_Draft_Order/functions/core_flows.Draft_Order.Workflows_Draft_Order.updateDraftOrderStep/page.mdx": "2025-04-24T08:23:52.930Z", "references/core_flows/interfaces/core_flows.AddDraftOrderPromotionWorkflowInput/page.mdx": "2025-04-24T08:23:57.277Z", "references/core_flows/interfaces/core_flows.AddDraftOrderShippingMethodsWorkflowInput/page.mdx": "2025-04-24T08:23:57.278Z", @@ -6219,5 +6207,16 @@ export const generatedEditDates = { "references/core_flows/interfaces/core_flows.ValidateDraftOrderStepInput/page.mdx": "2025-04-24T08:23:57.277Z", "app/commerce-modules/product/guides/variant-inventory/page.mdx": "2025-04-25T14:22:42.329Z", "app/troubleshooting/validation-error/page.mdx": "2025-04-25T14:14:57.568Z", - "app/integrations/guides/contentful/page.mdx": "2025-04-30T09:54:16.698Z" + "app/integrations/guides/contentful/page.mdx": "2025-05-01T15:33:21.351Z", + "references/modules/events/page.mdx": "2025-05-01T15:18:45.921Z", + "references/module_events/module_events.Auth/page.mdx": "2025-05-01T15:18:45.932Z", + "references/module_events/module_events.Cart/page.mdx": "2025-05-01T15:18:45.930Z", + "references/module_events/module_events.Customer/page.mdx": "2025-05-01T15:18:45.931Z", + "references/module_events/module_events.Fulfillment/page.mdx": "2025-05-01T15:18:45.934Z", + "references/module_events/module_events.Order/page.mdx": "2025-05-01T15:18:45.931Z", + "references/module_events/module_events.Product/page.mdx": "2025-05-01T15:18:45.933Z", + "references/module_events/module_events.Region/page.mdx": "2025-05-01T15:18:45.934Z", + "references/module_events/module_events.Sales_Channel/page.mdx": "2025-05-01T15:18:45.933Z", + "references/module_events/module_events.User/page.mdx": "2025-05-01T15:18:45.932Z", + "references/modules/module_events/page.mdx": "2025-05-01T15:18:45.930Z" } \ No newline at end of file diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs index 94ab70d78c..f63d9cc911 100644 --- a/www/apps/resources/generated/files-map.mjs +++ b/www/apps/resources/generated/files-map.mjs @@ -107,10 +107,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx", "pathname": "/commerce-modules/auth/create-actor-type" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/auth/events/page.mdx", - "pathname": "/commerce-modules/auth/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/auth/js-sdk/page.mdx", "pathname": "/commerce-modules/auth/js-sdk" @@ -135,10 +131,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/cart/concepts/page.mdx", "pathname": "/commerce-modules/cart/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/cart/events/page.mdx", - "pathname": "/commerce-modules/cart/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/cart/extend/page.mdx", "pathname": "/commerce-modules/cart/extend" @@ -187,10 +179,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/customer/customer-accounts/page.mdx", "pathname": "/commerce-modules/customer/customer-accounts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/customer/events/page.mdx", - "pathname": "/commerce-modules/customer/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/customer/extend/page.mdx", "pathname": "/commerce-modules/customer/extend" @@ -219,10 +207,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/concepts/page.mdx", "pathname": "/commerce-modules/fulfillment/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx", - "pathname": "/commerce-modules/fulfillment/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/fulfillment-provider/page.mdx", "pathname": "/commerce-modules/fulfillment/fulfillment-provider" @@ -303,10 +287,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/order/edit/page.mdx", "pathname": "/commerce-modules/order/edit" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/order/events/page.mdx", - "pathname": "/commerce-modules/order/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/order/exchange/page.mdx", "pathname": "/commerce-modules/order/exchange" @@ -359,10 +339,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/payment/account-holder/page.mdx", "pathname": "/commerce-modules/payment/account-holder" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/payment/events/page.mdx", - "pathname": "/commerce-modules/payment/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/payment/js-sdk/page.mdx", "pathname": "/commerce-modules/payment/js-sdk" @@ -451,10 +427,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/product/admin-widget-zones/page.mdx", "pathname": "/commerce-modules/product/admin-widget-zones" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/product/events/page.mdx", - "pathname": "/commerce-modules/product/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/product/extend/page.mdx", "pathname": "/commerce-modules/product/extend" @@ -539,10 +511,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/region/admin-widget-zones/page.mdx", "pathname": "/commerce-modules/region/admin-widget-zones" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/region/events/page.mdx", - "pathname": "/commerce-modules/region/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/region/js-sdk/page.mdx", "pathname": "/commerce-modules/region/js-sdk" @@ -563,10 +531,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/admin-widget-zones/page.mdx", "pathname": "/commerce-modules/sales-channel/admin-widget-zones" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx", - "pathname": "/commerce-modules/sales-channel/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/js-sdk/page.mdx", "pathname": "/commerce-modules/sales-channel/js-sdk" @@ -667,10 +631,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/user/admin-widget-zones/page.mdx", "pathname": "/commerce-modules/user/admin-widget-zones" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/user/events/page.mdx", - "pathname": "/commerce-modules/user/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/user/js-sdk/page.mdx", "pathname": "/commerce-modules/user/js-sdk" @@ -751,10 +711,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/deployment/storefront/vercel/page.mdx", "pathname": "/deployment/storefront/vercel" }, - { - "filePath": "/www/apps/resources/app/events-reference/page.mdx", - "pathname": "/events-reference" - }, { "filePath": "/www/apps/resources/app/examples/guides/custom-item-price/page.mdx", "pathname": "/examples/guides/custom-item-price" @@ -13443,6 +13399,42 @@ export const filesMap = [ "filePath": "/www/apps/resources/references/medusa_config/types/medusa_config.PluginDetails/page.mdx", "pathname": "/references/medusa_config/types/medusa_config.PluginDetails" }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Auth/page.mdx", + "pathname": "/references/module_events/module_events.Auth" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Cart/page.mdx", + "pathname": "/references/module_events/module_events.Cart" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Customer/page.mdx", + "pathname": "/references/module_events/module_events.Customer" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Fulfillment/page.mdx", + "pathname": "/references/module_events/module_events.Fulfillment" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Order/page.mdx", + "pathname": "/references/module_events/module_events.Order" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Product/page.mdx", + "pathname": "/references/module_events/module_events.Product" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Region/page.mdx", + "pathname": "/references/module_events/module_events.Region" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.Sales_Channel/page.mdx", + "pathname": "/references/module_events/module_events.Sales_Channel" + }, + { + "filePath": "/www/apps/resources/references/module_events/module_events.User/page.mdx", + "pathname": "/references/module_events/module_events.User" + }, { "filePath": "/www/apps/resources/references/modules/api_key/page.mdx", "pathname": "/references/modules/api_key" @@ -13503,6 +13495,10 @@ export const filesMap = [ "filePath": "/www/apps/resources/references/modules/event/page.mdx", "pathname": "/references/modules/event" }, + { + "filePath": "/www/apps/resources/references/modules/events/page.mdx", + "pathname": "/references/modules/events" + }, { "filePath": "/www/apps/resources/references/modules/file/page.mdx", "pathname": "/references/modules/file" @@ -13551,6 +13547,10 @@ export const filesMap = [ "filePath": "/www/apps/resources/references/modules/medusa_config/page.mdx", "pathname": "/references/modules/medusa_config" }, + { + "filePath": "/www/apps/resources/references/modules/module_events/page.mdx", + "pathname": "/references/modules/module_events" + }, { "filePath": "/www/apps/resources/references/modules/modules_sdk/page.mdx", "pathname": "/references/modules/modules_sdk" diff --git a/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs b/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs index 1990dbaf12..908b64795e 100644 --- a/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs +++ b/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs @@ -774,7 +774,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/auth/events", + "path": "/references/auth/events", "title": "Events Reference", "children": [] }, @@ -1518,7 +1518,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/cart/events", + "path": "/references/cart/events", "title": "Events Reference", "children": [] }, @@ -2932,7 +2932,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/customer/events", + "path": "/references/customer/events", "title": "Events Reference", "children": [] }, @@ -4081,7 +4081,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/fulfillment/events", + "path": "/references/fulfillment/events", "title": "Events Reference", "children": [] }, @@ -7453,7 +7453,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/order/events", + "path": "/references/order/events", "title": "Events Reference", "children": [] }, @@ -9636,7 +9636,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/payment/events", + "path": "/references/payment/events", "title": "Events Reference", "children": [] }, @@ -12164,7 +12164,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/product/events", + "path": "/references/product/events", "title": "Events Reference", "children": [] }, @@ -14203,7 +14203,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/region/events", + "path": "/references/region/events", "title": "Events Reference", "children": [] }, @@ -14758,7 +14758,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/sales-channel/events", + "path": "/references/sales-channel/events", "title": "Events Reference", "children": [] }, @@ -16889,7 +16889,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/commerce-modules/user/events", + "path": "/references/user/events", "title": "Events Reference", "children": [] }, diff --git a/www/apps/resources/generated/generated-references-sidebar.mjs b/www/apps/resources/generated/generated-references-sidebar.mjs index 1f979c8e96..042b5562f5 100644 --- a/www/apps/resources/generated/generated-references-sidebar.mjs +++ b/www/apps/resources/generated/generated-references-sidebar.mjs @@ -6733,7 +6733,7 @@ const generatedgeneratedReferencesSidebarSidebar = { "loaded": true, "isPathHref": true, "type": "link", - "path": "/events-reference", + "path": "/references/events", "title": "Events", "description": "List of events emitted by Medusa's Commerce Modules.", "children": [] diff --git a/www/apps/resources/generated/slug-changes.mjs b/www/apps/resources/generated/slug-changes.mjs index 02df2ac3d1..4b570f7cd7 100644 --- a/www/apps/resources/generated/slug-changes.mjs +++ b/www/apps/resources/generated/slug-changes.mjs @@ -4854,6 +4854,51 @@ export const slugChanges = [ "newSlug": "/references/medusa-config", "filePath": "/www/apps/resources/references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx" }, + { + "origSlug": "/references/module_events/module_events.Auth", + "newSlug": "/references/auth/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Auth/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Cart", + "newSlug": "/references/cart/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Cart/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Customer", + "newSlug": "/references/customer/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Customer/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Fulfillment", + "newSlug": "/references/fulfillment/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Fulfillment/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Order", + "newSlug": "/references/order/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Order/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Product", + "newSlug": "/references/product/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Product/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Region", + "newSlug": "/references/region/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Region/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.Sales_Channel", + "newSlug": "/references/sales-channel/events", + "filePath": "/www/apps/resources/references/module_events/module_events.Sales_Channel/page.mdx" + }, + { + "origSlug": "/references/module_events/module_events.User", + "newSlug": "/references/user/events", + "filePath": "/www/apps/resources/references/module_events/module_events.User/page.mdx" + }, { "origSlug": "/references/modules/api_key_models", "newSlug": "/references/api-key/models", @@ -4884,6 +4929,11 @@ export const slugChanges = [ "newSlug": "/references/data-model", "filePath": "/www/apps/resources/references/modules/dml/page.mdx" }, + { + "origSlug": "/references/modules/events", + "newSlug": "/references/events", + "filePath": "/www/apps/resources/references/modules/events/page.mdx" + }, { "origSlug": "/references/modules/fulfillment_models", "newSlug": "/references/fulfillment/models", diff --git a/www/apps/resources/next.config.mjs b/www/apps/resources/next.config.mjs index 4a3625c143..724f700862 100644 --- a/www/apps/resources/next.config.mjs +++ b/www/apps/resources/next.config.mjs @@ -173,6 +173,61 @@ const nextConfig = { destination: "/infrastructure-modules/:path*", permanent: true, }, + { + source: "/events-reference", + destination: "/references/events", + permanent: true, + }, + { + source: "/commerce-modules/auth/events", + destination: "/references/auth/events", + permanent: true, + }, + { + source: "/commerce-modules/cart/events", + destination: "/references/cart/events", + permanent: true, + }, + { + source: "/commerce-modules/customer/events", + destination: "/references/customer/events", + permanent: true, + }, + { + source: "/commerce-modules/fulfillment/events", + destination: "/references/fulfillment/events", + permanent: true, + }, + { + source: "/commerce-modules/order/events", + destination: "/references/order/events", + permanent: true, + }, + { + source: "/commerce-modules/payment/events", + destination: "/references/payment/events", + permanent: true, + }, + { + source: "/commerce-modules/product/events", + destination: "/references/product/events", + permanent: true, + }, + { + source: "/commerce-modules/region/events", + destination: "/references/region/events", + permanent: true, + }, + { + source: "/commerce-modules/sales-channel/events", + destination: "/references/sales-channel/events", + permanent: true, + }, + { + source: "/commerce-modules/user/events", + destination: "/references/user/events", + permanent: true, + }, ] }, outputFileTracingExcludes: { diff --git a/www/apps/resources/references/module_events/module_events.Auth/page.mdx b/www/apps/resources/references/module_events/module_events.Auth/page.mdx new file mode 100644 index 0000000000..3752e7ae56 --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Auth/page.mdx @@ -0,0 +1,55 @@ +--- +slug: /references/auth/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Auth Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Auth Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Summary + + + + + +Event + + +Description + + + + + + +[auth.password_reset](#authpassword_reset) + + +Emitted when a reset password token is generated. You can listen to this event +to send a reset password email to the user or customer, for example. + + + +
+ +## `auth.password_reset` + +Emitted when a reset password token is generated. You can listen to this event +to send a reset password email to the user or customer, for example. + +### Payload + +```ts +{ + entity_id, // The identifier of the user or customer. For example, an email address. + actor_type, // The type of actor. For example, "customer", "user", or custom. + token, // The generated token. +} +``` + +### Workflows Emitting this Event + +- [generateResetPasswordTokenWorkflow](/references/medusa-workflows/generateResetPasswordTokenWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Cart/page.mdx b/www/apps/resources/references/module_events/module_events.Cart/page.mdx new file mode 100644 index 0000000000..33765314b9 --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Cart/page.mdx @@ -0,0 +1,134 @@ +--- +slug: /references/cart/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Cart Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Cart Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Summary + + + + + +Event + + +Description + + + + + + +[cart.created](#cartcreated) + + +Emitted when a cart is created. + + + + +[cart.updated](#cartupdated) + + +Emitted when a cart's details are updated. + + + + +[cart.customer_updated](#cartcustomer_updated) + + +Emitted when the customer in the cart is updated. + + + + +[cart.region_updated](#cartregion_updated) + + +Emitted when the cart's region is updated. This +event is emitted alongside the CartWorkflowEvents.UPDATED event. + + + +
+ +## `cart.created` + +Emitted when a cart is created. + +### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +### Workflows Emitting this Event + +- [createCartWorkflow](/references/medusa-workflows/createCartWorkflow) + +--- + +## `cart.updated` + +Emitted when a cart's details are updated. + +### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +### Workflows Emitting this Event + +- [updateLineItemInCartWorkflow](/references/medusa-workflows/updateLineItemInCartWorkflow) +- [updateCartWorkflow](/references/medusa-workflows/updateCartWorkflow) +- [addToCartWorkflow](/references/medusa-workflows/addToCartWorkflow) +- [addShippingMethodToCartWorkflow](/references/medusa-workflows/addShippingMethodToCartWorkflow) + +--- + +## `cart.customer_updated` + +Emitted when the customer in the cart is updated. + +### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +### Workflows Emitting this Event + +- [updateCartWorkflow](/references/medusa-workflows/updateCartWorkflow) + +--- + +## `cart.region_updated` + +Emitted when the cart's region is updated. This +event is emitted alongside the CartWorkflowEvents.UPDATED event. + +### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +### Workflows Emitting this Event + +- [updateCartWorkflow](/references/medusa-workflows/updateCartWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Customer/page.mdx b/www/apps/resources/references/module_events/module_events.Customer/page.mdx new file mode 100644 index 0000000000..5f080af94e --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Customer/page.mdx @@ -0,0 +1,105 @@ +--- +slug: /references/customer/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Customer Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Customer Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Summary + + + + + +Event + + +Description + + + + + + +[customer.created](#customercreated) + + +Emitted when a customer is created. + + + + +[customer.updated](#customerupdated) + + +Emitted when a customer is updated. + + + + +[customer.deleted](#customerdeleted) + + +Emitted when a customer is deleted. + + + +
+ +## `customer.created` + +Emitted when a customer is created. + +### Payload + +```ts +[{ + id, // The ID of the customer +}] +``` + +### Workflows Emitting this Event + +- [createCustomersWorkflow](/references/medusa-workflows/createCustomersWorkflow) +- [createCustomerAccountWorkflow](/references/medusa-workflows/createCustomerAccountWorkflow) + +--- + +## `customer.updated` + +Emitted when a customer is updated. + +### Payload + +```ts +[{ + id, // The ID of the customer +}] +``` + +### Workflows Emitting this Event + +- [updateCustomersWorkflow](/references/medusa-workflows/updateCustomersWorkflow) + +--- + +## `customer.deleted` + +Emitted when a customer is deleted. + +### Payload + +```ts +[{ + id, // The ID of the customer +}] +``` + +### Workflows Emitting this Event + +- [deleteCustomersWorkflow](/references/medusa-workflows/deleteCustomersWorkflow) +- [removeCustomerAccountWorkflow](/references/medusa-workflows/removeCustomerAccountWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Fulfillment/page.mdx b/www/apps/resources/references/module_events/module_events.Fulfillment/page.mdx new file mode 100644 index 0000000000..49be660c67 --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Fulfillment/page.mdx @@ -0,0 +1,78 @@ +--- +slug: /references/fulfillment/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Fulfillment Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Fulfillment Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Summary + + + + + +Event + + +Description + + + + + + +[shipment.created](#shipmentcreated) + + +Emitted when a shipment is created for an order. + + + + +[delivery.created](#deliverycreated) + + +Emitted when a fulfillment is marked as delivered. + + + +
+ +## `shipment.created` + +Emitted when a shipment is created for an order. + +### Payload + +```ts +{ + id, // the ID of the shipment + no_notification, // whether to notify the customer +} +``` + +### Workflows Emitting this Event + +- [createOrderShipmentWorkflow](/references/medusa-workflows/createOrderShipmentWorkflow) + +--- + +## `delivery.created` + +Emitted when a fulfillment is marked as delivered. + +### Payload + +```ts +{ + id, // the ID of the fulfillment +} +``` + +### Workflows Emitting this Event + +- [markOrderFulfillmentAsDeliveredWorkflow](/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Order/page.mdx b/www/apps/resources/references/module_events/module_events.Order/page.mdx new file mode 100644 index 0000000000..9fd02958c8 --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Order/page.mdx @@ -0,0 +1,459 @@ +--- +slug: /references/order/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Order Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Order Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Order Events + +### Summary + + + + + +Event + + +Description + + + + + + +[order.updated](#orderupdated) + + +Emitted when the details of an order or draft order is updated. This +doesn't include updates made by an edit. + + + + +[order.placed](#orderplaced) + + +Emitted when an order is placed, or when a draft order is converted to an +order. + + + + +[order.canceled](#ordercanceled) + + +Emitted when an order is canceld. + + + + +[order.completed](#ordercompleted) + + +Emitted when orders are completed. + + + + +[order.archived](#orderarchived) + + +Emitted when an order is archived. + + + + +[order.fulfillment_created](#orderfulfillment_created) + + +Emitted when a fulfillment is created for an order. + + + + +[order.fulfillment_canceled](#orderfulfillment_canceled) + + +Emitted when an order's fulfillment is canceled. + + + + +[order.return_requested](#orderreturn_requested) + + +Emitted when a return request is confirmed. + + + + +[order.return_received](#orderreturn_received) + + +Emitted when a return is marked as received. + + + + +[order.claim_created](#orderclaim_created) + + +Emitted when a claim is created for an order. + + + + +[order.exchange_created](#orderexchange_created) + + +Emitted when an exchange is created for an order. + + + + +[order.transfer_requested](#ordertransfer_requested) + + +Emitted when an order is requested to be transferred to +another customer. + + + +
+ +### `order.updated` + +Emitted when the details of an order or draft order is updated. This +doesn't include updates made by an edit. + +#### Payload + +```ts +{ + id, // The ID of the order +} +``` + +#### Workflows Emitting this Event + +- [updateOrderWorkflow](/references/medusa-workflows/updateOrderWorkflow) +- [updateDraftOrderWorkflow](/references/medusa-workflows/updateDraftOrderWorkflow) + +--- + +### `order.placed` + +Emitted when an order is placed, or when a draft order is converted to an +order. + +#### Payload + +```ts +{ + id, // The ID of the order +} +``` + +#### Workflows Emitting this Event + +- [completeCartWorkflow](/references/medusa-workflows/completeCartWorkflow) +- [convertDraftOrderWorkflow](/references/medusa-workflows/convertDraftOrderWorkflow) +- [processPaymentWorkflow](/references/medusa-workflows/processPaymentWorkflow) + +--- + +### `order.canceled` + +Emitted when an order is canceld. + +#### Payload + +```ts +{ + id, // The ID of the order +} +``` + +#### Workflows Emitting this Event + +- [cancelOrderWorkflow](/references/medusa-workflows/cancelOrderWorkflow) + +--- + +### `order.completed` + +Emitted when orders are completed. + +#### Payload + +```ts +[{ + id, // The ID of the order +}] +``` + +#### Workflows Emitting this Event + +- [completeOrderWorkflow](/references/medusa-workflows/completeOrderWorkflow) + +--- + +### `order.archived` + +Emitted when an order is archived. + +#### Payload + +```ts +[{ + id, // The ID of the order +}] +``` + +#### Workflows Emitting this Event + +- [archiveOrderWorkflow](/references/medusa-workflows/archiveOrderWorkflow) + +--- + +### `order.fulfillment_created` + +Emitted when a fulfillment is created for an order. + +#### Payload + +```ts +{ + order_id, // The ID of the order + fulfillment_id, // The ID of the fulfillment + no_notification, // Whether to notify the customer +} +``` + +#### Workflows Emitting this Event + +- [createOrderFulfillmentWorkflow](/references/medusa-workflows/createOrderFulfillmentWorkflow) + +--- + +### `order.fulfillment_canceled` + +Emitted when an order's fulfillment is canceled. + +#### Payload + +```ts +{ + order_id, // The ID of the order + fulfillment_id, // The ID of the fulfillment + no_notification, // Whether to notify the customer +} +``` + +#### Workflows Emitting this Event + +- [cancelOrderFulfillmentWorkflow](/references/medusa-workflows/cancelOrderFulfillmentWorkflow) + +--- + +### `order.return_requested` + +Emitted when a return request is confirmed. + +#### Payload + +```ts +{ + order_id, // The ID of the order + return_id, // The ID of the return +} +``` + +#### Workflows Emitting this Event + +- [createAndCompleteReturnOrderWorkflow](/references/medusa-workflows/createAndCompleteReturnOrderWorkflow) +- [confirmReturnRequestWorkflow](/references/medusa-workflows/confirmReturnRequestWorkflow) + +--- + +### `order.return_received` + +Emitted when a return is marked as received. + +#### Payload + +```ts +{ + order_id, // The ID of the order + return_id, // The ID of the return +} +``` + +#### Workflows Emitting this Event + +- [createAndCompleteReturnOrderWorkflow](/references/medusa-workflows/createAndCompleteReturnOrderWorkflow) +- [confirmReturnReceiveWorkflow](/references/medusa-workflows/confirmReturnReceiveWorkflow) + +--- + +### `order.claim_created` + +Emitted when a claim is created for an order. + +#### Payload + +```ts +{ + order_id, // The ID of the order + claim_id, // The ID of the claim +} +``` + +#### Workflows Emitting this Event + +- [confirmClaimRequestWorkflow](/references/medusa-workflows/confirmClaimRequestWorkflow) + +--- + +### `order.exchange_created` + +Emitted when an exchange is created for an order. + +#### Payload + +```ts +{ + order_id, // The ID of the order + exchange_id, // The ID of the exchange +} +``` + +#### Workflows Emitting this Event + +- [confirmExchangeRequestWorkflow](/references/medusa-workflows/confirmExchangeRequestWorkflow) + +--- + +### `order.transfer_requested` + +Emitted when an order is requested to be transferred to +another customer. + +#### Payload + +```ts +{ + id, // The ID of the order + order_change_id, // The ID of the order change created for the transfer +} +``` + +#### Workflows Emitting this Event + +- [requestOrderTransferWorkflow](/references/medusa-workflows/requestOrderTransferWorkflow) + +--- + +## Order Edit Events + +### Summary + + + + + +Event + + +Description + + + + + + +[order-edit.requested](#order-editrequested) + + +Emitted when an order edit is requested. + + + + +[order-edit.confirmed](#order-editconfirmed) + + +Emitted when an order edit request is confirmed. + + + + +[order-edit.canceled](#order-editcanceled) + + +Emitted when an order edit request is canceled. + + + +
+ +### `order-edit.requested` + +Emitted when an order edit is requested. + +#### Payload + +```ts +{ + order_id, // The ID of the order + actions, // The actions to edit the order +} +``` + +#### Workflows Emitting this Event + +- [requestOrderEditRequestWorkflow](/references/medusa-workflows/requestOrderEditRequestWorkflow) + +--- + +### `order-edit.confirmed` + +Emitted when an order edit request is confirmed. + +#### Payload + +```ts +{ + order_id, // The ID of the order + actions, // The actions to edit the order +} +``` + +#### Workflows Emitting this Event + +- [confirmOrderEditRequestWorkflow](/references/medusa-workflows/confirmOrderEditRequestWorkflow) + +--- + +### `order-edit.canceled` + +Emitted when an order edit request is canceled. + +#### Payload + +```ts +{ + order_id, // The ID of the order + actions, // The actions to edit the order +} +``` + +#### Workflows Emitting this Event + +- [cancelBeginOrderEditWorkflow](/references/medusa-workflows/cancelBeginOrderEditWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Product/page.mdx b/www/apps/resources/references/module_events/module_events.Product/page.mdx new file mode 100644 index 0000000000..b1aa930d97 --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Product/page.mdx @@ -0,0 +1,699 @@ +--- +slug: /references/product/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Product Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Product Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Product Category Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-category.created](#product-categorycreated) + + +Emitted when product categories are created. + + + + +[product-category.updated](#product-categoryupdated) + + +Emitted when product categories are updated. + + + + +[product-category.deleted](#product-categorydeleted) + + +Emitted when product categories are deleted. + + + +
+ +### `product-category.created` + +Emitted when product categories are created. + +#### Payload + +```ts +[{ + id, // The ID of the product category +}] +``` + +#### Workflows Emitting this Event + +- [createProductCategoriesWorkflow](/references/medusa-workflows/createProductCategoriesWorkflow) + +--- + +### `product-category.updated` + +Emitted when product categories are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product category +}] +``` + +#### Workflows Emitting this Event + +- [updateProductCategoriesWorkflow](/references/medusa-workflows/updateProductCategoriesWorkflow) + +--- + +### `product-category.deleted` + +Emitted when product categories are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product category +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductCategoriesWorkflow](/references/medusa-workflows/deleteProductCategoriesWorkflow) + +--- + +## Product Collection Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-collection.created](#product-collectioncreated) + + +Emitted when product collections are created. + + + + +[product-collection.updated](#product-collectionupdated) + + +Emitted when product collections are updated. + + + + +[product-collection.deleted](#product-collectiondeleted) + + +Emitted when product collections are deleted. + + + +
+ +### `product-collection.created` + +Emitted when product collections are created. + +#### Payload + +```ts +[{ + id, // The ID of the product collection +}] +``` + +#### Workflows Emitting this Event + +- [createCollectionsWorkflow](/references/medusa-workflows/createCollectionsWorkflow) + +--- + +### `product-collection.updated` + +Emitted when product collections are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product collection +}] +``` + +#### Workflows Emitting this Event + +- [updateCollectionsWorkflow](/references/medusa-workflows/updateCollectionsWorkflow) + +--- + +### `product-collection.deleted` + +Emitted when product collections are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product collection +}] +``` + +#### Workflows Emitting this Event + +- [deleteCollectionsWorkflow](/references/medusa-workflows/deleteCollectionsWorkflow) + +--- + +## Product Variant Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-variant.updated](#product-variantupdated) + + +Emitted when product variants are updated. + + + + +[product-variant.created](#product-variantcreated) + + +Emitted when product variants are created. + + + + +[product-variant.deleted](#product-variantdeleted) + + +Emitted when product variants are deleted. + + + +
+ +### `product-variant.updated` + +Emitted when product variants are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product variant +}] +``` + +#### Workflows Emitting this Event + +- [updateProductVariantsWorkflow](/references/medusa-workflows/updateProductVariantsWorkflow) +- [batchProductVariantsWorkflow](/references/medusa-workflows/batchProductVariantsWorkflow) + +--- + +### `product-variant.created` + +Emitted when product variants are created. + +#### Payload + +```ts +[{ + id, // The ID of the product variant +}] +``` + +#### Workflows Emitting this Event + +- [createProductVariantsWorkflow](/references/medusa-workflows/createProductVariantsWorkflow) +- [createProductsWorkflow](/references/medusa-workflows/createProductsWorkflow) +- [batchProductVariantsWorkflow](/references/medusa-workflows/batchProductVariantsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +### `product-variant.deleted` + +Emitted when product variants are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product variant +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductVariantsWorkflow](/references/medusa-workflows/deleteProductVariantsWorkflow) +- [batchProductVariantsWorkflow](/references/medusa-workflows/batchProductVariantsWorkflow) + +--- + +## Product Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product.updated](#productupdated) + + +Emitted when products are updated. + + + + +[product.created](#productcreated) + + +Emitted when products are created. + + + + +[product.deleted](#productdeleted) + + +Emitted when products are deleted. + + + +
+ +### `product.updated` + +Emitted when products are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product +}] +``` + +#### Workflows Emitting this Event + +- [updateProductsWorkflow](/references/medusa-workflows/updateProductsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +### `product.created` + +Emitted when products are created. + +#### Payload + +```ts +[{ + id, // The ID of the product +}] +``` + +#### Workflows Emitting this Event + +- [createProductsWorkflow](/references/medusa-workflows/createProductsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +### `product.deleted` + +Emitted when products are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductsWorkflow](/references/medusa-workflows/deleteProductsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +## Product Type Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-type.updated](#product-typeupdated) + + +Emitted when product types are updated. + + + + +[product-type.created](#product-typecreated) + + +Emitted when product types are created. + + + + +[product-type.deleted](#product-typedeleted) + + +Emitted when product types are deleted. + + + +
+ +### `product-type.updated` + +Emitted when product types are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product type +}] +``` + +#### Workflows Emitting this Event + +- [updateProductTypesWorkflow](/references/medusa-workflows/updateProductTypesWorkflow) + +--- + +### `product-type.created` + +Emitted when product types are created. + +#### Payload + +```ts +[{ + id, // The ID of the product type +}] +``` + +#### Workflows Emitting this Event + +- [createProductTypesWorkflow](/references/medusa-workflows/createProductTypesWorkflow) + +--- + +### `product-type.deleted` + +Emitted when product types are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product type +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductTypesWorkflow](/references/medusa-workflows/deleteProductTypesWorkflow) + +--- + +## Product Tag Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-tag.updated](#product-tagupdated) + + +Emitted when product tags are updated. + + + + +[product-tag.created](#product-tagcreated) + + +Emitted when product tags are created. + + + + +[product-tag.deleted](#product-tagdeleted) + + +Emitted when product tags are deleted. + + + +
+ +### `product-tag.updated` + +Emitted when product tags are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product tag +}] +``` + +#### Workflows Emitting this Event + +- [updateProductTagsWorkflow](/references/medusa-workflows/updateProductTagsWorkflow) + +--- + +### `product-tag.created` + +Emitted when product tags are created. + +#### Payload + +```ts +[{ + id, // The ID of the product tag +}] +``` + +#### Workflows Emitting this Event + +- [createProductTagsWorkflow](/references/medusa-workflows/createProductTagsWorkflow) + +--- + +### `product-tag.deleted` + +Emitted when product tags are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product tag +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductTagsWorkflow](/references/medusa-workflows/deleteProductTagsWorkflow) + +--- + +## Product Option Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-option.updated](#product-optionupdated) + + +Emitted when product options are updated. + + + + +[product-option.created](#product-optioncreated) + + +Emitted when product options are created. + + + + +[product-option.deleted](#product-optiondeleted) + + +Emitted when product options are deleted. + + + +
+ +### `product-option.updated` + +Emitted when product options are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product option +}] +``` + +#### Workflows Emitting this Event + +- [updateProductOptionsWorkflow](/references/medusa-workflows/updateProductOptionsWorkflow) + +--- + +### `product-option.created` + +Emitted when product options are created. + +#### Payload + +```ts +[{ + id, // The ID of the product option +}] +``` + +#### Workflows Emitting this Event + +- [createProductOptionsWorkflow](/references/medusa-workflows/createProductOptionsWorkflow) + +--- + +### `product-option.deleted` + +Emitted when product options are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product option +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductOptionsWorkflow](/references/medusa-workflows/deleteProductOptionsWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Region/page.mdx b/www/apps/resources/references/module_events/module_events.Region/page.mdx new file mode 100644 index 0000000000..7eb665b8fb --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Region/page.mdx @@ -0,0 +1,103 @@ +--- +slug: /references/region/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Region Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Region Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Summary + + + + + +Event + + +Description + + + + + + +[region.updated](#regionupdated) + + +Emitted when regions are updated. + + + + +[region.created](#regioncreated) + + +Emitted when regions are created. + + + + +[region.deleted](#regiondeleted) + + +Emitted when regions are deleted. + + + +
+ +## `region.updated` + +Emitted when regions are updated. + +### Payload + +```ts +[{ + id, // The ID of the region +}] +``` + +### Workflows Emitting this Event + +- [updateRegionsWorkflow](/references/medusa-workflows/updateRegionsWorkflow) + +--- + +## `region.created` + +Emitted when regions are created. + +### Payload + +```ts +[{ + id, // The ID of the region +}] +``` + +### Workflows Emitting this Event + +- [createRegionsWorkflow](/references/medusa-workflows/createRegionsWorkflow) + +--- + +## `region.deleted` + +Emitted when regions are deleted. + +### Payload + +```ts +[{ + id, // The ID of the region +}] +``` + +### Workflows Emitting this Event + +- [deleteRegionsWorkflow](/references/medusa-workflows/deleteRegionsWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.Sales_Channel/page.mdx b/www/apps/resources/references/module_events/module_events.Sales_Channel/page.mdx new file mode 100644 index 0000000000..a6b6035506 --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.Sales_Channel/page.mdx @@ -0,0 +1,103 @@ +--- +slug: /references/sales-channel/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Sales Channel Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the Sales Channel Module. If you use the module outside the Medusa application, these events aren't emitted. + +## Summary + + + + + +Event + + +Description + + + + + + +[sales-channel.created](#sales-channelcreated) + + +Emitted when sales channels are created. + + + + +[sales-channel.updated](#sales-channelupdated) + + +Emitted when sales channels are updated. + + + + +[sales-channel.deleted](#sales-channeldeleted) + + +Emitted when sales channels are deleted. + + + +
+ +## `sales-channel.created` + +Emitted when sales channels are created. + +### Payload + +```ts +[{ + id, // The ID of the sales channel +}] +``` + +### Workflows Emitting this Event + +- [createSalesChannelsWorkflow](/references/medusa-workflows/createSalesChannelsWorkflow) + +--- + +## `sales-channel.updated` + +Emitted when sales channels are updated. + +### Payload + +```ts +[{ + id, // The ID of the sales channel +}] +``` + +### Workflows Emitting this Event + +- [updateSalesChannelsWorkflow](/references/medusa-workflows/updateSalesChannelsWorkflow) + +--- + +## `sales-channel.deleted` + +Emitted when sales channels are deleted. + +### Payload + +```ts +[{ + id, // The ID of the sales channel +}] +``` + +### Workflows Emitting this Event + +- [deleteSalesChannelsWorkflow](/references/medusa-workflows/deleteSalesChannelsWorkflow) diff --git a/www/apps/resources/references/module_events/module_events.User/page.mdx b/www/apps/resources/references/module_events/module_events.User/page.mdx new file mode 100644 index 0000000000..61cb721eed --- /dev/null +++ b/www/apps/resources/references/module_events/module_events.User/page.mdx @@ -0,0 +1,237 @@ +--- +slug: /references/user/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# User Module Events Reference + +This reference shows all the events emitted by the Medusa application related to the User Module. If you use the module outside the Medusa application, these events aren't emitted. + +## User Events + +### Summary + + + + + +Event + + +Description + + + + + + +[user.created](#usercreated) + + +Emitted when users are created. + + + + +[user.updated](#userupdated) + + +Emitted when users are updated. + + + + +[user.deleted](#userdeleted) + + +Emitted when users are deleted. + + + +
+ +### `user.created` + +Emitted when users are created. + +#### Payload + +```ts +[{ + id, // The ID of the user +}] +``` + +#### Workflows Emitting this Event + +- [createUsersWorkflow](/references/medusa-workflows/createUsersWorkflow) +- [createUserAccountWorkflow](/references/medusa-workflows/createUserAccountWorkflow) +- [acceptInviteWorkflow](/references/medusa-workflows/acceptInviteWorkflow) + +--- + +### `user.updated` + +Emitted when users are updated. + +#### Payload + +```ts +[{ + id, // The ID of the user +}] +``` + +#### Workflows Emitting this Event + +- [updateUsersWorkflow](/references/medusa-workflows/updateUsersWorkflow) + +--- + +### `user.deleted` + +Emitted when users are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the user +}] +``` + +#### Workflows Emitting this Event + +- [deleteUsersWorkflow](/references/medusa-workflows/deleteUsersWorkflow) +- [removeUserAccountWorkflow](/references/medusa-workflows/removeUserAccountWorkflow) + +--- + +## Invite Events + +### Summary + + + + + +Event + + +Description + + + + + + +[invite.accepted](#inviteaccepted) + + +Emitted when an invite is accepted. + + + + +[invite.created](#invitecreated) + + +Emitted when invites are created. You can listen to this event +to send an email to the invited users, for example. + + + + +[invite.deleted](#invitedeleted) + + +Emitted when invites are deleted. + + + + +[invite.resent](#inviteresent) + + +Emitted when invites should be resent because their token was +refreshed. You can listen to this event to send an email to the invited users, +for example. + + + +
+ +### `invite.accepted` + +Emitted when an invite is accepted. + +#### Payload + +```ts +{ + id, // The ID of the invite +} +``` + +#### Workflows Emitting this Event + +- [acceptInviteWorkflow](/references/medusa-workflows/acceptInviteWorkflow) + +--- + +### `invite.created` + +Emitted when invites are created. You can listen to this event +to send an email to the invited users, for example. + +#### Payload + +```ts +[{ + id, // The ID of the invite +}] +``` + +#### Workflows Emitting this Event + +- [createInvitesWorkflow](/references/medusa-workflows/createInvitesWorkflow) + +--- + +### `invite.deleted` + +Emitted when invites are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the invite +}] +``` + +#### Workflows Emitting this Event + +- [deleteInvitesWorkflow](/references/medusa-workflows/deleteInvitesWorkflow) + +--- + +### `invite.resent` + +Emitted when invites should be resent because their token was +refreshed. You can listen to this event to send an email to the invited users, +for example. + +#### Payload + +```ts +[{ + id, // The ID of the invite +}] +``` + +#### Workflows Emitting this Event + +- [refreshInviteTokensWorkflow](/references/medusa-workflows/refreshInviteTokensWorkflow) diff --git a/www/apps/resources/references/modules/events/page.mdx b/www/apps/resources/references/modules/events/page.mdx new file mode 100644 index 0000000000..d780ff5af0 --- /dev/null +++ b/www/apps/resources/references/modules/events/page.mdx @@ -0,0 +1,1921 @@ +--- +slug: /references/events +sidebar_label: Events Reference +--- + +import { TypeList } from "docs-ui" + +# Events Reference + +This documentation page includes the list of all events emitted by [Medusa's workflows](https://docs.medusajs.com/resources/medusa-workflows-reference). + +## Cart Events + +### Summary + + + + + +Event + + +Description + + + + + + +[cart.created](#cartcreated) + + +Emitted when a cart is created. + + + + +[cart.updated](#cartupdated) + + +Emitted when a cart's details are updated. + + + + +[cart.customer_updated](#cartcustomer_updated) + + +Emitted when the customer in the cart is updated. + + + + +[cart.region_updated](#cartregion_updated) + + +Emitted when the cart's region is updated. This +event is emitted alongside the CartWorkflowEvents.UPDATED event. + + + +
+ +### `cart.created` + +Emitted when a cart is created. + +#### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +#### Workflows Emitting this Event + +- [createCartWorkflow](/references/medusa-workflows/createCartWorkflow) + +--- + +### `cart.updated` + +Emitted when a cart's details are updated. + +#### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +#### Workflows Emitting this Event + +- [updateLineItemInCartWorkflow](/references/medusa-workflows/updateLineItemInCartWorkflow) +- [updateCartWorkflow](/references/medusa-workflows/updateCartWorkflow) +- [addToCartWorkflow](/references/medusa-workflows/addToCartWorkflow) +- [addShippingMethodToCartWorkflow](/references/medusa-workflows/addShippingMethodToCartWorkflow) + +--- + +### `cart.customer_updated` + +Emitted when the customer in the cart is updated. + +#### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +#### Workflows Emitting this Event + +- [updateCartWorkflow](/references/medusa-workflows/updateCartWorkflow) + +--- + +### `cart.region_updated` + +Emitted when the cart's region is updated. This +event is emitted alongside the CartWorkflowEvents.UPDATED event. + +#### Payload + +```ts +{ + id, // The ID of the cart +} +``` + +#### Workflows Emitting this Event + +- [updateCartWorkflow](/references/medusa-workflows/updateCartWorkflow) + +--- + +## Customer Events + +### Summary + + + + + +Event + + +Description + + + + + + +[customer.created](#customercreated) + + +Emitted when a customer is created. + + + + +[customer.updated](#customerupdated) + + +Emitted when a customer is updated. + + + + +[customer.deleted](#customerdeleted) + + +Emitted when a customer is deleted. + + + +
+ +### `customer.created` + +Emitted when a customer is created. + +#### Payload + +```ts +[{ + id, // The ID of the customer +}] +``` + +#### Workflows Emitting this Event + +- [createCustomersWorkflow](/references/medusa-workflows/createCustomersWorkflow) +- [createCustomerAccountWorkflow](/references/medusa-workflows/createCustomerAccountWorkflow) + +--- + +### `customer.updated` + +Emitted when a customer is updated. + +#### Payload + +```ts +[{ + id, // The ID of the customer +}] +``` + +#### Workflows Emitting this Event + +- [updateCustomersWorkflow](/references/medusa-workflows/updateCustomersWorkflow) + +--- + +### `customer.deleted` + +Emitted when a customer is deleted. + +#### Payload + +```ts +[{ + id, // The ID of the customer +}] +``` + +#### Workflows Emitting this Event + +- [deleteCustomersWorkflow](/references/medusa-workflows/deleteCustomersWorkflow) +- [removeCustomerAccountWorkflow](/references/medusa-workflows/removeCustomerAccountWorkflow) + +--- + +## Order Events + +### Summary + + + + + +Event + + +Description + + + + + + +[order.updated](#orderupdated) + + +Emitted when the details of an order or draft order is updated. This +doesn't include updates made by an edit. + + + + +[order.placed](#orderplaced) + + +Emitted when an order is placed, or when a draft order is converted to an +order. + + + + +[order.canceled](#ordercanceled) + + +Emitted when an order is canceld. + + + + +[order.completed](#ordercompleted) + + +Emitted when orders are completed. + + + + +[order.archived](#orderarchived) + + +Emitted when an order is archived. + + + + +[order.fulfillment_created](#orderfulfillment_created) + + +Emitted when a fulfillment is created for an order. + + + + +[order.fulfillment_canceled](#orderfulfillment_canceled) + + +Emitted when an order's fulfillment is canceled. + + + + +[order.return_requested](#orderreturn_requested) + + +Emitted when a return request is confirmed. + + + + +[order.return_received](#orderreturn_received) + + +Emitted when a return is marked as received. + + + + +[order.claim_created](#orderclaim_created) + + +Emitted when a claim is created for an order. + + + + +[order.exchange_created](#orderexchange_created) + + +Emitted when an exchange is created for an order. + + + + +[order.transfer_requested](#ordertransfer_requested) + + +Emitted when an order is requested to be transferred to +another customer. + + + +
+ +### `order.updated` + +Emitted when the details of an order or draft order is updated. This +doesn't include updates made by an edit. + +#### Payload + +```ts +{ + id, // The ID of the order +} +``` + +#### Workflows Emitting this Event + +- [updateOrderWorkflow](/references/medusa-workflows/updateOrderWorkflow) +- [updateDraftOrderWorkflow](/references/medusa-workflows/updateDraftOrderWorkflow) + +--- + +### `order.placed` + +Emitted when an order is placed, or when a draft order is converted to an +order. + +#### Payload + +```ts +{ + id, // The ID of the order +} +``` + +#### Workflows Emitting this Event + +- [completeCartWorkflow](/references/medusa-workflows/completeCartWorkflow) +- [convertDraftOrderWorkflow](/references/medusa-workflows/convertDraftOrderWorkflow) +- [processPaymentWorkflow](/references/medusa-workflows/processPaymentWorkflow) + +--- + +### `order.canceled` + +Emitted when an order is canceld. + +#### Payload + +```ts +{ + id, // The ID of the order +} +``` + +#### Workflows Emitting this Event + +- [cancelOrderWorkflow](/references/medusa-workflows/cancelOrderWorkflow) + +--- + +### `order.completed` + +Emitted when orders are completed. + +#### Payload + +```ts +[{ + id, // The ID of the order +}] +``` + +#### Workflows Emitting this Event + +- [completeOrderWorkflow](/references/medusa-workflows/completeOrderWorkflow) + +--- + +### `order.archived` + +Emitted when an order is archived. + +#### Payload + +```ts +[{ + id, // The ID of the order +}] +``` + +#### Workflows Emitting this Event + +- [archiveOrderWorkflow](/references/medusa-workflows/archiveOrderWorkflow) + +--- + +### `order.fulfillment_created` + +Emitted when a fulfillment is created for an order. + +#### Payload + +```ts +{ + order_id, // The ID of the order + fulfillment_id, // The ID of the fulfillment + no_notification, // Whether to notify the customer +} +``` + +#### Workflows Emitting this Event + +- [createOrderFulfillmentWorkflow](/references/medusa-workflows/createOrderFulfillmentWorkflow) + +--- + +### `order.fulfillment_canceled` + +Emitted when an order's fulfillment is canceled. + +#### Payload + +```ts +{ + order_id, // The ID of the order + fulfillment_id, // The ID of the fulfillment + no_notification, // Whether to notify the customer +} +``` + +#### Workflows Emitting this Event + +- [cancelOrderFulfillmentWorkflow](/references/medusa-workflows/cancelOrderFulfillmentWorkflow) + +--- + +### `order.return_requested` + +Emitted when a return request is confirmed. + +#### Payload + +```ts +{ + order_id, // The ID of the order + return_id, // The ID of the return +} +``` + +#### Workflows Emitting this Event + +- [createAndCompleteReturnOrderWorkflow](/references/medusa-workflows/createAndCompleteReturnOrderWorkflow) +- [confirmReturnRequestWorkflow](/references/medusa-workflows/confirmReturnRequestWorkflow) + +--- + +### `order.return_received` + +Emitted when a return is marked as received. + +#### Payload + +```ts +{ + order_id, // The ID of the order + return_id, // The ID of the return +} +``` + +#### Workflows Emitting this Event + +- [createAndCompleteReturnOrderWorkflow](/references/medusa-workflows/createAndCompleteReturnOrderWorkflow) +- [confirmReturnReceiveWorkflow](/references/medusa-workflows/confirmReturnReceiveWorkflow) + +--- + +### `order.claim_created` + +Emitted when a claim is created for an order. + +#### Payload + +```ts +{ + order_id, // The ID of the order + claim_id, // The ID of the claim +} +``` + +#### Workflows Emitting this Event + +- [confirmClaimRequestWorkflow](/references/medusa-workflows/confirmClaimRequestWorkflow) + +--- + +### `order.exchange_created` + +Emitted when an exchange is created for an order. + +#### Payload + +```ts +{ + order_id, // The ID of the order + exchange_id, // The ID of the exchange +} +``` + +#### Workflows Emitting this Event + +- [confirmExchangeRequestWorkflow](/references/medusa-workflows/confirmExchangeRequestWorkflow) + +--- + +### `order.transfer_requested` + +Emitted when an order is requested to be transferred to +another customer. + +#### Payload + +```ts +{ + id, // The ID of the order + order_change_id, // The ID of the order change created for the transfer +} +``` + +#### Workflows Emitting this Event + +- [requestOrderTransferWorkflow](/references/medusa-workflows/requestOrderTransferWorkflow) + +--- + +## Order Edit Events + +### Summary + + + + + +Event + + +Description + + + + + + +[order-edit.requested](#order-editrequested) + + +Emitted when an order edit is requested. + + + + +[order-edit.confirmed](#order-editconfirmed) + + +Emitted when an order edit request is confirmed. + + + + +[order-edit.canceled](#order-editcanceled) + + +Emitted when an order edit request is canceled. + + + +
+ +### `order-edit.requested` + +Emitted when an order edit is requested. + +#### Payload + +```ts +{ + order_id, // The ID of the order + actions, // The actions to edit the order +} +``` + +#### Workflows Emitting this Event + +- [requestOrderEditRequestWorkflow](/references/medusa-workflows/requestOrderEditRequestWorkflow) + +--- + +### `order-edit.confirmed` + +Emitted when an order edit request is confirmed. + +#### Payload + +```ts +{ + order_id, // The ID of the order + actions, // The actions to edit the order +} +``` + +#### Workflows Emitting this Event + +- [confirmOrderEditRequestWorkflow](/references/medusa-workflows/confirmOrderEditRequestWorkflow) + +--- + +### `order-edit.canceled` + +Emitted when an order edit request is canceled. + +#### Payload + +```ts +{ + order_id, // The ID of the order + actions, // The actions to edit the order +} +``` + +#### Workflows Emitting this Event + +- [cancelBeginOrderEditWorkflow](/references/medusa-workflows/cancelBeginOrderEditWorkflow) + +--- + +## User Events + +### Summary + + + + + +Event + + +Description + + + + + + +[user.created](#usercreated) + + +Emitted when users are created. + + + + +[user.updated](#userupdated) + + +Emitted when users are updated. + + + + +[user.deleted](#userdeleted) + + +Emitted when users are deleted. + + + +
+ +### `user.created` + +Emitted when users are created. + +#### Payload + +```ts +[{ + id, // The ID of the user +}] +``` + +#### Workflows Emitting this Event + +- [createUsersWorkflow](/references/medusa-workflows/createUsersWorkflow) +- [createUserAccountWorkflow](/references/medusa-workflows/createUserAccountWorkflow) +- [acceptInviteWorkflow](/references/medusa-workflows/acceptInviteWorkflow) + +--- + +### `user.updated` + +Emitted when users are updated. + +#### Payload + +```ts +[{ + id, // The ID of the user +}] +``` + +#### Workflows Emitting this Event + +- [updateUsersWorkflow](/references/medusa-workflows/updateUsersWorkflow) + +--- + +### `user.deleted` + +Emitted when users are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the user +}] +``` + +#### Workflows Emitting this Event + +- [deleteUsersWorkflow](/references/medusa-workflows/deleteUsersWorkflow) +- [removeUserAccountWorkflow](/references/medusa-workflows/removeUserAccountWorkflow) + +--- + +## Auth Events + +### Summary + + + + + +Event + + +Description + + + + + + +[auth.password_reset](#authpassword_reset) + + +Emitted when a reset password token is generated. You can listen to this event +to send a reset password email to the user or customer, for example. + + + +
+ +### `auth.password_reset` + +Emitted when a reset password token is generated. You can listen to this event +to send a reset password email to the user or customer, for example. + +#### Payload + +```ts +{ + entity_id, // The identifier of the user or customer. For example, an email address. + actor_type, // The type of actor. For example, "customer", "user", or custom. + token, // The generated token. +} +``` + +#### Workflows Emitting this Event + +- [generateResetPasswordTokenWorkflow](/references/medusa-workflows/generateResetPasswordTokenWorkflow) + +--- + +## Sales Channel Events + +### Summary + + + + + +Event + + +Description + + + + + + +[sales-channel.created](#sales-channelcreated) + + +Emitted when sales channels are created. + + + + +[sales-channel.updated](#sales-channelupdated) + + +Emitted when sales channels are updated. + + + + +[sales-channel.deleted](#sales-channeldeleted) + + +Emitted when sales channels are deleted. + + + +
+ +### `sales-channel.created` + +Emitted when sales channels are created. + +#### Payload + +```ts +[{ + id, // The ID of the sales channel +}] +``` + +#### Workflows Emitting this Event + +- [createSalesChannelsWorkflow](/references/medusa-workflows/createSalesChannelsWorkflow) + +--- + +### `sales-channel.updated` + +Emitted when sales channels are updated. + +#### Payload + +```ts +[{ + id, // The ID of the sales channel +}] +``` + +#### Workflows Emitting this Event + +- [updateSalesChannelsWorkflow](/references/medusa-workflows/updateSalesChannelsWorkflow) + +--- + +### `sales-channel.deleted` + +Emitted when sales channels are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the sales channel +}] +``` + +#### Workflows Emitting this Event + +- [deleteSalesChannelsWorkflow](/references/medusa-workflows/deleteSalesChannelsWorkflow) + +--- + +## Product Category Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-category.created](#product-categorycreated) + + +Emitted when product categories are created. + + + + +[product-category.updated](#product-categoryupdated) + + +Emitted when product categories are updated. + + + + +[product-category.deleted](#product-categorydeleted) + + +Emitted when product categories are deleted. + + + +
+ +### `product-category.created` + +Emitted when product categories are created. + +#### Payload + +```ts +[{ + id, // The ID of the product category +}] +``` + +#### Workflows Emitting this Event + +- [createProductCategoriesWorkflow](/references/medusa-workflows/createProductCategoriesWorkflow) + +--- + +### `product-category.updated` + +Emitted when product categories are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product category +}] +``` + +#### Workflows Emitting this Event + +- [updateProductCategoriesWorkflow](/references/medusa-workflows/updateProductCategoriesWorkflow) + +--- + +### `product-category.deleted` + +Emitted when product categories are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product category +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductCategoriesWorkflow](/references/medusa-workflows/deleteProductCategoriesWorkflow) + +--- + +## Product Collection Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-collection.created](#product-collectioncreated) + + +Emitted when product collections are created. + + + + +[product-collection.updated](#product-collectionupdated) + + +Emitted when product collections are updated. + + + + +[product-collection.deleted](#product-collectiondeleted) + + +Emitted when product collections are deleted. + + + +
+ +### `product-collection.created` + +Emitted when product collections are created. + +#### Payload + +```ts +[{ + id, // The ID of the product collection +}] +``` + +#### Workflows Emitting this Event + +- [createCollectionsWorkflow](/references/medusa-workflows/createCollectionsWorkflow) + +--- + +### `product-collection.updated` + +Emitted when product collections are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product collection +}] +``` + +#### Workflows Emitting this Event + +- [updateCollectionsWorkflow](/references/medusa-workflows/updateCollectionsWorkflow) + +--- + +### `product-collection.deleted` + +Emitted when product collections are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product collection +}] +``` + +#### Workflows Emitting this Event + +- [deleteCollectionsWorkflow](/references/medusa-workflows/deleteCollectionsWorkflow) + +--- + +## Product Variant Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-variant.updated](#product-variantupdated) + + +Emitted when product variants are updated. + + + + +[product-variant.created](#product-variantcreated) + + +Emitted when product variants are created. + + + + +[product-variant.deleted](#product-variantdeleted) + + +Emitted when product variants are deleted. + + + +
+ +### `product-variant.updated` + +Emitted when product variants are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product variant +}] +``` + +#### Workflows Emitting this Event + +- [updateProductVariantsWorkflow](/references/medusa-workflows/updateProductVariantsWorkflow) +- [batchProductVariantsWorkflow](/references/medusa-workflows/batchProductVariantsWorkflow) + +--- + +### `product-variant.created` + +Emitted when product variants are created. + +#### Payload + +```ts +[{ + id, // The ID of the product variant +}] +``` + +#### Workflows Emitting this Event + +- [createProductVariantsWorkflow](/references/medusa-workflows/createProductVariantsWorkflow) +- [createProductsWorkflow](/references/medusa-workflows/createProductsWorkflow) +- [batchProductVariantsWorkflow](/references/medusa-workflows/batchProductVariantsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +### `product-variant.deleted` + +Emitted when product variants are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product variant +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductVariantsWorkflow](/references/medusa-workflows/deleteProductVariantsWorkflow) +- [batchProductVariantsWorkflow](/references/medusa-workflows/batchProductVariantsWorkflow) + +--- + +## Product Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product.updated](#productupdated) + + +Emitted when products are updated. + + + + +[product.created](#productcreated) + + +Emitted when products are created. + + + + +[product.deleted](#productdeleted) + + +Emitted when products are deleted. + + + +
+ +### `product.updated` + +Emitted when products are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product +}] +``` + +#### Workflows Emitting this Event + +- [updateProductsWorkflow](/references/medusa-workflows/updateProductsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +### `product.created` + +Emitted when products are created. + +#### Payload + +```ts +[{ + id, // The ID of the product +}] +``` + +#### Workflows Emitting this Event + +- [createProductsWorkflow](/references/medusa-workflows/createProductsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +### `product.deleted` + +Emitted when products are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductsWorkflow](/references/medusa-workflows/deleteProductsWorkflow) +- [batchProductsWorkflow](/references/medusa-workflows/batchProductsWorkflow) +- [importProductsWorkflow](/references/medusa-workflows/importProductsWorkflow) + +--- + +## Product Type Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-type.updated](#product-typeupdated) + + +Emitted when product types are updated. + + + + +[product-type.created](#product-typecreated) + + +Emitted when product types are created. + + + + +[product-type.deleted](#product-typedeleted) + + +Emitted when product types are deleted. + + + +
+ +### `product-type.updated` + +Emitted when product types are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product type +}] +``` + +#### Workflows Emitting this Event + +- [updateProductTypesWorkflow](/references/medusa-workflows/updateProductTypesWorkflow) + +--- + +### `product-type.created` + +Emitted when product types are created. + +#### Payload + +```ts +[{ + id, // The ID of the product type +}] +``` + +#### Workflows Emitting this Event + +- [createProductTypesWorkflow](/references/medusa-workflows/createProductTypesWorkflow) + +--- + +### `product-type.deleted` + +Emitted when product types are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product type +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductTypesWorkflow](/references/medusa-workflows/deleteProductTypesWorkflow) + +--- + +## Product Tag Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-tag.updated](#product-tagupdated) + + +Emitted when product tags are updated. + + + + +[product-tag.created](#product-tagcreated) + + +Emitted when product tags are created. + + + + +[product-tag.deleted](#product-tagdeleted) + + +Emitted when product tags are deleted. + + + +
+ +### `product-tag.updated` + +Emitted when product tags are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product tag +}] +``` + +#### Workflows Emitting this Event + +- [updateProductTagsWorkflow](/references/medusa-workflows/updateProductTagsWorkflow) + +--- + +### `product-tag.created` + +Emitted when product tags are created. + +#### Payload + +```ts +[{ + id, // The ID of the product tag +}] +``` + +#### Workflows Emitting this Event + +- [createProductTagsWorkflow](/references/medusa-workflows/createProductTagsWorkflow) + +--- + +### `product-tag.deleted` + +Emitted when product tags are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product tag +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductTagsWorkflow](/references/medusa-workflows/deleteProductTagsWorkflow) + +--- + +## Product Option Events + +### Summary + + + + + +Event + + +Description + + + + + + +[product-option.updated](#product-optionupdated) + + +Emitted when product options are updated. + + + + +[product-option.created](#product-optioncreated) + + +Emitted when product options are created. + + + + +[product-option.deleted](#product-optiondeleted) + + +Emitted when product options are deleted. + + + +
+ +### `product-option.updated` + +Emitted when product options are updated. + +#### Payload + +```ts +[{ + id, // The ID of the product option +}] +``` + +#### Workflows Emitting this Event + +- [updateProductOptionsWorkflow](/references/medusa-workflows/updateProductOptionsWorkflow) + +--- + +### `product-option.created` + +Emitted when product options are created. + +#### Payload + +```ts +[{ + id, // The ID of the product option +}] +``` + +#### Workflows Emitting this Event + +- [createProductOptionsWorkflow](/references/medusa-workflows/createProductOptionsWorkflow) + +--- + +### `product-option.deleted` + +Emitted when product options are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the product option +}] +``` + +#### Workflows Emitting this Event + +- [deleteProductOptionsWorkflow](/references/medusa-workflows/deleteProductOptionsWorkflow) + +--- + +## Invite Events + +### Summary + + + + + +Event + + +Description + + + + + + +[invite.accepted](#inviteaccepted) + + +Emitted when an invite is accepted. + + + + +[invite.created](#invitecreated) + + +Emitted when invites are created. You can listen to this event +to send an email to the invited users, for example. + + + + +[invite.deleted](#invitedeleted) + + +Emitted when invites are deleted. + + + + +[invite.resent](#inviteresent) + + +Emitted when invites should be resent because their token was +refreshed. You can listen to this event to send an email to the invited users, +for example. + + + +
+ +### `invite.accepted` + +Emitted when an invite is accepted. + +#### Payload + +```ts +{ + id, // The ID of the invite +} +``` + +#### Workflows Emitting this Event + +- [acceptInviteWorkflow](/references/medusa-workflows/acceptInviteWorkflow) + +--- + +### `invite.created` + +Emitted when invites are created. You can listen to this event +to send an email to the invited users, for example. + +#### Payload + +```ts +[{ + id, // The ID of the invite +}] +``` + +#### Workflows Emitting this Event + +- [createInvitesWorkflow](/references/medusa-workflows/createInvitesWorkflow) + +--- + +### `invite.deleted` + +Emitted when invites are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the invite +}] +``` + +#### Workflows Emitting this Event + +- [deleteInvitesWorkflow](/references/medusa-workflows/deleteInvitesWorkflow) + +--- + +### `invite.resent` + +Emitted when invites should be resent because their token was +refreshed. You can listen to this event to send an email to the invited users, +for example. + +#### Payload + +```ts +[{ + id, // The ID of the invite +}] +``` + +#### Workflows Emitting this Event + +- [refreshInviteTokensWorkflow](/references/medusa-workflows/refreshInviteTokensWorkflow) + +--- + +## Region Events + +### Summary + + + + + +Event + + +Description + + + + + + +[region.updated](#regionupdated) + + +Emitted when regions are updated. + + + + +[region.created](#regioncreated) + + +Emitted when regions are created. + + + + +[region.deleted](#regiondeleted) + + +Emitted when regions are deleted. + + + +
+ +### `region.updated` + +Emitted when regions are updated. + +#### Payload + +```ts +[{ + id, // The ID of the region +}] +``` + +#### Workflows Emitting this Event + +- [updateRegionsWorkflow](/references/medusa-workflows/updateRegionsWorkflow) + +--- + +### `region.created` + +Emitted when regions are created. + +#### Payload + +```ts +[{ + id, // The ID of the region +}] +``` + +#### Workflows Emitting this Event + +- [createRegionsWorkflow](/references/medusa-workflows/createRegionsWorkflow) + +--- + +### `region.deleted` + +Emitted when regions are deleted. + +#### Payload + +```ts +[{ + id, // The ID of the region +}] +``` + +#### Workflows Emitting this Event + +- [deleteRegionsWorkflow](/references/medusa-workflows/deleteRegionsWorkflow) + +--- + +## Fulfillment Events + +### Summary + + + + + +Event + + +Description + + + + + + +[shipment.created](#shipmentcreated) + + +Emitted when a shipment is created for an order. + + + + +[delivery.created](#deliverycreated) + + +Emitted when a fulfillment is marked as delivered. + + + +
+ +### `shipment.created` + +Emitted when a shipment is created for an order. + +#### Payload + +```ts +{ + id, // the ID of the shipment + no_notification, // whether to notify the customer +} +``` + +#### Workflows Emitting this Event + +- [createOrderShipmentWorkflow](/references/medusa-workflows/createOrderShipmentWorkflow) + +--- + +### `delivery.created` + +Emitted when a fulfillment is marked as delivered. + +#### Payload + +```ts +{ + id, // the ID of the fulfillment +} +``` + +#### Workflows Emitting this Event + +- [markOrderFulfillmentAsDeliveredWorkflow](/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow) diff --git a/www/apps/resources/references/modules/module_events/page.mdx b/www/apps/resources/references/modules/module_events/page.mdx new file mode 100644 index 0000000000..ed38b7c0b0 --- /dev/null +++ b/www/apps/resources/references/modules/module_events/page.mdx @@ -0,0 +1,239 @@ +import { TypeList } from "docs-ui" + +# module-events + +## Namespaces + +- [Auth](../../module_events/module_events.Auth/page.mdx) +- [Cart](../../module_events/module_events.Cart/page.mdx) +- [Customer](../../module_events/module_events.Customer/page.mdx) +- [Fulfillment](../../module_events/module_events.Fulfillment/page.mdx) +- [Order](../../module_events/module_events.Order/page.mdx) +- [Product](../../module_events/module_events.Product/page.mdx) +- [Region](../../module_events/module_events.Region/page.mdx) +- [Sales Channel](../../module_events/module_events.Sales_Channel/page.mdx) +- [User](../../module_events/module_events.User/page.mdx) + +## Variables + +- [AuthWorkflowEvents](../../module_events/module_events.Auth/page.mdx#authworkflowevents) +- [CartWorkflowEvents](../../module_events/module_events.Cart/page.mdx#cartworkflowevents) +- [CustomerWorkflowEvents](../../module_events/module_events.Customer/page.mdx#customerworkflowevents) +- [FulfillmentWorkflowEvents](../../module_events/module_events.Fulfillment/page.mdx#fulfillmentworkflowevents) +- [InviteWorkflowEvents](../../module_events/module_events.User/page.mdx#inviteworkflowevents) +- [OrderEditWorkflowEvents](../../module_events/module_events.Order/page.mdx#ordereditworkflowevents) +- [OrderWorkflowEvents](../../module_events/module_events.Order/page.mdx#orderworkflowevents) +- [ProductCategoryWorkflowEvents](../../module_events/module_events.Product/page.mdx#productcategoryworkflowevents) +- [ProductCollectionWorkflowEvents](../../module_events/module_events.Product/page.mdx#productcollectionworkflowevents) +- [ProductOptionWorkflowEvents](../../module_events/module_events.Product/page.mdx#productoptionworkflowevents) +- [ProductTagWorkflowEvents](../../module_events/module_events.Product/page.mdx#producttagworkflowevents) +- [ProductTypeWorkflowEvents](../../module_events/module_events.Product/page.mdx#producttypeworkflowevents) +- [ProductVariantWorkflowEvents](../../module_events/module_events.Product/page.mdx#productvariantworkflowevents) +- [ProductWorkflowEvents](../../module_events/module_events.Product/page.mdx#productworkflowevents) +- [RegionWorkflowEvents](../../module_events/module_events.Region/page.mdx#regionworkflowevents) +- [SalesChannelWorkflowEvents](../../module_events/module_events.Sales_Channel/page.mdx#saleschannelworkflowevents) +- [UserWorkflowEvents](../../module_events/module_events.User/page.mdx#userworkflowevents) + +## Auth + +### AuthWorkflowEvents + + `Const` **AuthWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Cart + +### CartWorkflowEvents + + `Const` **CartWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Customer + +### CustomerWorkflowEvents + + `Const` **CustomerWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Fulfillment + +### FulfillmentWorkflowEvents + + `Const` **FulfillmentWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Invite + +### InviteWorkflowEvents + + `Const` **InviteWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Order + +### OrderWorkflowEvents + + `Const` **OrderWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Order Edit + +### OrderEditWorkflowEvents + + `Const` **OrderEditWorkflowEvents**: `Object` + +#### Properties + + + +___ + +___ + +## Product + +### ProductWorkflowEvents + + `Const` **ProductWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Product Category + +### ProductCategoryWorkflowEvents + + `Const` **ProductCategoryWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Product Collection + +### ProductCollectionWorkflowEvents + + `Const` **ProductCollectionWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Product Option + +### ProductOptionWorkflowEvents + + `Const` **ProductOptionWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Product Tag + +### ProductTagWorkflowEvents + + `Const` **ProductTagWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Product Type + +### ProductTypeWorkflowEvents + + `Const` **ProductTypeWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Product Variant + +### ProductVariantWorkflowEvents + + `Const` **ProductVariantWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Region + +### RegionWorkflowEvents + + `Const` **RegionWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## Sales Channel + +### SalesChannelWorkflowEvents + + `Const` **SalesChannelWorkflowEvents**: `Object` + +#### Properties + + + +___ + +## User + +### UserWorkflowEvents + + `Const` **UserWorkflowEvents**: `Object` + +#### Properties + + diff --git a/www/apps/resources/sidebars/auth.mjs b/www/apps/resources/sidebars/auth.mjs index 004835f259..0bc343f9d1 100644 --- a/www/apps/resources/sidebars/auth.mjs +++ b/www/apps/resources/sidebars/auth.mjs @@ -157,7 +157,7 @@ export const authSidebar = [ }, { type: "link", - path: "/commerce-modules/auth/events", + path: "/references/auth/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/cart.mjs b/www/apps/resources/sidebars/cart.mjs index 4e0b80948f..861102f89e 100644 --- a/www/apps/resources/sidebars/cart.mjs +++ b/www/apps/resources/sidebars/cart.mjs @@ -138,7 +138,7 @@ export const cartSidebar = [ }, { type: "link", - path: "/commerce-modules/cart/events", + path: "/references/cart/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/customer.mjs b/www/apps/resources/sidebars/customer.mjs index e38d0c33c6..4a924e0e55 100644 --- a/www/apps/resources/sidebars/customer.mjs +++ b/www/apps/resources/sidebars/customer.mjs @@ -128,7 +128,7 @@ export const customerSidebar = [ }, { type: "link", - path: "/commerce-modules/customer/events", + path: "/references/customer/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/fulfillment.mjs b/www/apps/resources/sidebars/fulfillment.mjs index eef7e338c1..d3c83fd57e 100644 --- a/www/apps/resources/sidebars/fulfillment.mjs +++ b/www/apps/resources/sidebars/fulfillment.mjs @@ -155,7 +155,7 @@ export const fulfillmentSidebar = [ }, { type: "link", - path: "/commerce-modules/fulfillment/events", + path: "/references/fulfillment/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/order-module.mjs b/www/apps/resources/sidebars/order-module.mjs index cdb97c6af9..d70bb57d23 100644 --- a/www/apps/resources/sidebars/order-module.mjs +++ b/www/apps/resources/sidebars/order-module.mjs @@ -166,7 +166,7 @@ export const orderSidebar = [ }, { type: "link", - path: "/commerce-modules/order/events", + path: "/references/order/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/payment.mjs b/www/apps/resources/sidebars/payment.mjs index b53e484ca4..ce3fa287d3 100644 --- a/www/apps/resources/sidebars/payment.mjs +++ b/www/apps/resources/sidebars/payment.mjs @@ -174,7 +174,7 @@ export const paymentSidebar = [ }, { type: "link", - path: "/commerce-modules/payment/events", + path: "/references/payment/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/product.mjs b/www/apps/resources/sidebars/product.mjs index 04ecbb06a9..0cf262ae2e 100644 --- a/www/apps/resources/sidebars/product.mjs +++ b/www/apps/resources/sidebars/product.mjs @@ -150,7 +150,7 @@ export const productSidebar = [ }, { type: "link", - path: "/commerce-modules/product/events", + path: "/references/product/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/references.mjs b/www/apps/resources/sidebars/references.mjs index 36ef666af2..30315f5378 100644 --- a/www/apps/resources/sidebars/references.mjs +++ b/www/apps/resources/sidebars/references.mjs @@ -106,7 +106,7 @@ export const referencesSidebar = [ }, { type: "link", - path: "/events-reference", + path: "/references/events", title: "Events", description: "List of events emitted by Medusa's Commerce Modules.", }, diff --git a/www/apps/resources/sidebars/region.mjs b/www/apps/resources/sidebars/region.mjs index 11608c9c57..d087844642 100644 --- a/www/apps/resources/sidebars/region.mjs +++ b/www/apps/resources/sidebars/region.mjs @@ -116,7 +116,7 @@ export const regionSidebar = [ }, { type: "link", - path: "/commerce-modules/region/events", + path: "/references/region/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/sales-channel.mjs b/www/apps/resources/sidebars/sales-channel.mjs index 23d39a9dc9..5b3f3504ee 100644 --- a/www/apps/resources/sidebars/sales-channel.mjs +++ b/www/apps/resources/sidebars/sales-channel.mjs @@ -123,7 +123,7 @@ export const salesChannelSidebar = [ }, { type: "link", - path: "/commerce-modules/sales-channel/events", + path: "/references/sales-channel/events", title: "Events Reference", }, { diff --git a/www/apps/resources/sidebars/user.mjs b/www/apps/resources/sidebars/user.mjs index af76530bd9..60c9d65f08 100644 --- a/www/apps/resources/sidebars/user.mjs +++ b/www/apps/resources/sidebars/user.mjs @@ -117,7 +117,7 @@ export const userSidebar = [ }, { type: "link", - path: "/commerce-modules/user/events", + path: "/references/user/events", title: "Events Reference", }, { diff --git a/www/apps/resources/utils/get-sidebar-for-path.ts b/www/apps/resources/utils/get-sidebar-for-path.ts index 150de5d9a7..940efeea6b 100644 --- a/www/apps/resources/utils/get-sidebar-for-path.ts +++ b/www/apps/resources/utils/get-sidebar-for-path.ts @@ -116,7 +116,7 @@ const sidebarMappings: { "/medusa-workflows-reference", "/references/core-flows", "/references/data-model", - "/events-reference", + "/references/events", "/references/helper-steps", "/service-factory-reference", "/test-tools-reference", diff --git a/www/apps/ui/.content.eslintrc.mjs b/www/apps/ui/.content.eslintrc.mjs index e89fdc8c22..07e2e6627f 100644 --- a/www/apps/ui/.content.eslintrc.mjs +++ b/www/apps/ui/.content.eslintrc.mjs @@ -19,11 +19,7 @@ const compat = new FlatCompat({ export default [ { - ignores: [ - "**/references/**/*", - "**/events-reference/**/*", - "**/_events-table/**/*", - ], + ignores: ["**/references/**/*"], }, { plugins: { diff --git a/www/apps/user-guide/.content.eslintrc.mjs b/www/apps/user-guide/.content.eslintrc.mjs index 65f47a8b41..40e7abc55e 100644 --- a/www/apps/user-guide/.content.eslintrc.mjs +++ b/www/apps/user-guide/.content.eslintrc.mjs @@ -19,11 +19,7 @@ const compat = new FlatCompat({ export default [ { - ignores: [ - "**/references/**/*", - "**/events-reference/**/*", - "**/_events-table/**/*", - ], + ignores: ["**/references/**/*"], }, { plugins: { diff --git a/www/packages/docs-ui/src/constants.tsx b/www/packages/docs-ui/src/constants.tsx index 6b1e2d6e05..5c6f6c9e96 100644 --- a/www/packages/docs-ui/src/constants.tsx +++ b/www/packages/docs-ui/src/constants.tsx @@ -321,7 +321,7 @@ export const navDropdownItems: NavigationItem[] = [ { type: "link", title: "Events Reference", - link: "/resources/events-reference", + link: "/resources/references/events", }, { type: "link", diff --git a/www/utils/generated/typedoc-json-output/events.json b/www/utils/generated/typedoc-json-output/events.json new file mode 100644 index 0000000000..1831998c29 --- /dev/null +++ b/www/utils/generated/typedoc-json-output/events.json @@ -0,0 +1,4914 @@ +{ + "id": 0, + "name": "events", + "variant": "project", + "kind": 1, + "flags": {}, + "children": [ + { + "id": 1, + "name": "CartWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Cart" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 4, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L4" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 2, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 3, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a cart is created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 15, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L15" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.created\"" + }, + { + "id": 4, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a cart's details are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateLineItemInCartWorkflow, updateCartWorkflow, addToCartWorkflow, addShippingMethodToCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 26, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L26" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.updated\"" + }, + { + "id": 5, + "name": "CUSTOMER_UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when the customer in the cart is updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.customer_updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 37, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L37" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.customer_updated\"" + }, + { + "id": 6, + "name": "REGION_UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when the cart's region is updated. This\nevent is emitted alongside the " + }, + { + "kind": "inline-tag", + "tag": "@link", + "text": "CartWorkflowEvents.UPDATED", + "target": 4 + }, + { + "kind": "text", + "text": " event." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.region_updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 49, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L49" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.region_updated\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 3, + 4, + 5, + 6 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 4, + "character": 34, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L4" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 7, + "name": "CustomerWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Cart" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 55, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L55" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 8, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 9, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a customer is created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the customer\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "customer.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createCustomersWorkflow, createCustomerAccountWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 66, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L66" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"customer.created\"" + }, + { + "id": 10, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a customer is updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the customer\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "customer.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCustomersWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 77, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L77" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"customer.updated\"" + }, + { + "id": 11, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a customer is deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the customer\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "customer.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteCustomersWorkflow, removeCustomerAccountWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 88, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L88" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"customer.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 9, + 10, + 11 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 55, + "character": 38, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L55" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 12, + "name": "OrderWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Order" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 94, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L94" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 13, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 14, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when the details of an order or draft order is updated. This\ndoesn't include updates made by an edit." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateOrderWorkflow, updateDraftOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 106, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L106" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.updated\"" + }, + { + "id": 15, + "name": "PLACED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is placed, or when a draft order is converted to an\norder." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.placed" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "completeCartWorkflow, convertDraftOrderWorkflow, processPaymentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 119, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L119" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.placed\"" + }, + { + "id": 16, + "name": "CANCELED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is canceld." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.canceled" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "cancelOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 130, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L130" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.canceled\"" + }, + { + "id": 17, + "name": "COMPLETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when orders are completed." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the order\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.completed" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "completeOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 141, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L141" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.completed\"" + }, + { + "id": 18, + "name": "ARCHIVED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is archived." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the order\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.archived" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "archiveOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 152, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L152" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.archived\"" + }, + { + "id": 19, + "name": "FULFILLMENT_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a fulfillment is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n fulfillment_id, // The ID of the fulfillment\n no_notification, // Whether to notify the customer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.fulfillment_created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createOrderFulfillmentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 166, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L166" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.fulfillment_created\"" + }, + { + "id": 20, + "name": "FULFILLMENT_CANCELED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order's fulfillment is canceled." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n fulfillment_id, // The ID of the fulfillment\n no_notification, // Whether to notify the customer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.fulfillment_canceled" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "cancelOrderFulfillmentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 179, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L179" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.fulfillment_canceled\"" + }, + { + "id": 21, + "name": "RETURN_REQUESTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a return request is confirmed." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n return_id, // The ID of the return\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.return_requested" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createAndCompleteReturnOrderWorkflow, confirmReturnRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 192, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L192" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.return_requested\"" + }, + { + "id": 22, + "name": "RETURN_RECEIVED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a return is marked as received." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n return_id, // The ID of the return\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.return_received" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createAndCompleteReturnOrderWorkflow, confirmReturnReceiveWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 204, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L204" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.return_received\"" + }, + { + "id": 23, + "name": "CLAIM_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a claim is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n claim_id, // The ID of the claim\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.claim_created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "confirmClaimRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 217, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L217" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.claim_created\"" + }, + { + "id": 24, + "name": "EXCHANGE_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an exchange is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n exchange_id, // The ID of the exchange\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.exchange_created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "confirmExchangeRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 229, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L229" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.exchange_created\"" + }, + { + "id": 25, + "name": "TRANSFER_REQUESTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is requested to be transferred to\nanother customer." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n order_change_id, // The ID of the order change created for the transfer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.transfer_requested" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "requestOrderTransferWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 243, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L243" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.transfer_requested\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 94, + "character": 35, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L94" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 26, + "name": "OrderEditWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Order" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 249, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L249" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 27, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 28, + "name": "REQUESTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order edit is requested." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n actions, // The actions to edit the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order-edit.requested" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "requestOrderEditRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 261, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L261" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order-edit.requested\"" + }, + { + "id": 29, + "name": "CONFIRMED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order edit request is confirmed." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n actions, // The actions to edit the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order-edit.confirmed" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "confirmOrderEditRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 273, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L273" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order-edit.confirmed\"" + }, + { + "id": 30, + "name": "CANCELED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order edit request is canceled." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n actions, // The actions to edit the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order-edit.canceled" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "cancelBeginOrderEditWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 285, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L285" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order-edit.canceled\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 28, + 29, + 30 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 249, + "character": 39, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L249" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 31, + "name": "UserWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "User" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 291, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L291" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 32, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 33, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when users are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the user\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "user.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createUsersWorkflow, createUserAccountWorkflow, acceptInviteWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 302, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L302" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"user.created\"" + }, + { + "id": 34, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when users are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the user\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "user.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateUsersWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 313, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L313" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"user.updated\"" + }, + { + "id": 35, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when users are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the user\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "user.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteUsersWorkflow, removeUserAccountWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 324, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L324" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"user.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 33, + 34, + 35 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 291, + "character": 34, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L291" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 36, + "name": "AuthWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Auth" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 330, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L330" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 37, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 38, + "name": "PASSWORD_RESET", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a reset password token is generated. You can listen to this event\nto send a reset password email to the user or customer, for example." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n entity_id, // The identifier of the user or customer. For example, an email address.\n actor_type, // The type of actor. For example, \"customer\", \"user\", or custom.\n token, // The generated token.\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "auth.password_reset" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "generateResetPasswordTokenWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 344, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L344" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"auth.password_reset\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 38 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 330, + "character": 34, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L330" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 39, + "name": "SalesChannelWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Sales Channel" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 350, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L350" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 40, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 41, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when sales channels are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the sales channel\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "sales-channel.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createSalesChannelsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 361, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L361" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"sales-channel.created\"" + }, + { + "id": 42, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when sales channels are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the sales channel\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "sales-channel.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateSalesChannelsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 372, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L372" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"sales-channel.updated\"" + }, + { + "id": 43, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when sales channels are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the sales channel\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "sales-channel.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteSalesChannelsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 383, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L383" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"sales-channel.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 41, + 42, + 43 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 350, + "character": 42, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L350" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 44, + "name": "ProductCategoryWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 389, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L389" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 45, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 46, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product categories are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product category\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-category.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductCategoriesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 400, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L400" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-category.created\"" + }, + { + "id": 47, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product categories are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product category\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-category.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductCategoriesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 411, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L411" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-category.updated\"" + }, + { + "id": 48, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product categories are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product category\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-category.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductCategoriesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 422, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L422" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-category.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 46, + 47, + 48 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 389, + "character": 45, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L389" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 49, + "name": "ProductCollectionWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 428, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L428" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 50, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 51, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product collections are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product collection\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-collection.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createCollectionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 439, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L439" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-collection.created\"" + }, + { + "id": 52, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product collections are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product collection\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-collection.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCollectionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 450, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L450" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-collection.updated\"" + }, + { + "id": 53, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product collections are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product collection\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-collection.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteCollectionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 461, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L461" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-collection.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 51, + 52, + 53 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 428, + "character": 47, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L428" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 54, + "name": "ProductVariantWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 467, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L467" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 55, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 56, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product variants are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product variant\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-variant.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductVariantsWorkflow, batchProductVariantsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 478, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L478" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-variant.updated\"" + }, + { + "id": 57, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product variants are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product variant\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-variant.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductVariantsWorkflow, createProductsWorkflow, batchProductVariantsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 489, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L489" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-variant.created\"" + }, + { + "id": 58, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product variants are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product variant\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-variant.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductVariantsWorkflow, batchProductVariantsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 500, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L500" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-variant.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 56, + 57, + 58 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 467, + "character": 44, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L467" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 59, + "name": "ProductWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 506, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L506" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 60, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 61, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when products are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 517, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L517" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product.updated\"" + }, + { + "id": 62, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when products are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 528, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L528" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product.created\"" + }, + { + "id": 63, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when products are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 539, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L539" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 61, + 62, + 63 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 506, + "character": 37, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L506" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 64, + "name": "ProductTypeWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 545, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L545" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 65, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 66, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product types are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product type\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-type.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductTypesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 556, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L556" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-type.updated\"" + }, + { + "id": 67, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product types are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product type\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-type.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductTypesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 567, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L567" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-type.created\"" + }, + { + "id": 68, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product types are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product type\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-type.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductTypesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 578, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L578" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-type.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 66, + 67, + 68 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 545, + "character": 41, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L545" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 69, + "name": "ProductTagWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 584, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L584" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 70, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 71, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product tags are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product tag\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-tag.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductTagsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 595, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L595" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-tag.updated\"" + }, + { + "id": 72, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product tags are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product tag\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-tag.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductTagsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 606, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L606" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-tag.created\"" + }, + { + "id": 73, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product tags are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product tag\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-tag.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductTagsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 617, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L617" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-tag.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 71, + 72, + 73 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 584, + "character": 40, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L584" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 74, + "name": "ProductOptionWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 623, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L623" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 75, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 76, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product options are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product option\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-option.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductOptionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 634, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L634" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-option.updated\"" + }, + { + "id": 77, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product options are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product option\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-option.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductOptionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 645, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L645" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-option.created\"" + }, + { + "id": 78, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product options are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product option\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-option.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductOptionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 656, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L656" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-option.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 76, + 77, + 78 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 623, + "character": 43, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L623" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 79, + "name": "InviteWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "User" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 662, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L662" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 80, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 81, + "name": "ACCEPTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an invite is accepted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the invite\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.accepted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "acceptInviteWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 673, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L673" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.accepted\"" + }, + { + "id": 82, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when invites are created. You can listen to this event\nto send an email to the invited users, for example." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the invite\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createInvitesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 685, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L685" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.created\"" + }, + { + "id": 83, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when invites are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the invite\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteInvitesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 696, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L696" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.deleted\"" + }, + { + "id": 84, + "name": "RESENT", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when invites should be resent because their token was\nrefreshed. You can listen to this event to send an email to the invited users,\nfor example." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the invite\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.resent" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "refreshInviteTokensWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 709, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L709" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.resent\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 81, + 82, + 83, + 84 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 662, + "character": 36, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L662" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 85, + "name": "RegionWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Region" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 715, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L715" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 86, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 87, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when regions are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the region\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "region.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateRegionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 726, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L726" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"region.updated\"" + }, + { + "id": 88, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when regions are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the region\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "region.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createRegionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 737, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L737" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"region.created\"" + }, + { + "id": 89, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when regions are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the region\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "region.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteRegionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 748, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L748" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"region.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 87, + 88, + 89 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 715, + "character": 36, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L715" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 90, + "name": "FulfillmentWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Fulfillment" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 754, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L754" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 91, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 92, + "name": "SHIPMENT_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a shipment is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // the ID of the shipment\n no_notification, // whether to notify the customer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "shipment.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createOrderShipmentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 766, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L766" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"shipment.created\"" + }, + { + "id": 93, + "name": "DELIVERY_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a fulfillment is marked as delivered." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // the ID of the fulfillment\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "delivery.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "markOrderFulfillmentAsDeliveredWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 777, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L777" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"delivery.created\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 92, + 93 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 754, + "character": 41, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L754" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 1, + 7, + 12, + 26, + 31, + 36, + 39, + 44, + 49, + 54, + 59, + 64, + 69, + 74, + 79, + 85, + 90 + ] + } + ], + "categories": [ + { + "title": "Auth", + "children": [ + 36 + ] + }, + { + "title": "Cart", + "children": [ + 1, + 7 + ] + }, + { + "title": "Fulfillment", + "children": [ + 90 + ] + }, + { + "title": "Order", + "children": [ + 12, + 26 + ] + }, + { + "title": "Product", + "children": [ + 44, + 49, + 54, + 59, + 64, + 69, + 74 + ] + }, + { + "title": "Region", + "children": [ + 85 + ] + }, + { + "title": "Sales Channel", + "children": [ + 39 + ] + }, + { + "title": "User", + "children": [ + 31, + 79 + ] + } + ], + "packageName": "@medusajs/utils", + "symbolIdMap": { + "0": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "" + }, + "1": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "CartWorkflowEvents" + }, + "2": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "3": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "4": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "5": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CUSTOMER_UPDATED" + }, + "6": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.REGION_UPDATED" + }, + "7": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "CustomerWorkflowEvents" + }, + "8": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "9": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "10": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "11": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "12": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "OrderWorkflowEvents" + }, + "13": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "14": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "15": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.PLACED" + }, + "16": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CANCELED" + }, + "17": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.COMPLETED" + }, + "18": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.ARCHIVED" + }, + "19": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.FULFILLMENT_CREATED" + }, + "20": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.FULFILLMENT_CANCELED" + }, + "21": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.RETURN_REQUESTED" + }, + "22": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.RETURN_RECEIVED" + }, + "23": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CLAIM_CREATED" + }, + "24": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.EXCHANGE_CREATED" + }, + "25": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.TRANSFER_REQUESTED" + }, + "26": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "OrderEditWorkflowEvents" + }, + "27": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "28": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.REQUESTED" + }, + "29": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CONFIRMED" + }, + "30": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CANCELED" + }, + "31": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "UserWorkflowEvents" + }, + "32": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "33": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "34": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "35": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "36": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "AuthWorkflowEvents" + }, + "37": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "38": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.PASSWORD_RESET" + }, + "39": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "SalesChannelWorkflowEvents" + }, + "40": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "41": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "42": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "43": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "44": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductCategoryWorkflowEvents" + }, + "45": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "46": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "47": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "48": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "49": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductCollectionWorkflowEvents" + }, + "50": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "51": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "52": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "53": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "54": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductVariantWorkflowEvents" + }, + "55": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "56": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "57": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "58": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "59": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductWorkflowEvents" + }, + "60": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "61": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "62": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "63": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "64": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductTypeWorkflowEvents" + }, + "65": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "66": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "67": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "68": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "69": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductTagWorkflowEvents" + }, + "70": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "71": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "72": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "73": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "74": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductOptionWorkflowEvents" + }, + "75": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "76": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "77": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "78": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "79": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "InviteWorkflowEvents" + }, + "80": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "81": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.ACCEPTED" + }, + "82": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "83": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "84": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.RESENT" + }, + "85": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "RegionWorkflowEvents" + }, + "86": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "87": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "88": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "89": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "90": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "FulfillmentWorkflowEvents" + }, + "91": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "92": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.SHIPMENT_CREATED" + }, + "93": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELIVERY_CREATED" + } + }, + "files": { + "entries": { + "1": "../../../../packages/core/utils/src/core-flows/events.ts" + }, + "reflections": { + "1": 0 + } + } +} diff --git a/www/utils/generated/typedoc-json-output/module-events.json b/www/utils/generated/typedoc-json-output/module-events.json new file mode 100644 index 0000000000..d23943c72c --- /dev/null +++ b/www/utils/generated/typedoc-json-output/module-events.json @@ -0,0 +1,5268 @@ +{ + "id": 0, + "name": "module-events", + "variant": "project", + "kind": 1, + "flags": {}, + "children": [ + { + "id": 94, + "name": "Cart", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 1, + "name": "CartWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Cart" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 5, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L5" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 2, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 3, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a cart is created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 16, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L16" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.created\"" + }, + { + "id": 4, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a cart's details are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateLineItemInCartWorkflow, updateCartWorkflow, addToCartWorkflow, addShippingMethodToCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 27, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L27" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.updated\"" + }, + { + "id": 5, + "name": "CUSTOMER_UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when the customer in the cart is updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.customer_updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 38, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L38" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.customer_updated\"" + }, + { + "id": 6, + "name": "REGION_UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when the cart's region is updated. This\nevent is emitted alongside the " + }, + { + "kind": "inline-tag", + "tag": "@link", + "text": "CartWorkflowEvents.UPDATED", + "target": 4 + }, + { + "kind": "text", + "text": " event." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the cart\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "cart.region_updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCartWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 50, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L50" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"cart.region_updated\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 3, + 4, + 5, + 6 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 5, + "character": 34, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L5" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 1 + ] + } + ], + "categories": [ + { + "title": "Cart", + "children": [ + 1 + ] + } + ] + }, + { + "id": 95, + "name": "Customer", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 7, + "name": "CustomerWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Customer" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 57, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L57" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 8, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 9, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a customer is created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the customer\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "customer.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createCustomersWorkflow, createCustomerAccountWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 68, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L68" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"customer.created\"" + }, + { + "id": 10, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a customer is updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the customer\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "customer.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCustomersWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 79, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L79" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"customer.updated\"" + }, + { + "id": 11, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a customer is deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the customer\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "customer.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteCustomersWorkflow, removeCustomerAccountWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 90, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L90" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"customer.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 9, + 10, + 11 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 57, + "character": 38, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L57" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 7 + ] + } + ], + "categories": [ + { + "title": "Customer", + "children": [ + 7 + ] + } + ] + }, + { + "id": 96, + "name": "Order", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 12, + "name": "OrderWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Order" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 97, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L97" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 13, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 14, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when the details of an order or draft order is updated. This\ndoesn't include updates made by an edit." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateOrderWorkflow, updateDraftOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 109, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L109" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.updated\"" + }, + { + "id": 15, + "name": "PLACED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is placed, or when a draft order is converted to an\norder." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.placed" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "completeCartWorkflow, convertDraftOrderWorkflow, processPaymentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 122, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L122" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.placed\"" + }, + { + "id": 16, + "name": "CANCELED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is canceld." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.canceled" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "cancelOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 133, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L133" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.canceled\"" + }, + { + "id": 17, + "name": "COMPLETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when orders are completed." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the order\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.completed" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "completeOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 144, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L144" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.completed\"" + }, + { + "id": 18, + "name": "ARCHIVED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is archived." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the order\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.archived" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "archiveOrderWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 155, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L155" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.archived\"" + }, + { + "id": 19, + "name": "FULFILLMENT_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a fulfillment is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n fulfillment_id, // The ID of the fulfillment\n no_notification, // Whether to notify the customer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.fulfillment_created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createOrderFulfillmentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 169, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L169" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.fulfillment_created\"" + }, + { + "id": 20, + "name": "FULFILLMENT_CANCELED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order's fulfillment is canceled." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n fulfillment_id, // The ID of the fulfillment\n no_notification, // Whether to notify the customer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.fulfillment_canceled" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "cancelOrderFulfillmentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 182, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L182" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.fulfillment_canceled\"" + }, + { + "id": 21, + "name": "RETURN_REQUESTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a return request is confirmed." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n return_id, // The ID of the return\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.return_requested" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createAndCompleteReturnOrderWorkflow, confirmReturnRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 195, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L195" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.return_requested\"" + }, + { + "id": 22, + "name": "RETURN_RECEIVED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a return is marked as received." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n return_id, // The ID of the return\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.return_received" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createAndCompleteReturnOrderWorkflow, confirmReturnReceiveWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 207, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L207" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.return_received\"" + }, + { + "id": 23, + "name": "CLAIM_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a claim is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n claim_id, // The ID of the claim\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.claim_created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "confirmClaimRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 220, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L220" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.claim_created\"" + }, + { + "id": 24, + "name": "EXCHANGE_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an exchange is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n exchange_id, // The ID of the exchange\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.exchange_created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "confirmExchangeRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 232, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L232" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.exchange_created\"" + }, + { + "id": 25, + "name": "TRANSFER_REQUESTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order is requested to be transferred to\nanother customer." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the order\n order_change_id, // The ID of the order change created for the transfer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order.transfer_requested" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "requestOrderTransferWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 246, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L246" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order.transfer_requested\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 97, + "character": 35, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L97" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 26, + "name": "OrderEditWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Order Edit" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 253, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L253" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 27, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 28, + "name": "REQUESTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order edit is requested." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n actions, // The actions to edit the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order-edit.requested" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "requestOrderEditRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 265, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L265" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order-edit.requested\"" + }, + { + "id": 29, + "name": "CONFIRMED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order edit request is confirmed." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n actions, // The actions to edit the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order-edit.confirmed" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "confirmOrderEditRequestWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 277, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L277" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order-edit.confirmed\"" + }, + { + "id": 30, + "name": "CANCELED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an order edit request is canceled." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n order_id, // The ID of the order\n actions, // The actions to edit the order\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "order-edit.canceled" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "cancelBeginOrderEditWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 289, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L289" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"order-edit.canceled\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 28, + 29, + 30 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 253, + "character": 39, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L253" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 12, + 26 + ] + } + ], + "categories": [ + { + "title": "Order", + "children": [ + 12 + ] + }, + { + "title": "Order Edit", + "children": [ + 26 + ] + } + ] + }, + { + "id": 97, + "name": "User", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 31, + "name": "UserWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "User" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 296, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L296" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 32, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 33, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when users are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the user\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "user.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createUsersWorkflow, createUserAccountWorkflow, acceptInviteWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 307, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L307" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"user.created\"" + }, + { + "id": 34, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when users are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the user\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "user.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateUsersWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 318, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L318" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"user.updated\"" + }, + { + "id": 35, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when users are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the user\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "user.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteUsersWorkflow, removeUserAccountWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 329, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L329" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"user.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 33, + 34, + 35 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 296, + "character": 34, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L296" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 79, + "name": "InviteWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Invite" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 677, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L677" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 80, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 81, + "name": "ACCEPTED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when an invite is accepted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // The ID of the invite\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.accepted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "acceptInviteWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 688, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L688" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.accepted\"" + }, + { + "id": 82, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when invites are created. You can listen to this event\nto send an email to the invited users, for example." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the invite\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createInvitesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 700, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L700" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.created\"" + }, + { + "id": 83, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when invites are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the invite\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteInvitesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 711, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L711" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.deleted\"" + }, + { + "id": 84, + "name": "RESENT", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when invites should be resent because their token was\nrefreshed. You can listen to this event to send an email to the invited users,\nfor example." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the invite\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "invite.resent" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "refreshInviteTokensWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 724, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L724" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"invite.resent\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 81, + 82, + 83, + 84 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 677, + "character": 36, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L677" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 31, + 79 + ] + } + ], + "categories": [ + { + "title": "User", + "children": [ + 31 + ] + }, + { + "title": "Invite", + "children": [ + 79 + ] + } + ] + }, + { + "id": 98, + "name": "Auth", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 36, + "name": "AuthWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Auth" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 336, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L336" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 37, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 38, + "name": "PASSWORD_RESET", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a reset password token is generated. You can listen to this event\nto send a reset password email to the user or customer, for example." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n entity_id, // The identifier of the user or customer. For example, an email address.\n actor_type, // The type of actor. For example, \"customer\", \"user\", or custom.\n token, // The generated token.\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "auth.password_reset" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "generateResetPasswordTokenWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 350, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L350" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"auth.password_reset\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 38 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 336, + "character": 34, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L336" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 36 + ] + } + ], + "categories": [ + { + "title": "Auth", + "children": [ + 36 + ] + } + ] + }, + { + "id": 99, + "name": "Sales Channel", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 39, + "name": "SalesChannelWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Sales Channel" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 357, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L357" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 40, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 41, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when sales channels are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the sales channel\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "sales-channel.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createSalesChannelsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 368, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L368" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"sales-channel.created\"" + }, + { + "id": 42, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when sales channels are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the sales channel\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "sales-channel.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateSalesChannelsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 379, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L379" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"sales-channel.updated\"" + }, + { + "id": 43, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when sales channels are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the sales channel\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "sales-channel.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteSalesChannelsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 390, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L390" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"sales-channel.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 41, + 42, + 43 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 357, + "character": 42, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L357" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 39 + ] + } + ], + "categories": [ + { + "title": "Sales Channel", + "children": [ + 39 + ] + } + ] + }, + { + "id": 100, + "name": "Product", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 44, + "name": "ProductCategoryWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product Category" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 397, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L397" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 45, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 46, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product categories are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product category\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-category.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductCategoriesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 408, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L408" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-category.created\"" + }, + { + "id": 47, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product categories are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product category\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-category.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductCategoriesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 419, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L419" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-category.updated\"" + }, + { + "id": 48, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product categories are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product category\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-category.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductCategoriesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 430, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L430" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-category.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 46, + 47, + 48 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 397, + "character": 45, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L397" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 49, + "name": "ProductCollectionWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product Collection" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 437, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L437" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 50, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 51, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product collections are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product collection\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-collection.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createCollectionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 448, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L448" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-collection.created\"" + }, + { + "id": 52, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product collections are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product collection\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-collection.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateCollectionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 459, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L459" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-collection.updated\"" + }, + { + "id": 53, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product collections are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product collection\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-collection.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteCollectionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 470, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L470" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-collection.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 51, + 52, + 53 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 437, + "character": 47, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L437" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 54, + "name": "ProductVariantWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product Variant" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 477, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L477" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 55, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 56, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product variants are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product variant\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-variant.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductVariantsWorkflow, batchProductVariantsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 488, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L488" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-variant.updated\"" + }, + { + "id": 57, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product variants are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product variant\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-variant.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductVariantsWorkflow, createProductsWorkflow, batchProductVariantsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 499, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L499" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-variant.created\"" + }, + { + "id": 58, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product variants are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product variant\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-variant.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductVariantsWorkflow, batchProductVariantsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 510, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L510" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-variant.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 56, + 57, + 58 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 477, + "character": 44, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L477" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 59, + "name": "ProductWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 517, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L517" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 60, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 61, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when products are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 528, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L528" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product.updated\"" + }, + { + "id": 62, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when products are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 539, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L539" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product.created\"" + }, + { + "id": 63, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when products are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductsWorkflow, batchProductsWorkflow, importProductsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 550, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L550" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 61, + 62, + 63 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 517, + "character": 37, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L517" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 64, + "name": "ProductTypeWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product Type" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 557, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L557" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 65, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 66, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product types are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product type\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-type.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductTypesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 568, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L568" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-type.updated\"" + }, + { + "id": 67, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product types are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product type\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-type.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductTypesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 579, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L579" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-type.created\"" + }, + { + "id": 68, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product types are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product type\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-type.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductTypesWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 590, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L590" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-type.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 66, + 67, + 68 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 557, + "character": 41, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L557" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 69, + "name": "ProductTagWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product Tag" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 597, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L597" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 70, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 71, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product tags are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product tag\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-tag.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductTagsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 608, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L608" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-tag.updated\"" + }, + { + "id": 72, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product tags are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product tag\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-tag.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductTagsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 619, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L619" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-tag.created\"" + }, + { + "id": 73, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product tags are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product tag\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-tag.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductTagsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 630, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L630" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-tag.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 71, + 72, + 73 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 597, + "character": 40, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L597" + } + ] + } + }, + "defaultValue": "..." + }, + { + "id": 74, + "name": "ProductOptionWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Product Option" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 637, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L637" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 75, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 76, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product options are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product option\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-option.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateProductOptionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 648, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L648" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-option.updated\"" + }, + { + "id": 77, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product options are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product option\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-option.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createProductOptionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 659, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L659" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-option.created\"" + }, + { + "id": 78, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when product options are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the product option\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "product-option.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteProductOptionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 670, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L670" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"product-option.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 76, + 77, + 78 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 637, + "character": 43, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L637" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 44, + 49, + 54, + 59, + 64, + 69, + 74 + ] + } + ], + "categories": [ + { + "title": "Product Category", + "children": [ + 44 + ] + }, + { + "title": "Product Collection", + "children": [ + 49 + ] + }, + { + "title": "Product Variant", + "children": [ + 54 + ] + }, + { + "title": "Product", + "children": [ + 59 + ] + }, + { + "title": "Product Type", + "children": [ + 64 + ] + }, + { + "title": "Product Tag", + "children": [ + 69 + ] + }, + { + "title": "Product Option", + "children": [ + 74 + ] + } + ] + }, + { + "id": 101, + "name": "Region", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 85, + "name": "RegionWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Region" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 731, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L731" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 86, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 87, + "name": "UPDATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when regions are updated." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the region\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "region.updated" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "updateRegionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 742, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L742" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"region.updated\"" + }, + { + "id": 88, + "name": "CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when regions are created." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the region\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "region.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createRegionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 753, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L753" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"region.created\"" + }, + { + "id": 89, + "name": "DELETED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when regions are deleted." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n[{\n id, // The ID of the region\n}]\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "region.deleted" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "deleteRegionsWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 764, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L764" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"region.deleted\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 87, + 88, + 89 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 731, + "character": 36, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L731" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 85 + ] + } + ], + "categories": [ + { + "title": "Region", + "children": [ + 85 + ] + } + ] + }, + { + "id": 102, + "name": "Fulfillment", + "variant": "declaration", + "kind": 4, + "flags": {}, + "children": [ + { + "id": 90, + "name": "FulfillmentWorkflowEvents", + "variant": "declaration", + "kind": 32, + "flags": { + "isConst": true + }, + "comment": { + "summary": [], + "blockTags": [ + { + "tag": "@category", + "content": [ + { + "kind": "text", + "text": "Fulfillment" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 771, + "character": 13, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L771" + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 91, + "name": "__type", + "variant": "declaration", + "kind": 65536, + "flags": {}, + "children": [ + { + "id": 92, + "name": "SHIPMENT_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a shipment is created for an order." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // the ID of the shipment\n no_notification, // whether to notify the customer\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "shipment.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "createOrderShipmentWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 783, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L783" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"shipment.created\"" + }, + { + "id": 93, + "name": "DELIVERY_CREATED", + "variant": "declaration", + "kind": 1024, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Emitted when a fulfillment is marked as delivered." + } + ], + "blockTags": [ + { + "tag": "@eventPayload", + "content": [ + { + "kind": "code", + "text": "```ts\n{\n id, // the ID of the fulfillment\n}\n```" + } + ] + }, + { + "tag": "@eventName", + "content": [ + { + "kind": "code", + "text": "delivery.created" + } + ] + }, + { + "tag": "@workflows", + "content": [ + { + "kind": "text", + "text": "markOrderFulfillmentAsDeliveredWorkflow" + } + ] + } + ] + }, + "sources": [ + { + "fileName": "events.ts", + "line": 794, + "character": 2, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L794" + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"delivery.created\"" + } + ], + "groups": [ + { + "title": "Properties", + "children": [ + 92, + 93 + ] + } + ], + "sources": [ + { + "fileName": "events.ts", + "line": 771, + "character": 41, + "url": "https://github.com/medusajs/medusa/blob/91f5ac91a9c14b0fdc7caf5830c6b54f94889414/packages/core/utils/src/core-flows/events.ts#L771" + } + ] + } + }, + "defaultValue": "..." + } + ], + "groups": [ + { + "title": "Variables", + "children": [ + 90 + ] + } + ], + "categories": [ + { + "title": "Fulfillment", + "children": [ + 90 + ] + } + ] + } + ], + "groups": [ + { + "title": "Namespaces", + "children": [ + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102 + ] + }, + { + "title": "Variables", + "children": [ + 1, + 7, + 12, + 26, + 31, + 36, + 39, + 44, + 49, + 54, + 59, + 64, + 69, + 74, + 79, + 85, + 90 + ] + } + ], + "categories": [ + { + "title": "Auth", + "children": [ + 36 + ] + }, + { + "title": "Cart", + "children": [ + 1 + ] + }, + { + "title": "Customer", + "children": [ + 7 + ] + }, + { + "title": "Fulfillment", + "children": [ + 90 + ] + }, + { + "title": "Invite", + "children": [ + 79 + ] + }, + { + "title": "Order", + "children": [ + 12 + ] + }, + { + "title": "Order Edit", + "children": [ + 26 + ] + }, + { + "title": "Other", + "children": [ + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102 + ] + }, + { + "title": "Product", + "children": [ + 59 + ] + }, + { + "title": "Product Category", + "children": [ + 44 + ] + }, + { + "title": "Product Collection", + "children": [ + 49 + ] + }, + { + "title": "Product Option", + "children": [ + 74 + ] + }, + { + "title": "Product Tag", + "children": [ + 69 + ] + }, + { + "title": "Product Type", + "children": [ + 64 + ] + }, + { + "title": "Product Variant", + "children": [ + 54 + ] + }, + { + "title": "Region", + "children": [ + 85 + ] + }, + { + "title": "Sales Channel", + "children": [ + 39 + ] + }, + { + "title": "User", + "children": [ + 31 + ] + } + ], + "packageName": "@medusajs/utils", + "symbolIdMap": { + "0": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "" + }, + "1": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "CartWorkflowEvents" + }, + "2": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "3": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "4": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "5": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CUSTOMER_UPDATED" + }, + "6": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.REGION_UPDATED" + }, + "7": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "CustomerWorkflowEvents" + }, + "8": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "9": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "10": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "11": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "12": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "OrderWorkflowEvents" + }, + "13": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "14": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "15": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.PLACED" + }, + "16": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CANCELED" + }, + "17": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.COMPLETED" + }, + "18": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.ARCHIVED" + }, + "19": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.FULFILLMENT_CREATED" + }, + "20": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.FULFILLMENT_CANCELED" + }, + "21": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.RETURN_REQUESTED" + }, + "22": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.RETURN_RECEIVED" + }, + "23": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CLAIM_CREATED" + }, + "24": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.EXCHANGE_CREATED" + }, + "25": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.TRANSFER_REQUESTED" + }, + "26": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "OrderEditWorkflowEvents" + }, + "27": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "28": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.REQUESTED" + }, + "29": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CONFIRMED" + }, + "30": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CANCELED" + }, + "31": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "UserWorkflowEvents" + }, + "32": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "33": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "34": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "35": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "36": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "AuthWorkflowEvents" + }, + "37": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "38": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.PASSWORD_RESET" + }, + "39": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "SalesChannelWorkflowEvents" + }, + "40": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "41": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "42": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "43": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "44": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductCategoryWorkflowEvents" + }, + "45": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "46": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "47": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "48": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "49": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductCollectionWorkflowEvents" + }, + "50": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "51": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "52": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "53": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "54": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductVariantWorkflowEvents" + }, + "55": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "56": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "57": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "58": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "59": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductWorkflowEvents" + }, + "60": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "61": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "62": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "63": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "64": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductTypeWorkflowEvents" + }, + "65": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "66": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "67": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "68": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "69": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductTagWorkflowEvents" + }, + "70": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "71": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "72": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "73": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "74": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "ProductOptionWorkflowEvents" + }, + "75": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "76": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "77": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "78": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "79": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "InviteWorkflowEvents" + }, + "80": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "81": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.ACCEPTED" + }, + "82": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "83": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "84": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.RESENT" + }, + "85": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "RegionWorkflowEvents" + }, + "86": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "87": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.UPDATED" + }, + "88": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.CREATED" + }, + "89": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELETED" + }, + "90": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "FulfillmentWorkflowEvents" + }, + "91": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object" + }, + "92": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.SHIPMENT_CREATED" + }, + "93": { + "sourceFileName": "../../../../packages/core/utils/src/core-flows/events.ts", + "qualifiedName": "__object.DELIVERY_CREATED" + } + }, + "files": { + "entries": { + "1": "../../../../packages/core/utils/src/core-flows/events.ts" + }, + "reflections": { + "1": 0 + } + } +} diff --git a/www/utils/packages/docs-generator/src/classes/kinds/events.ts b/www/utils/packages/docs-generator/src/classes/kinds/events.ts index 0795e88543..5071200cf1 100644 --- a/www/utils/packages/docs-generator/src/classes/kinds/events.ts +++ b/www/utils/packages/docs-generator/src/classes/kinds/events.ts @@ -3,6 +3,7 @@ import DefaultKindGenerator, { GetDocBlockOptions } from "./default.js" import { glob } from "glob" import getMonorepoRoot from "../../utils/get-monorepo-root.js" import { readFile } from "fs/promises" +import { MedusaEvent } from "types" class EventsKindGenerator extends DefaultKindGenerator { protected allowedKinds: ts.SyntaxKind[] = [ts.SyntaxKind.VariableDeclaration] @@ -33,17 +34,7 @@ class EventsKindGenerator extends DefaultKindGenerator { const properties = (node.initializer as ts.ObjectLiteralExpression) .properties - const events: { - name: string - parentName: string - propertyName: string - payload: string - description?: string - workflows: string[] - version?: string - deprecated?: boolean - deprecated_message?: string - }[] = properties + const events: MedusaEvent[] = properties .filter((property) => ts.isPropertyAssignment(property)) .map((property) => { const propertyAssignment = property as ts.PropertyAssignment diff --git a/www/utils/packages/typedoc-config/tsdoc.json b/www/utils/packages/typedoc-config/tsdoc.json index d86c97f071..4bf7c2edae 100644 --- a/www/utils/packages/typedoc-config/tsdoc.json +++ b/www/utils/packages/typedoc-config/tsdoc.json @@ -57,6 +57,18 @@ { "tagName": "@tags", "syntaxKind": "block" + }, + { + "tagName": "@eventName", + "syntaxKind": "block" + }, + { + "tagName": "@workflows", + "syntaxKind": "block" + }, + { + "tagName": "@eventPayload", + "syntaxKind": "block" } ] } \ No newline at end of file diff --git a/www/utils/packages/typedoc-generate-references/src/constants/custom-options.ts b/www/utils/packages/typedoc-generate-references/src/constants/custom-options.ts index 038dc6f5be..9599dd400c 100644 --- a/www/utils/packages/typedoc-generate-references/src/constants/custom-options.ts +++ b/www/utils/packages/typedoc-generate-references/src/constants/custom-options.ts @@ -47,6 +47,19 @@ const customOptions: Record> = { tsConfigName: "types.json", name: "event", }), + events: getOptions({ + entryPointPath: "packages/core/utils/src/core-flows/events.ts", + tsConfigName: "utils.json", + name: "events", + enableEventsResolver: true, + }), + "module-events": getOptions({ + entryPointPath: "packages/core/utils/src/core-flows/events.ts", + tsConfigName: "utils.json", + name: "module-events", + enableEventsResolver: true, + generateCustomNamespaces: true, + }), file: getOptions({ entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts", tsConfigName: "utils.json", diff --git a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/events.ts b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/events.ts new file mode 100644 index 0000000000..479d1dc11b --- /dev/null +++ b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/events.ts @@ -0,0 +1,29 @@ +import { FormattingOptionsType } from "types" + +const eventsOptions: FormattingOptionsType = { + "^modules/events/": { + reflectionDescription: `This documentation page includes the list of all events emitted by [Medusa's workflows](https://docs.medusajs.com/resources/medusa-workflows-reference).`, + frontmatterData: { + slug: "/references/events", + sidebar_label: "Events Reference", + }, + isEventsReference: true, + reflectionTitle: { + fullReplacement: "Events Reference", + }, + }, + "^module_events": { + expandMembers: true, + isEventsReference: true, + reflectionDescription: `This reference shows all the events emitted by the Medusa application related to the {{alias}} Module. If you use the module outside the Medusa application, these events aren't emitted.`, + reflectionTitle: { + suffix: "Module Events Reference", + }, + frontmatterData: { + slug: "/references/{{alias-slug}}/events", + sidebar_label: "Events Reference", + }, + }, +} + +export default eventsOptions diff --git a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/index.ts b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/index.ts index 03d23c7938..5cb8861c3f 100644 --- a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/index.ts +++ b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/index.ts @@ -18,6 +18,7 @@ import cacheOptions from "./cache.js" import eventOptions from "./event.js" import fileServiceOptions from "./file-service.js" import notificationServiceOptions from "./notification-service.js" +import eventsOptions from "./events.js" const mergerCustomOptions: FormattingOptionsType = { ...authProviderOptions, @@ -25,6 +26,7 @@ const mergerCustomOptions: FormattingOptionsType = { ...coreFlowsOptions, ...dmlOptions, ...eventOptions, + ...eventsOptions, ...fileServiceOptions, ...fileOptions, ...fulfillmentProviderOptions, diff --git a/www/utils/packages/typedoc-generate-references/src/constants/merger-options.ts b/www/utils/packages/typedoc-generate-references/src/constants/merger-options.ts index 4dcc04096b..4bcaed4e3f 100644 --- a/www/utils/packages/typedoc-generate-references/src/constants/merger-options.ts +++ b/www/utils/packages/typedoc-generate-references/src/constants/merger-options.ts @@ -30,6 +30,9 @@ const allowedProjectDocuments: AllowedProjectDocumentsOption = { [ReflectionKind.Method]: true, [ReflectionKind.Property]: true, }, + "module-events": { + [ReflectionKind.Namespace]: true, + }, } modules.forEach((module) => { diff --git a/www/utils/packages/typedoc-plugin-custom/src/events-resolver.ts b/www/utils/packages/typedoc-plugin-custom/src/events-resolver.ts new file mode 100644 index 0000000000..b9c39db7a5 --- /dev/null +++ b/www/utils/packages/typedoc-plugin-custom/src/events-resolver.ts @@ -0,0 +1,106 @@ +import { readFileSync } from "fs" +import path from "path" +import { + Application, + Comment, + CommentTag, + Context, + Converter, + DeclarationReflection, + ParameterType, + ReflectionKind, +} from "typedoc" +import { MedusaEvent } from "types" +import { getDirname } from "utils" + +export class EventsResolver { + private app: Application + private events: MedusaEvent[] = [] + + constructor(app: Application) { + this.app = app + + this.app.options.addDeclaration({ + name: "enableEventsResolver", + help: "Whether to resolve events.", + type: ParameterType.Boolean, + defaultValue: false, + }) + + this.app.converter.on( + Converter.EVENT_RESOLVE_BEGIN, + this.resolveEvents.bind(this) + ) + } + + resolveEvents(context: Context) { + if (!this.app.options.getValue("enableEventsResolver")) { + return + } + + if (!this.events.length) { + const __dirname = getDirname(import.meta.url) + + const jsonFilePath = path.resolve( + __dirname, + path.join("..", "..", "..", "generated", "events-output.json") + ) + // read events file + const eventsJSON = readFileSync(jsonFilePath, "utf-8") + this.events = JSON.parse(eventsJSON) + } + + for (const reflection of context.project.getReflectionsByKind( + ReflectionKind.Variable + )) { + if ( + !(reflection instanceof DeclarationReflection) || + reflection.type?.type !== "reflection" || + !reflection.type.declaration.children + ) { + continue + } + + const relatedEvents = this.events.filter( + (event) => event.parentName === reflection.name + ) + + if (!relatedEvents.length) { + continue + } + + // loop over variable properties + for (const property of reflection.type.declaration.children) { + const propertyEvent = relatedEvents.find( + (event) => event.propertyName === property.name + ) + + if (!propertyEvent) { + continue + } + + if (!property.comment) { + property.comment = new Comment() + } + + property.comment.blockTags.push( + new CommentTag("@eventName", [ + { + kind: "code", + text: propertyEvent.name, + }, + ]) + ) + + property.comment.blockTags.push( + new CommentTag("@workflows", [ + { + kind: "text", + text: propertyEvent.workflows.join(", "), + }, + ]) + ) + } + } + } +} diff --git a/www/utils/packages/typedoc-plugin-custom/src/index.ts b/www/utils/packages/typedoc-plugin-custom/src/index.ts index 187c6b4d38..afd92b3450 100644 --- a/www/utils/packages/typedoc-plugin-custom/src/index.ts +++ b/www/utils/packages/typedoc-plugin-custom/src/index.ts @@ -13,6 +13,7 @@ import { load as dmlTypesNormalizer } from "./dml-types-normalizer.js" import { MermaidDiagramDMLGenerator } from "./mermaid-diagram-dml-generator.js" import { load as dmlJsonParser } from "./dml-json-parser.js" import { GenerateCustomNamespacePlugin } from "./generate-custom-namespaces.js" +import { EventsResolver } from "./events-resolver.js" export function load(app: Application) { resolveReferencesPluginLoad(app) @@ -30,4 +31,5 @@ export function load(app: Application) { new DmlRelationsResolver(app) new MermaidDiagramDMLGenerator(app) new GenerateCustomNamespacePlugin(app) + new EventsResolver(app) } diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/package.json b/www/utils/packages/typedoc-plugin-markdown-medusa/package.json index 153bb4e228..679c0c9dfb 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/package.json +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/package.json @@ -38,6 +38,7 @@ "dependencies": { "handlebars": "^4.7.8", "js-beautify": "^1.15.1", + "slugify": "^1.6.6", "utils": "*" } } diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts index 922dd3d0fb..cb1f6e5d88 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts @@ -80,6 +80,8 @@ import versionHelper from "./resources/helpers/version.js" import sourceCodeLinkHelper from "./resources/helpers/source-code-link.js" import workflowExamplesHelper from "./resources/helpers/workflow-examples.js" import stepExamplesHelper from "./resources/helpers/step-examples.js" +import ifEventsReferenceHelper from "./resources/helpers/if-events-reference.js" +import eventsListingHelper from "./resources/helpers/events-listing.js" import { MarkdownTheme } from "./theme.js" import { getDirname } from "utils" @@ -191,4 +193,6 @@ export function registerHelpers(theme: MarkdownTheme) { sourceCodeLinkHelper() workflowExamplesHelper() stepExamplesHelper() + ifEventsReferenceHelper(theme) + eventsListingHelper() } diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/events-listing.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/events-listing.ts new file mode 100644 index 0000000000..07f245fe13 --- /dev/null +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/events-listing.ts @@ -0,0 +1,142 @@ +import Handlebars from "handlebars" +import pkg from "slugify" +import { DeclarationReflection } from "typedoc" +import { pascalToWords } from "utils" + +const slugify = pkg.default + +export default function () { + Handlebars.registerHelper( + "eventsListing", + function (this: DeclarationReflection) { + const content: string[] = [] + + const subtitleLevel = (this.children?.length ?? 0) > 1 ? 3 : 2 + const showHeader = (this.children?.length ?? 0) > 1 + + this.children?.forEach((child, index) => { + content.push( + formatEventsType(child as DeclarationReflection, { + subtitleLevel, + showHeader, + }) + ) + if (index < this.children!.length - 1) { + content.push("") + content.push("---") + content.push("") + } + }) + + return content.join("\n") + } + ) +} + +function formatEventsType( + eventVariable: DeclarationReflection, + { + subtitleLevel = 3, + showHeader = true, + }: { + subtitleLevel?: number + showHeader?: boolean + } +) { + if (eventVariable.type?.type !== "reflection") { + return "" + } + const content: string[] = [] + const subHeaderPrefix = "#".repeat(subtitleLevel) + const header = pascalToWords( + eventVariable.name.replaceAll("WorkflowEvents", "") + ) + if (showHeader) { + content.push(`## ${header} Events`) + } + content.push("") + + const eventProperties = eventVariable.type.declaration.children || [] + + content.push(`${subHeaderPrefix} Summary`) + content.push("") + // table start + content.push(``) + // table header start + content.push(` `) + content.push(` `) + content.push(` \nEvent\n`) + content.push(` \nDescription\n`) + // table header end + content.push(` `) + content.push(` `) + // table body start + content.push(` `) + eventProperties.forEach((event) => { + const eventName = + event.comment?.blockTags + .find((tag) => tag.tag === "@eventName") + ?.content.map((content) => content.text) + .join("") || "" + const eventDescription = event.comment?.summary + .map((content) => content.text) + .join("") + + content.push(` `) + content.push( + ` \n[${eventName}](#${slugify( + eventName.replace(".", ""), + { + lower: true, + } + )})\n` + ) + content.push(` \n${eventDescription}\n`) + content.push(` `) + }) + // table body end + content.push(` `) + // table end + content.push(`
`) + content.push("") + + eventProperties.forEach((event, index) => { + const eventName = event.comment?.blockTags + .find((tag) => tag.tag === "@eventName") + ?.content.map((content) => content.text) + .join("") + const eventDescription = event.comment?.summary + .map((content) => content.text) + .join("") + const eventPayload = event.comment?.blockTags + .find((tag) => tag.tag === "@eventPayload") + ?.content.map((content) => content.text) + .join("") + const workflows = event.comment?.blockTags + .find((tag) => tag.tag === "@workflows") + ?.content.map((content) => content.text) + .join("") + .split(", ") + + content.push(`${subHeaderPrefix} \`${eventName}\``) + content.push("") + content.push(eventDescription || "") + content.push("") + content.push(`${subHeaderPrefix}# Payload`) + content.push("") + content.push(eventPayload || "") + content.push("") + content.push(`${subHeaderPrefix}# Workflows Emitting this Event`) + content.push("") + workflows?.forEach((workflow) => { + content.push(`- [${workflow}](/references/medusa-workflows/${workflow})`) + }) + content.push("") + if (index < eventProperties.length - 1) { + content.push("---") + content.push("") + } + }) + + return content.join("\n") +} diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/if-events-reference.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/if-events-reference.ts new file mode 100644 index 0000000000..888c17f1f1 --- /dev/null +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/if-events-reference.ts @@ -0,0 +1,15 @@ +import Handlebars from "handlebars" +import { Reflection } from "typedoc" +import { MarkdownTheme } from "../../theme.js" + +export default function (theme: MarkdownTheme) { + Handlebars.registerHelper( + "ifEventsReference", + function (this: Reflection, options: Handlebars.HelperOptions) { + const { isEventsReference = false } = + theme.getFormattingOptionsForLocation() + + return isEventsReference ? options.fn(this) : options.inverse(this) + } + ) +} diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/templates/reflection.hbs b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/templates/reflection.hbs index a725b75ad6..6249b4afcb 100755 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/templates/reflection.hbs +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/templates/reflection.hbs @@ -6,6 +6,16 @@ {{{ startSections }}} +{{#ifEventsReference}} + +{{#with model}} + +{{{ eventsListing }}} + +{{/with}} + +{{else}} + {{#with model}} {{#if (sectionEnabled "reflection_comment")}} @@ -148,4 +158,6 @@ {{/with}} +{{/ifEventsReference}} + {{{ endSections }}} \ No newline at end of file diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/utils/reflection-template-strings.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/utils/reflection-template-strings.ts index 5e2557b338..d4c811ff00 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/src/utils/reflection-template-strings.ts +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/utils/reflection-template-strings.ts @@ -1,4 +1,7 @@ import { Reflection, ReflectionKind } from "typedoc" +import pkg from "slugify" + +const slugify = pkg.default export function replaceTemplateVariables( reflection: Reflection, @@ -11,6 +14,12 @@ export function replaceTemplateVariables( return text .replaceAll("{{alias}}", reflection.name) .replaceAll("{{alias-lower}}", reflection.name.toLowerCase()) + .replaceAll( + "{{alias-slug}}", + slugify(reflection.name, { + lower: true, + }) + ) .replaceAll("{{parent.alias}}", reflection.parent?.name || "") .replaceAll( "{{parent.alias-lower}}", diff --git a/www/utils/packages/types/lib/index.d.ts b/www/utils/packages/types/lib/index.d.ts index 2249fee488..49c7bb71d9 100644 --- a/www/utils/packages/types/lib/index.d.ts +++ b/www/utils/packages/types/lib/index.d.ts @@ -100,6 +100,7 @@ export type FormattingOptionType = { shouldIncrementAfterStartSections?: boolean hideTocHeaders?: boolean workflowDiagramComponent?: string + isEventsReference?: boolean } export type AllowedProjectDocumentsOption = { @@ -289,6 +290,11 @@ export declare module "typedoc" { * Optionally specify a name prefix for all custom namespaces. */ customNamespaceNamePrefix: string + /** + * Whether to resolve events. + * @defaultValue false + */ + enableEventsResolver: boolean } } @@ -328,3 +334,15 @@ export declare type NamespaceGenerateDetails = { */ children?: NamespaceGenerateDetails[] } + +export declare type MedusaEvent = { + name: string + parentName: string + propertyName: string + payload: string + description?: string + workflows: string[] + version?: string + deprecated?: boolean + deprecated_message?: string +} diff --git a/www/utils/yarn.lock b/www/utils/yarn.lock index 9c18212f34..8391c0e4e6 100644 --- a/www/utils/yarn.lock +++ b/www/utils/yarn.lock @@ -5556,6 +5556,13 @@ __metadata: languageName: node linkType: hard +"slugify@npm:^1.6.6": + version: 1.6.6 + resolution: "slugify@npm:1.6.6" + checksum: e7e63f08f389a371d6228bc19d64ec84360bf0a538333446cc49dbbf3971751a6d180d2f31551188dd007a65ca771e69f574e0283290a7825a818e90b75ef44d + languageName: node + linkType: hard + "source-map@npm:^0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" @@ -6035,6 +6042,7 @@ __metadata: copyfiles: ^2.4.1 handlebars: ^4.7.8 js-beautify: ^1.15.1 + slugify: ^1.6.6 typedoc: ^0.27.5 types: "*" typescript: ^5.6.2