@mercurjs/docs 2.2.1 → 2.3.0-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/content/home.mdx +107 -0
  2. package/content/learn/architecture.mdx +168 -83
  3. package/content/learn/introduction.mdx +161 -56
  4. package/content/learn/migration-to-2-0.mdx +187 -0
  5. package/content/learn/why-mercur.mdx +91 -0
  6. package/content/platform/attribute/concepts/attribute-types.mdx +59 -0
  7. package/content/platform/attribute/concepts/global-vs-inline.mdx +50 -0
  8. package/content/platform/attribute/concepts/variant-axes.mdx +61 -0
  9. package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +69 -0
  10. package/content/platform/attribute/guides/create-a-variant-axis.mdx +83 -0
  11. package/content/platform/attribute/guides/create-an-attribute.mdx +81 -0
  12. package/content/platform/attribute/overview.mdx +87 -0
  13. package/content/platform/attribute/reference/data-models.mdx +56 -0
  14. package/content/platform/attribute/reference/events.mdx +41 -0
  15. package/content/platform/attribute/reference/links.mdx +45 -0
  16. package/content/platform/attribute/reference/service.mdx +49 -0
  17. package/content/platform/attribute/reference/workflows.mdx +45 -0
  18. package/content/platform/catalog/concepts/master-products.mdx +54 -0
  19. package/content/platform/catalog/concepts/product-seller-allowlist.mdx +48 -0
  20. package/content/platform/catalog/concepts/status-lifecycle.mdx +58 -0
  21. package/content/platform/catalog/concepts/variants-categories-collections.mdx +49 -0
  22. package/content/platform/catalog/guides/allowlist-stores.mdx +59 -0
  23. package/content/platform/catalog/guides/create-a-master-product.mdx +70 -0
  24. package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +79 -0
  25. package/content/platform/catalog/overview.mdx +90 -0
  26. package/content/platform/catalog/reference/data-models.mdx +66 -0
  27. package/content/platform/catalog/reference/events.mdx +41 -0
  28. package/content/platform/catalog/reference/links.mdx +44 -0
  29. package/content/platform/catalog/reference/service.mdx +52 -0
  30. package/content/platform/catalog/reference/workflows.mdx +40 -0
  31. package/content/platform/commission/concepts/order-commission-lines.mdx +65 -0
  32. package/content/platform/commission/concepts/rule-matching.mdx +86 -0
  33. package/content/platform/commission/concepts/rules-and-rates.mdx +84 -0
  34. package/content/platform/commission/guides/batch-update-rules.mdx +58 -0
  35. package/content/platform/commission/guides/create-a-rate.mdx +72 -0
  36. package/content/platform/commission/guides/refresh-order-commission-lines.mdx +55 -0
  37. package/content/platform/commission/overview.mdx +86 -0
  38. package/content/platform/commission/reference/data-models.mdx +75 -0
  39. package/content/platform/commission/reference/events.mdx +58 -0
  40. package/content/platform/commission/reference/links.mdx +45 -0
  41. package/content/platform/commission/reference/service.mdx +51 -0
  42. package/content/platform/commission/reference/workflows.mdx +40 -0
  43. package/content/platform/offer/concepts/pricing-and-inventory.mdx +72 -0
  44. package/content/platform/offer/concepts/shipping.mdx +48 -0
  45. package/content/platform/offer/concepts/what-is-an-offer.mdx +65 -0
  46. package/content/platform/offer/guides/bulk-create-offers.mdx +84 -0
  47. package/content/platform/offer/guides/create-an-offer.mdx +83 -0
  48. package/content/platform/offer/guides/manage-offer-inventory.mdx +53 -0
  49. package/content/platform/offer/overview.mdx +84 -0
  50. package/content/platform/offer/reference/data-models.mdx +56 -0
  51. package/content/platform/offer/reference/events.mdx +39 -0
  52. package/content/platform/offer/reference/links.mdx +60 -0
  53. package/content/platform/offer/reference/service.mdx +53 -0
  54. package/content/platform/offer/reference/workflows.mdx +38 -0
  55. package/content/platform/order-group/concepts/computed-totals.mdx +59 -0
  56. package/content/platform/order-group/concepts/order-splitting.mdx +61 -0
  57. package/content/platform/order-group/concepts/the-order-group.mdx +64 -0
  58. package/content/platform/order-group/guides/list-order-groups.mdx +67 -0
  59. package/content/platform/order-group/guides/retrieve-an-order-group.mdx +63 -0
  60. package/content/platform/order-group/guides/split-a-cart.mdx +58 -0
  61. package/content/platform/order-group/overview.mdx +83 -0
  62. package/content/platform/order-group/reference/data-models.mdx +40 -0
  63. package/content/platform/order-group/reference/events.mdx +37 -0
  64. package/content/platform/order-group/reference/links.mdx +45 -0
  65. package/content/platform/order-group/reference/service.mdx +50 -0
  66. package/content/platform/order-group/reference/workflows.mdx +39 -0
  67. package/content/platform/payout/concepts/account-lifecycle.mdx +75 -0
  68. package/content/platform/payout/concepts/accounts-and-onboarding.mdx +70 -0
  69. package/content/platform/payout/concepts/payout-pipeline.mdx +99 -0
  70. package/content/platform/payout/guides/create-a-payout-account.mdx +59 -0
  71. package/content/platform/payout/guides/process-a-provider-webhook.mdx +74 -0
  72. package/content/platform/payout/guides/start-provider-onboarding.mdx +50 -0
  73. package/content/platform/payout/overview.mdx +86 -0
  74. package/content/platform/payout/reference/data-models.mdx +61 -0
  75. package/content/platform/payout/reference/events.mdx +48 -0
  76. package/content/platform/payout/reference/links.mdx +36 -0
  77. package/content/platform/payout/reference/service.mdx +53 -0
  78. package/content/platform/payout/reference/workflows.mdx +32 -0
  79. package/content/platform/product-edit/concepts/change-actions.mdx +66 -0
  80. package/content/platform/product-edit/concepts/change-pipeline.mdx +69 -0
  81. package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +66 -0
  82. package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +76 -0
  83. package/content/platform/product-edit/guides/edit-a-product.mdx +74 -0
  84. package/content/platform/product-edit/guides/request-a-revision.mdx +54 -0
  85. package/content/platform/product-edit/overview.mdx +85 -0
  86. package/content/platform/product-edit/reference/data-models.mdx +65 -0
  87. package/content/platform/product-edit/reference/events.mdx +47 -0
  88. package/content/platform/product-edit/reference/links.mdx +39 -0
  89. package/content/platform/product-edit/reference/service.mdx +43 -0
  90. package/content/platform/product-edit/reference/workflows.mdx +49 -0
  91. package/content/platform/review/concepts/product-vs-seller-reviews.mdx +63 -0
  92. package/content/platform/review/concepts/ratings-and-moderation.mdx +64 -0
  93. package/content/platform/review/concepts/the-review-model.mdx +56 -0
  94. package/content/platform/review/guides/compute-aggregate-ratings.mdx +57 -0
  95. package/content/platform/review/guides/create-a-review.mdx +55 -0
  96. package/content/platform/review/guides/moderate-a-review.mdx +58 -0
  97. package/content/platform/review/guides/respond-to-a-review.mdx +61 -0
  98. package/content/platform/review/overview.mdx +87 -0
  99. package/content/platform/review/reference/data-models.mdx +36 -0
  100. package/content/platform/review/reference/events.mdx +61 -0
  101. package/content/platform/review/reference/links.mdx +43 -0
  102. package/content/platform/review/reference/service.mdx +54 -0
  103. package/content/platform/review/reference/workflows.mdx +31 -0
  104. package/content/platform/store/concepts/lifecycle.mdx +62 -0
  105. package/content/platform/store/concepts/store-entity.mdx +53 -0
  106. package/content/platform/store/concepts/team.mdx +50 -0
  107. package/content/platform/store/guides/create-a-store.mdx +55 -0
  108. package/content/platform/store/guides/manage-the-team.mdx +55 -0
  109. package/content/platform/store/guides/moderate-a-store.mdx +59 -0
  110. package/content/platform/store/overview.mdx +86 -0
  111. package/content/platform/store/reference/data-models.mdx +89 -0
  112. package/content/platform/store/reference/events.mdx +43 -0
  113. package/content/platform/store/reference/links.mdx +71 -0
  114. package/content/platform/store/reference/service.mdx +51 -0
  115. package/content/platform/store/reference/workflows.mdx +35 -0
  116. package/content/references/api/admin/commission-rates/create-commission-rate.mdx +1 -1
  117. package/content/references/api/admin/commission-rates/list-commission-rates.mdx +2 -2
  118. package/content/references/api/admin/commission-rates/update-commission-rate.mdx +1 -1
  119. package/content/references/api/admin/offers/batch-create-offers.mdx +3 -3
  120. package/content/references/api/admin/order-groups/list-order-groups.mdx +1 -1
  121. package/content/references/api/admin/product-attributes/create-attribute-value.mdx +2 -2
  122. package/content/references/api/admin/product-attributes/create-product-attribute.mdx +1 -1
  123. package/content/references/api/admin/product-attributes/update-product-attribute.mdx +1 -1
  124. package/content/references/api/admin/product-changes/confirm-product-change.mdx +1 -1
  125. package/content/references/api/admin/products/batch-product-attributes.mdx +1 -1
  126. package/content/references/api/admin/products/create-product.mdx +1 -1
  127. package/content/references/api/admin/products/preview-product.mdx +1 -1
  128. package/content/references/api/admin.mdx +4 -5
  129. package/content/references/api/conventions.mdx +9 -7
  130. package/content/references/api/store/carts/add-line-item.mdx +1 -1
  131. package/content/references/api/store/offers/list-offers.mdx +1 -1
  132. package/content/references/api/store/order-groups/list-order-groups.mdx +1 -1
  133. package/content/references/api/store.mdx +4 -13
  134. package/content/references/api/vendor/members/accept-member-invite.mdx +1 -1
  135. package/content/references/api/vendor/offers/batch-create-offers.mdx +4 -0
  136. package/content/references/api/vendor/offers/batch-offer-inventory-items.mdx +2 -0
  137. package/content/references/api/vendor/offers/create-offer.mdx +13 -1
  138. package/content/references/api/vendor/offers/list-offers.mdx +4 -0
  139. package/content/references/api/vendor/offers/retrieve-offer.mdx +4 -0
  140. package/content/references/api/vendor/offers/update-offer.mdx +12 -0
  141. package/content/references/api/vendor/payout-accounts/create-onboarding.mdx +1 -1
  142. package/content/references/api/vendor/products/batch-product-attributes.mdx +1 -1
  143. package/content/references/api/vendor/products/create-product-variant.mdx +1 -1
  144. package/content/references/api/vendor/products/create-product.mdx +1 -1
  145. package/content/references/api/vendor/products/delete-product.mdx +1 -1
  146. package/content/references/api/vendor/products/update-product.mdx +1 -1
  147. package/content/references/api/vendor/sellers/create-seller.mdx +2 -2
  148. package/content/references/api/vendor/sellers/list-sellers.mdx +1 -1
  149. package/content/references/api/vendor.mdx +6 -5
  150. package/content/references/configuration.mdx +16 -33
  151. package/content/references/overview.mdx +34 -52
  152. package/content/references/panel-extensions/create-page.mdx +194 -0
  153. package/content/references/panel-extensions/custom-fields.mdx +256 -0
  154. package/content/references/panel-extensions/overview.mdx +102 -0
  155. package/content/references/panel-extensions/widgets.mdx +212 -0
  156. package/content/resources/ai/mcp.mdx +2 -2
  157. package/content/resources/ai/overview.mdx +21 -16
  158. package/content/resources/ai/skills.mdx +67 -0
  159. package/content/resources/best-practices/api-routes.mdx +55 -43
  160. package/content/resources/best-practices/custom-fields.mdx +116 -92
  161. package/content/resources/best-practices/frontend.mdx +62 -50
  162. package/content/resources/best-practices/module-links.mdx +48 -34
  163. package/content/resources/best-practices/modules.mdx +53 -27
  164. package/content/resources/best-practices/overview.mdx +45 -17
  165. package/content/resources/best-practices/subscribers-and-jobs.mdx +37 -24
  166. package/content/resources/best-practices/types.mdx +38 -23
  167. package/content/resources/best-practices/workflows.mdx +33 -21
  168. package/content/resources/customization/custom-fields.mdx +15 -15
  169. package/content/resources/customization/extend-a-workflow.mdx +7 -4
  170. package/content/resources/customization/extending-panels.mdx +55 -52
  171. package/content/resources/deployment/medusa-cloud.mdx +21 -20
  172. package/content/resources/deployment/self-host.mdx +123 -0
  173. package/content/resources/integrations/overview.mdx +38 -0
  174. package/content/resources/integrations/stripe-connect.mdx +39 -38
  175. package/content/resources/tutorials/add-a-block.mdx +25 -18
  176. package/content/resources/tutorials/add-a-widget.mdx +32 -23
  177. package/content/resources/tutorials/add-order-detail-button.mdx +33 -20
  178. package/content/resources/tutorials/attributes-and-variant-axes.mdx +28 -27
  179. package/content/resources/tutorials/build-a-block.mdx +26 -15
  180. package/content/resources/tutorials/custom-api-route.mdx +32 -20
  181. package/content/resources/tutorials/custom-panel-page.mdx +21 -12
  182. package/content/resources/tutorials/customize-navigation.mdx +30 -23
  183. package/content/resources/tutorials/extend-forms-and-tables.mdx +36 -28
  184. package/content/resources/tutorials/extend-onboarding.mdx +38 -35
  185. package/content/resources/tutorials/master-products-and-offers.mdx +28 -23
  186. package/content/telemetry.mdx +3 -3
  187. package/content/user-guide/admin/attributes/how-tos/create-an-attribute.mdx +64 -0
  188. package/content/user-guide/admin/attributes/how-tos/manage-possible-values.mdx +40 -0
  189. package/content/user-guide/admin/attributes/overview.mdx +22 -0
  190. package/content/user-guide/admin/commissions/how-tos/create-a-commission-rule.mdx +63 -0
  191. package/content/user-guide/admin/commissions/how-tos/edit-the-global-commission.mdx +48 -0
  192. package/content/user-guide/admin/commissions/how-tos/manage-a-commission-rule.mdx +45 -0
  193. package/content/user-guide/admin/commissions/overview.mdx +25 -0
  194. package/content/user-guide/admin/overview.mdx +20 -12
  195. package/content/user-guide/admin/product-requests/how-tos/review-a-new-product.mdx +58 -0
  196. package/content/user-guide/admin/product-requests/how-tos/review-a-product-edit.mdx +48 -0
  197. package/content/user-guide/admin/product-requests/overview.mdx +25 -0
  198. package/content/user-guide/vendor/offers/how-tos/create-an-offer.mdx +59 -0
  199. package/content/user-guide/vendor/offers/how-tos/update-prices-and-stock.mdx +40 -0
  200. package/content/user-guide/vendor/offers/overview.mdx +22 -0
  201. package/content/user-guide/vendor/onboarding.mdx +79 -0
  202. package/content/user-guide/vendor/orders/how-tos/fulfill-an-order.mdx +49 -0
  203. package/content/user-guide/vendor/orders/how-tos/mark-an-order-as-delivered.mdx +33 -0
  204. package/content/user-guide/vendor/orders/how-tos/process-a-return.mdx +42 -0
  205. package/content/user-guide/vendor/orders/how-tos/refund-an-order.mdx +38 -0
  206. package/content/user-guide/vendor/orders/how-tos/ship-an-order.mdx +40 -0
  207. package/content/user-guide/vendor/orders/overview.mdx +31 -0
  208. package/content/user-guide/vendor/overview.mdx +23 -12
  209. package/content/user-guide/vendor/products/how-tos/edit-a-product.mdx +44 -0
  210. package/content/user-guide/vendor/products/how-tos/submit-a-product.mdx +63 -0
  211. package/content/user-guide/vendor/products/overview.mdx +22 -0
  212. package/llms.txt +176 -142
  213. package/package.json +1 -1
  214. package/content/learn/concepts.mdx +0 -84
  215. package/content/learn/installation.mdx +0 -117
  216. package/content/learn/mirakl-alternative.mdx +0 -86
  217. package/content/migration/from-1-x-to-2-0.mdx +0 -152
  218. package/content/migration/from-2-0-to-2-1.mdx +0 -105
  219. package/content/migration/overview.mdx +0 -58
  220. package/content/references/api/store/search/search.mdx +0 -136
  221. package/content/references/modules/commission.mdx +0 -106
  222. package/content/references/modules/custom-fields.mdx +0 -45
  223. package/content/references/modules/media.mdx +0 -55
  224. package/content/references/modules/offer.mdx +0 -64
  225. package/content/references/modules/payout.mdx +0 -121
  226. package/content/references/modules/product-attribute.mdx +0 -111
  227. package/content/references/modules/product-edit.mdx +0 -80
  228. package/content/references/modules/search.mdx +0 -112
  229. package/content/references/modules/seller.mdx +0 -175
  230. package/content/references/panel-extension-api.mdx +0 -337
  231. package/content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx +0 -48
  232. package/content/references/workflows/cart/complete-cart-with-split-orders.mdx +0 -36
  233. package/content/references/workflows/cart/list-seller-shipping-options-for-cart.mdx +0 -39
  234. package/content/references/workflows/cart/update-cart-seller-promotions.mdx +0 -42
  235. package/content/references/workflows/commission/batch-commission-rules.mdx +0 -49
  236. package/content/references/workflows/commission/create-commission-rates.mdx +0 -41
  237. package/content/references/workflows/commission/delete-commission-rates.mdx +0 -34
  238. package/content/references/workflows/commission/refresh-order-commission-lines.mdx +0 -30
  239. package/content/references/workflows/commission/update-commission-rates.mdx +0 -35
  240. package/content/references/workflows/media/set-category-images.mdx +0 -41
  241. package/content/references/workflows/media/set-collection-images.mdx +0 -41
  242. package/content/references/workflows/member/accept-member-invite.mdx +0 -33
  243. package/content/references/workflows/member/add-seller-member.mdx +0 -30
  244. package/content/references/workflows/member/create-member-invites.mdx +0 -34
  245. package/content/references/workflows/member/delete-member-invite.mdx +0 -24
  246. package/content/references/workflows/member/remove-seller-member.mdx +0 -28
  247. package/content/references/workflows/member/resend-member-invite.mdx +0 -28
  248. package/content/references/workflows/member/update-member-role.mdx +0 -28
  249. package/content/references/workflows/member/update-member.mdx +0 -35
  250. package/content/references/workflows/offer/batch-offer-inventory-items.mdx +0 -62
  251. package/content/references/workflows/offer/create-offers.mdx +0 -65
  252. package/content/references/workflows/offer/delete-offers.mdx +0 -35
  253. package/content/references/workflows/offer/update-offers.mdx +0 -53
  254. package/content/references/workflows/order/cancel-order-fulfillment.mdx +0 -34
  255. package/content/references/workflows/order/confirm-claim-request.mdx +0 -25
  256. package/content/references/workflows/order/confirm-exchange-request.mdx +0 -25
  257. package/content/references/workflows/order/confirm-order-edit-request.mdx +0 -25
  258. package/content/references/workflows/order/confirm-return-receive.mdx +0 -25
  259. package/content/references/workflows/order/create-order-fulfillment.mdx +0 -45
  260. package/content/references/workflows/order-group/get-order-group-detail.mdx +0 -29
  261. package/content/references/workflows/order-group/get-order-groups-list.mdx +0 -38
  262. package/content/references/workflows/overview.mdx +0 -72
  263. package/content/references/workflows/payout/create-onboarding.mdx +0 -36
  264. package/content/references/workflows/payout/create-payout-account.mdx +0 -33
  265. package/content/references/workflows/payout/create-payout.mdx +0 -30
  266. package/content/references/workflows/payout/process-payout-for-webhook.mdx +0 -34
  267. package/content/references/workflows/product/confirm-products.mdx +0 -48
  268. package/content/references/workflows/product/create-products.mdx +0 -63
  269. package/content/references/workflows/product/link-sellers-to-product-category.mdx +0 -40
  270. package/content/references/workflows/product/link-sellers-to-product.mdx +0 -40
  271. package/content/references/workflows/product/reject-product.mdx +0 -48
  272. package/content/references/workflows/product/request-product-change.mdx +0 -48
  273. package/content/references/workflows/product-attribute/add-product-attributes-to-product.mdx +0 -49
  274. package/content/references/workflows/product-attribute/create-and-link-product-attributes-to-product.mdx +0 -46
  275. package/content/references/workflows/product-attribute/create-product-attribute-values.mdx +0 -43
  276. package/content/references/workflows/product-attribute/create-product-attributes.mdx +0 -56
  277. package/content/references/workflows/product-attribute/delete-product-attribute-values.mdx +0 -30
  278. package/content/references/workflows/product-attribute/delete-product-attributes.mdx +0 -30
  279. package/content/references/workflows/product-attribute/remove-product-attributes-from-product.mdx +0 -29
  280. package/content/references/workflows/product-attribute/update-product-attribute-values.mdx +0 -44
  281. package/content/references/workflows/product-attribute/update-product-attributes-on-product.mdx +0 -41
  282. package/content/references/workflows/product-attribute/update-product-attributes.mdx +0 -48
  283. package/content/references/workflows/product-attribute/upsert-product-attribute-values.mdx +0 -43
  284. package/content/references/workflows/product-edit/auto-confirm-product-change.mdx +0 -39
  285. package/content/references/workflows/product-edit/cancel-product-change.mdx +0 -49
  286. package/content/references/workflows/product-edit/confirm-product-change.mdx +0 -57
  287. package/content/references/workflows/product-edit/create-product-change.mdx +0 -72
  288. package/content/references/workflows/product-edit/reject-product-change.mdx +0 -54
  289. package/content/references/workflows/product-edit/stage-product-change.mdx +0 -75
  290. package/content/references/workflows/seller/approve-seller.mdx +0 -36
  291. package/content/references/workflows/seller/create-seller-account.mdx +0 -59
  292. package/content/references/workflows/seller/create-seller-defaults.mdx +0 -22
  293. package/content/references/workflows/seller/create-sellers.mdx +0 -65
  294. package/content/references/workflows/seller/delete-seller-professional-details.mdx +0 -37
  295. package/content/references/workflows/seller/delete-sellers.mdx +0 -24
  296. package/content/references/workflows/seller/invite-seller.mdx +0 -28
  297. package/content/references/workflows/seller/suspend-seller.mdx +0 -37
  298. package/content/references/workflows/seller/terminate-seller.mdx +0 -37
  299. package/content/references/workflows/seller/unsuspend-seller.mdx +0 -36
  300. package/content/references/workflows/seller/unterminate-seller.mdx +0 -36
  301. package/content/references/workflows/seller/update-seller-address.mdx +0 -55
  302. package/content/references/workflows/seller/update-seller-payment-details.mdx +0 -52
  303. package/content/references/workflows/seller/update-seller-professional-details.mdx +0 -48
  304. package/content/references/workflows/seller/update-sellers.mdx +0 -57
  305. package/content/resources/ai/llms.mdx +0 -74
  306. package/content/resources/integrations/notifications.mdx +0 -39
  307. package/content/resources/integrations/search.mdx +0 -122
  308. package/content/resources/tutorials/configure-commissions.mdx +0 -127
  309. package/content/resources/tutorials/first-marketplace.mdx +0 -45
  310. package/content/resources/tutorials/handle-product-requests.mdx +0 -80
  311. package/content/resources/tutorials/import-export-products.mdx +0 -96
  312. package/content/resources/tutorials/seller-payouts-stripe.mdx +0 -89
  313. package/content/resources/tutorials/store-setup-checklist.mdx +0 -214
  314. package/content/tools/api-client.mdx +0 -155
  315. package/content/tools/cli.mdx +0 -196
  316. package/content/tools/dashboard-sdk.mdx +0 -35
@@ -1,12 +1,13 @@
1
1
  ---
2
- title: "Best Practices Overview"
3
- description: "How to build with Mercur — the layered architecture, the non-negotiable rules, and where each piece of logic belongs."
2
+ title: "How-to Guides"
3
+ sidebarTitle: "Overview"
4
+ description: "How to build with Mercur: the layered architecture, the non-negotiable rules, and where each piece of logic belongs."
4
5
  ---
5
6
 
6
7
  This section is a practical guide for developing on Mercur, written for both **human developers** and **AI coding agents**. It captures the conventions the codebase already follows so that new code reads as if it belongs, stays testable, and survives upgrades of the underlying Medusa framework.
7
8
 
8
9
  <Note>
9
- Mercur is a Medusa plugin. Every rule here is either a Medusa framework requirement or a Mercur convention that keeps the marketplace layer consistent. When Medusa's docs and this guide agree, follow both; when in doubt, mirror an existing module, workflow, or route in `packages/core`.
10
+ Mercur is a marketplace platform built on Medusa. Every rule here is either a Medusa requirement or a Mercur convention that keeps the marketplace layer consistent. When Medusa's docs and this guide agree, follow both; when in doubt, mirror an existing module, workflow, or route in `packages/core`.
10
11
  </Note>
11
12
 
12
13
  ## The layered architecture
@@ -29,7 +30,7 @@ graph TD
29
30
 
30
31
  <CardGroup cols={2}>
31
32
  <Card title="Module" icon="cube">
32
- Owns one domain's data. Thin CRUD only no orchestration, no cross-module calls.
33
+ Owns one domain's data. Thin CRUD only, with no orchestration and no cross-module calls.
33
34
  </Card>
34
35
  <Card title="Workflow" icon="diagram-project">
35
36
  Orchestrates a business operation across modules, step by step, with automatic rollback (compensation) on failure.
@@ -38,20 +39,20 @@ graph TD
38
39
  A thin HTTP adapter: validate input, run a workflow (or query for reads), shape the response.
39
40
  </Card>
40
41
  <Card title="Frontend" icon="window">
41
- Admin/Vendor panels and storefront. Talks to the API only through the typed SDK never raw `fetch`.
42
+ Admin/Vendor panels and storefront. Talks to the API only through the typed SDK, never raw `fetch`.
42
43
  </Card>
43
44
  </CardGroup>
44
45
 
45
46
  Why this shape matters:
46
47
 
47
- - **Testability** business logic lives in workflows, which can be run in isolation without an HTTP request.
48
- - **Reusability** a workflow can be called from a route, a subscriber, or a scheduled job.
49
- - **Upgrade safety** modules stay thin, so Medusa framework upgrades rarely touch your logic.
50
- - **Rollback** because mutations are workflow steps, a failure halfway through automatically undoes the earlier steps.
48
+ - **Testability:** business logic lives in workflows, which can be run in isolation without an HTTP request.
49
+ - **Reusability:** a workflow can be called from a route, a subscriber, or a scheduled job.
50
+ - **Upgrade safety:** modules stay thin, so Medusa framework upgrades rarely touch your logic.
51
+ - **Rollback:** because mutations are workflow steps, a failure halfway through automatically undoes the earlier steps.
51
52
 
52
53
  ## The non-negotiables
53
54
 
54
- These are hard rules. Breaking one produces code that looks like it works but silently violates the architecture no rollback, broken upgrades, or data written outside a workflow.
55
+ These are hard rules. Breaking one produces code that looks like it works but silently violates the architecture, with no rollback, broken upgrades, or data written outside a workflow.
55
56
 
56
57
  <Warning>
57
58
  **All mutations go through a workflow.** Never write to the database directly from an API route, a subscriber, or a scheduled job. Reads may query directly; writes must run a workflow so they get validation, compensation, and event emission.
@@ -92,24 +93,51 @@ When you're about to write a piece of logic, find the concern in this table befo
92
93
  A quick mental test: *"Does this change data?"* → it must run inside a workflow. *"Does this react to a change?"* → it's a subscriber. *"Does this run on a schedule?"* → it's a job. *"Is this just reading and shaping data for a screen?"* → it's a route + Query. Everything else is either module CRUD or frontend.
93
94
  </Tip>
94
95
 
95
- ## How to read the rest of this section
96
+ ## Guides
96
97
 
97
- Each page that follows drills into one layer and its rules:
98
+ Follow these guides to build each layer the Mercur way.
99
+
100
+ ### Server
98
101
 
99
102
  <CardGroup cols={2}>
100
- <Card title="Modules" href="/rc/resources/best-practices/modules" icon="cube">
103
+ <Card title="Create a Module" href="/resources/best-practices/modules" icon="cube">
101
104
  Thin CRUD, naming, and what must never live in a service.
102
105
  </Card>
103
- <Card title="Module links" href="/rc/resources/best-practices/module-links" icon="link">
106
+ <Card title="Link Modules" href="/resources/best-practices/module-links" icon="link">
104
107
  `defineLink`, link direction, and filtering by links.
105
108
  </Card>
106
- <Card title="Workflows" href="/rc/resources/best-practices/workflows" icon="diagram-project">
109
+ <Card title="Create a Workflow" href="/resources/best-practices/workflows" icon="diagram-project">
107
110
  Composition constraints, steps, compensation, and the query engine.
108
111
  </Card>
109
- <Card title="API routes" href="/rc/resources/best-practices/api-routes" icon="plug">
112
+ <Card title="Create an API Route" href="/resources/best-practices/api-routes" icon="plug">
110
113
  Thin adapters, Zod validation, middlewares as filters, `queryConfig`.
111
114
  </Card>
112
- <Card title="Subscribers & jobs" href="/rc/resources/best-practices/subscribers-and-jobs" icon="clock">
115
+ <Card title="Subscribers & Jobs" href="/resources/best-practices/subscribers-and-jobs" icon="clock">
113
116
  Event-driven side effects and scheduled work done safely.
114
117
  </Card>
118
+ <Card title="Add a Custom Field" href="/resources/best-practices/custom-fields" icon="table-cells">
119
+ Attach data to an entity end-to-end, from core to the panels.
120
+ </Card>
121
+ </CardGroup>
122
+
123
+ ### Panels
124
+
125
+ <CardGroup cols={2}>
126
+ <Card title="Extend the Panels" href="/resources/customization/extending-panels" icon="window">
127
+ The extension model shared by the Admin and Vendor panels.
128
+ </Card>
129
+ <Card title="Add a Widget" href="/resources/tutorials/add-a-widget" icon="puzzle-piece">
130
+ Inject a component into a built-in page zone.
131
+ </Card>
132
+ </CardGroup>
133
+
134
+ ### Blocks
135
+
136
+ <CardGroup cols={2}>
137
+ <Card title="Add a Block" href="/resources/tutorials/add-a-block" icon="cubes">
138
+ Install a feature block into your project.
139
+ </Card>
140
+ <Card title="Build a Block" href="/resources/tutorials/build-a-block" icon="hammer">
141
+ Package your own feature as a distributable block.
142
+ </Card>
115
143
  </CardGroup>
@@ -1,15 +1,16 @@
1
1
  ---
2
- title: "Subscribers & jobs"
3
- description: "React to events and run periodic work safely — fetch from { id }, mutate via workflows, log-don't-throw, idempotency and loop guards, cron jobs."
2
+ title: "How to React to Events and Schedule Jobs"
3
+ sidebarTitle: "Subscribers & Jobs"
4
+ description: "Run work outside the request cycle: react to events with subscribers, run periodic work with scheduled jobs, and always mutate through workflows."
4
5
  ---
5
6
 
6
- Subscribers and scheduled jobs are the two ways work happens *outside* a request. Both follow the same core rule as everything else: **they never mutate directly they run a [workflow](/rc/resources/best-practices/workflows).**
7
+ Subscribers and scheduled jobs are the two ways work happens outside a request. Both follow the same core rule as everything else: they never mutate directly, they run a [workflow](/rc/resources/best-practices/workflows).
7
8
 
8
- ## Subscribers react to events
9
+ ## Subscribers: react to events
9
10
 
10
- A subscriber listens for a domain event (emitted by a workflow via `emitEventStep`) and runs an asynchronous side effect: send a notification, sync a search index, create a link. It lives in `src/subscribers/` and exports a handler plus a `config` naming the event.
11
+ A subscriber listens for a domain event (emitted by a workflow via `emitEventStep`) and runs an asynchronous side effect, such as sending a notification, syncing a search index, or creating a link. It lives in `src/subscribers/` and exports a handler plus a `config` naming the event.
11
12
 
12
- ```ts title="src/subscribers/brand-created.ts"
13
+ ```ts apps/api/src/subscribers/brand-created.ts
13
14
  import { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
14
15
 
15
16
  export default async function brandCreatedHandler({
@@ -28,10 +29,12 @@ export const config: SubscriberConfig = {
28
29
  ### Fetch full data from `{ id }`
29
30
 
30
31
  <Warning>
31
- Event payloads carry **ids, not entities.** A subscriber receives `{ id }` (sometimes a couple of ids) and must fetch the full record it needs via Query. Never rely on a fat event payload it goes stale and couples the emitter to every consumer's needs.
32
+ Event payloads carry ids, not entities. A subscriber receives `{ id }` (sometimes a couple of ids) and must fetch the full record it needs via Query. Never rely on a fat event payload. It goes stale and couples the emitter to every consumer's needs.
32
33
  </Warning>
33
34
 
34
- ```ts
35
+ Fetch what you need through Query, keyed by the id on the event.
36
+
37
+ ```ts apps/api/src/subscribers/brand-created.ts
35
38
  const query = container.resolve(ContainerRegistrationKeys.QUERY)
36
39
  const { data: [brand] } = await query.graph({
37
40
  entity: "brand",
@@ -42,9 +45,9 @@ const { data: [brand] } = await query.graph({
42
45
 
43
46
  ### Mutate via workflows, never directly
44
47
 
45
- If the subscriber needs to change data, it runs a workflow same as a route would. The subscriber is the trigger; the workflow is the work.
48
+ If the subscriber needs to change data, it runs a workflow, same as a route would. The subscriber is the trigger. The workflow is the work.
46
49
 
47
- ```ts
50
+ ```ts apps/api/src/subscribers/brand-created.ts
48
51
  await createBrandNotificationWorkflow(container).run({
49
52
  input: { brand_id: brand.id },
50
53
  })
@@ -53,10 +56,10 @@ await createBrandNotificationWorkflow(container).run({
53
56
  ### Log, don't throw
54
57
 
55
58
  <Warning>
56
- A subscriber runs detached from the request. Throwing doesn't surface to a user it just fails silently or spams retries. **Catch errors and log them** (resolve the `logger`), then decide explicitly whether to rethrow for a retry or swallow.
59
+ A subscriber runs detached from the request. Throwing does not surface to a user. It just fails silently or spams retries. Catch errors and log them (resolve the `logger`), then decide explicitly whether to rethrow for a retry or swallow.
57
60
  </Warning>
58
61
 
59
- ```ts
62
+ ```ts apps/api/src/subscribers/brand-created.ts
60
63
  const logger = container.resolve("logger")
61
64
  try {
62
65
  await doWork()
@@ -67,20 +70,20 @@ try {
67
70
 
68
71
  ### Idempotency and loop guards
69
72
 
70
- Events can be delivered more than once, and a subscriber that mutates data can re-trigger the very event it listens to. Two defences:
73
+ Events can be delivered more than once, and a subscriber that mutates data can re-trigger the very event it listens to. You have two defences:
71
74
 
72
- - **Idempotency** make the handler safe to run twice. Check current state before acting (e.g. "is this product already linked to a brand?" before creating the link), or clear the marker that triggered the work so a redelivered event finds nothing left to do.
73
- - **Loop guards** if handling event X causes a mutation that emits X again, gate on a condition that becomes false after the first run, or key off a marker you set. Never emit the same event unconditionally from its own subscriber.
75
+ - **Idempotency:** make the handler safe to run twice. Check current state before acting (for example, "is this product already linked to a brand?" before creating the link), or clear the marker that triggered the work so a redelivered event finds nothing left to do.
76
+ - **Loop guards:** if handling event X causes a mutation that emits X again, gate on a condition that becomes false after the first run, or key off a marker you set. Never emit the same event unconditionally from its own subscriber.
74
77
 
75
78
  <Tip>
76
79
  A good idempotency check reads the current state through Query first and returns early if the work is already done. This makes redelivery harmless and removes the need for exactly-once guarantees.
77
80
  </Tip>
78
81
 
79
- ## Scheduled jobs periodic work
82
+ ## Scheduled jobs: periodic work
80
83
 
81
- A scheduled job runs on a cron interval to do time-based work: poll for records that became ready, reconcile drifted counters, emit a "settle now" event. It lives in `src/jobs/`, exports a handler taking the container, and a `config` with a `name` and a cron `schedule`.
84
+ A scheduled job runs on a cron interval to do time-based work, such as polling for records that became ready, reconciling drifted counters, or emitting a "settle now" event. It lives in `src/jobs/`, exports a handler taking the container, and a `config` with a `name` and a cron `schedule`.
82
85
 
83
- ```ts title="src/jobs/deactivate-stale-brands.ts"
86
+ ```ts apps/api/src/jobs/deactivate-stale-brands.ts
84
87
  import { MedusaContainer } from "@medusajs/framework/types"
85
88
 
86
89
  export default async function deactivateStaleBrands(container: MedusaContainer) {
@@ -93,7 +96,7 @@ export default async function deactivateStaleBrands(container: MedusaContainer)
93
96
  filters: { is_active: true /* + your staleness condition */ },
94
97
  })
95
98
 
96
- // pass all ids at once the workflow handles the batch, not the job
99
+ // pass all ids at once, the workflow handles the batch, not the job
97
100
  await deactivateBrandsWorkflow(container).run({
98
101
  input: { ids: stale.map((b) => b.id) },
99
102
  })
@@ -109,19 +112,21 @@ export const config = {
109
112
 
110
113
  ### When to use a job vs a subscriber
111
114
 
115
+ Pick the trigger that matches how the work starts.
116
+
112
117
  | Trigger | Use |
113
118
  | --- | --- |
114
119
  | "Something happened" (a workflow emitted an event) | **Subscriber** |
115
120
  | "It's time" / "poll for anything that became ready" | **Scheduled job** |
116
121
 
117
- A time-based pipeline often combines both: a daily job finds records that became eligible and emits an event (say `brand.review_due`), and a *subscriber* turns each event into a workflow run. Polling for "what's ready" is the job; reacting to each item is the subscriber.
122
+ A time-based pipeline often combines both: a daily job finds records that became eligible and emits an event (say `brand.review_due`), and a subscriber turns each event into a workflow run. Polling for "what's ready" is the job. Reacting to each item is the subscriber.
118
123
 
119
124
  ### Job best practices
120
125
 
121
- - **Do the work in batches** and bound result sets a job that `SELECT`s an unbounded table will eventually time out. Page through with `LIMIT`/`OFFSET` or a cursor.
122
- - **Idempotent by design** a job re-runs on every tick; it must only act on records still needing action (filter on the not-yet-processed state).
123
- - **Mutations run workflows**, reads run Query same as everywhere.
124
- - **Log a summary** each run (how many processed) so drift is visible.
126
+ - **Batch and bound result sets:** a job that `SELECT`s an unbounded table will eventually time out. Page through with `LIMIT`/`OFFSET` or a cursor.
127
+ - **Idempotent by design:** a job re-runs on every tick, so it must only act on records still needing action (filter on the not-yet-processed state).
128
+ - **Mutations run workflows:** reads run Query, same as everywhere.
129
+ - **Log a summary:** report how many records processed each run so drift is visible.
125
130
 
126
131
  ## Checklist
127
132
 
@@ -131,3 +136,11 @@ A time-based pipeline often combines both: a daily job finds records that became
131
136
  - Job exports `{ name, schedule }`; cron is correct (UTC).
132
137
  - Job filters to records still needing work, batches large sets, and logs a summary.
133
138
  - Neither a subscriber nor a job writes to the database outside a workflow.
139
+
140
+ ## Next steps
141
+
142
+ <CardGroup cols={2}>
143
+ <Card title="Workflows" href="/rc/resources/best-practices/workflows">
144
+ Do all mutations through a workflow so subscribers and jobs stay thin triggers.
145
+ </Card>
146
+ </CardGroup>
@@ -1,31 +1,32 @@
1
1
  ---
2
- title: "Types & augmentation"
3
- description: "Type the panels against your own backend extensions — augment framework DTOs with declaration merging so every SDK endpoint carries your custom data."
2
+ title: "How to Use Shared Types"
3
+ sidebarTitle: "Use Shared Types"
4
+ description: "Type the panels against your own backend extensions by augmenting framework DTOs with declaration merging."
4
5
  ---
5
6
 
6
- The panels are fully typed against the API through `@mercurjs/types` and the typed SDK. When you extend the backend adding [custom fields](/rc/resources/best-practices/custom-fields), a [linked module](/rc/resources/best-practices/module-links), an extra field on a DTO those additions aren't in the shipped types yet. You close the gap in the **frontend** with a small declaration-merging `.d.ts` file. Do that once and *every* SDK call that returns the entity carries your field, typed.
7
+ The panels are fully typed against the API through `@mercurjs/types` and the typed SDK. When you extend the backend with [custom fields](/rc/resources/best-practices/custom-fields), a [linked module](/rc/resources/best-practices/module-links), or an extra field on a DTO, those additions are not in the shipped types yet. You close the gap in the frontend with a small declaration-merging `.d.ts` file. Write it once, and every SDK call that returns the entity carries your field, typed.
7
8
 
8
9
  <Warning>
9
- **Never use `any` to paper over a missing field.** Casting a response to `any` (or `as { custom_fields: … }` at each call site) throws away type-checking and has to be repeated everywhere. Augment the type once instead.
10
+ Never use `any` to paper over a missing field. Casting a response to `any`, or to `as { custom_fields: … }` at each call site, throws away type-checking and has to be repeated everywhere. Augment the type once instead.
10
11
  </Warning>
11
12
 
12
13
  ## The scenario: a custom field, typed end-to-end
13
14
 
14
- Say you added a custom field on the backend for example `is_featured` on `product` (see [Custom fields](/rc/resources/best-practices/custom-fields)). The value now comes back from the API, but the panel's `ProductDTO` doesn't know about it, so `product.is_featured` is a type error.
15
+ Say you added a custom field on the backend, such as `is_featured` on `product` (see [Custom fields](/rc/resources/best-practices/custom-fields)). The value now comes back from the API, but the panel's `ProductDTO` does not know about it, so `product.is_featured` is a type error.
15
16
 
16
17
  Fix it in the panel with a declaration-merging file.
17
18
 
18
19
  ### Why merging works here
19
20
 
20
- The `ProductDTO` the SDK returns ultimately resolves to Medusa's upstream `ProductDTO`, which is declared as an **`interface`** in `@medusajs/types`. Interfaces are open, so you can **merge into it** with `declare module "@medusajs/types"`.
21
+ The `ProductDTO` the SDK returns ultimately resolves to Medusa's upstream `ProductDTO`, which is declared as an `interface` in `@medusajs/types`. Interfaces are open, so you can merge into it with `declare module "@medusajs/types"`.
21
22
 
22
- Because everything downstream `@mercurjs/types`, the SDK response wrappers (`AdminProductResponse`, list responses), and the panel hooks refers back to that same interface, your added members appear in all of them at once. You augment in one place and every product-returning endpoint is typed.
23
+ Everything downstream refers back to that same interface: `@mercurjs/types`, the SDK response wrappers such as `AdminProductResponse` and list responses, and the panel hooks. Your added members appear in all of them at once. You augment in one place and every product-returning endpoint is typed.
23
24
 
24
25
  ### Add the `.d.ts` in the panel
25
26
 
26
- Drop a declaration file anywhere under the panel's `src/` (it's picked up by the app's `tsconfig`):
27
+ Drop a declaration file anywhere under the panel's `src/`. It is picked up by the app's `tsconfig`.
27
28
 
28
- ```ts title="apps/vendor/src/types/custom-fields.d.ts"
29
+ ```ts apps/vendor/src/types/custom-fields.d.ts
29
30
  import "@medusajs/types"
30
31
 
31
32
  declare module "@medusajs/types" {
@@ -38,17 +39,17 @@ declare module "@medusajs/types" {
38
39
  ```
39
40
 
40
41
  <Warning>
41
- Two rules or the augmentation silently does nothing:
42
+ Follow two rules, or the augmentation silently does nothing:
42
43
 
43
- - The module name in `declare module "..."` must be the package that declares the interface you're merging into here `@medusajs/types`, the owner of `UpstreamProductDTO`, **not** `@mercurjs/types` (which only aliases it).
44
- - The file must be a module. Add an `import "@medusajs/types"` (or a trailing `export {}`) so TypeScript treats it as one.
44
+ - The module name in `declare module "..."` must be the package that declares the interface you are merging into. Here that is `@medusajs/types`, the owner of `UpstreamProductDTO`, not `@mercurjs/types`, which only aliases it.
45
+ - The file must be a module. Add an `import "@medusajs/types"`, or a trailing `export {}`, so TypeScript treats it as one.
45
46
  </Warning>
46
47
 
47
48
  ### Now the whole SDK is typed
48
49
 
49
50
  With that one file in place, no cast is needed anywhere:
50
51
 
51
- ```ts title="Every product endpoint carries the field"
52
+ ```ts Every product endpoint carries the field
52
53
  const { products } = await sdk.vendor.products.query()
53
54
  products[0].custom_fields?.is_featured // ✅ typed, no cast
54
55
 
@@ -57,16 +58,16 @@ product.custom_fields?.is_featured // ✅ typed everywhere ProductDTO flows
57
58
  ```
58
59
 
59
60
  <Tip>
60
- Types and runtime are separate concerns: this `.d.ts` makes the field *typed*, but it only *arrives* if the fetch asks for it. Let the [custom-fields `link` / registry merge](/rc/resources/best-practices/custom-fields#the-extension-api-link-property) add the fields to the built-in panel fetches rather than hand-adding `+field.*` the vendor product query in particular rejects arbitrary `*`-relation overrides.
61
+ Types and runtime are separate concerns. This `.d.ts` makes the field typed, but it only arrives if the fetch asks for it. Let the [custom-fields `link` / registry merge](/rc/resources/best-practices/custom-fields#the-extension-api-link-property) add the fields to the built-in panel fetches rather than hand-adding `+field.*`. The vendor product query in particular rejects arbitrary `*`-relation overrides.
61
62
  </Tip>
62
63
 
63
64
  ## Linked data resolves the same way
64
65
 
65
- The augmentation isn't limited to a custom field's own value it's how you make **linked-module data** typed too. When a [custom-fields config](/rc/resources/best-practices/custom-fields#the-extension-api-link-property) declares a `link`, the panel fetches that module's data alongside the entity ([SPEC-021](/rc/references/panel-extension-api)): `link: "brand"` merges `brand.*` into the built-in product fetch for you no hand-written field list.
66
+ The augmentation is not limited to a custom field's own value. It is how you make linked-module data typed too. When a [custom-fields config](/rc/resources/best-practices/custom-fields#the-extension-api-link-property) declares a `link`, the panel fetches that module's data alongside the entity (see [panel extensions](/references/panel-extensions/overview)). A `link: "brand"` merges `brand.*` into the built-in product fetch for you, with no hand-written field list.
66
67
 
67
- Pair that one config line with a matching augmentation, and the linked data is both **present at runtime** and **typed everywhere `ProductDTO` is imported**:
68
+ Pair that one config line with a matching augmentation, and the linked data is both present at runtime and typed everywhere `ProductDTO` is imported.
68
69
 
69
- ```ts title="src/custom-fields/product.tsx declare the link (runtime)"
70
+ ```ts src/custom-fields/product.tsx: declare the link (runtime)
70
71
  export default defineCustomFieldsConfig({
71
72
  model: "product",
72
73
  link: "brand", // brand.* is fetched with every product
@@ -78,7 +79,7 @@ export default defineCustomFieldsConfig({
78
79
  })
79
80
  ```
80
81
 
81
- ```ts title="src/types/brand.d.ts declare the shape (types)"
82
+ ```ts src/types/brand.d.ts: declare the shape (types)
82
83
  import "@medusajs/types"
83
84
 
84
85
  declare module "@medusajs/types" {
@@ -88,7 +89,7 @@ declare module "@medusajs/types" {
88
89
  }
89
90
  ```
90
91
 
91
- Now any code that imports `ProductDTO` a page, a hook, a column renderer sees `product.brand` resolved, with the data already fetched by the `link`:
92
+ Now any code that imports `ProductDTO`, whether a page, a hook, or a column renderer, sees `product.brand` resolved, with the data already fetched by the `link`:
92
93
 
93
94
  ```ts
94
95
  const { product } = await sdk.vendor.products.$id.query({ $id: id })
@@ -96,7 +97,7 @@ product.brand?.name // ✅ present (via link) and typed (via augmentation)
96
97
  ```
97
98
 
98
99
  <Tip>
99
- The `link` does the fetching, the `.d.ts` does the typing you write each once, per model, and every product-returning endpoint in the panel is covered. This is the payoff of augmentation: register the relationship in one place, consume it as a plain typed property everywhere.
100
+ The `link` does the fetching, the `.d.ts` does the typing. You write each once, per model, and every product-returning endpoint in the panel is covered. This is the payoff of augmentation: register the relationship in one place, then consume it as a plain typed property everywhere.
100
101
  </Tip>
101
102
 
102
103
  ## Where each type goes
@@ -108,13 +109,27 @@ product.brand?.name // ✅ present (via link) and typed (via augmentation)
108
109
  | A brand-new Mercur/domain DTO | The domain folder in `@mercurjs/types`, re-exported from `index.ts` |
109
110
 
110
111
  <Warning>
111
- DTOs and enums the platform already ships (`ProductDTO`, `SellerStatus`, `MercurModules`, `HttpTypes`) are imported from `@mercurjs/types` never redeclared. In the dashboards, `HttpTypes` comes from `@mercurjs/types` too, which is what keeps request/response types aligned with Mercur's extended routes.
112
+ DTOs and enums the platform already ships (`ProductDTO`, `SellerStatus`, `MercurModules`, `HttpTypes`) are imported from `@mercurjs/types`, never redeclared. In the dashboards, `HttpTypes` comes from `@mercurjs/types` too, which is what keeps request/response types aligned with Mercur's extended routes.
112
113
  </Warning>
113
114
 
114
115
  ## Checklist
115
116
 
116
117
  - No `any`, and no per-call-site casts for extended data.
117
- - Backend additions typed in the panel via a `.d.ts` merging into the **framework** interface that owns the DTO.
118
- - Augmentation files name the correct package and are real modules (`import`/`export {}`).
118
+ - Backend additions typed in the panel via a `.d.ts` merging into the framework interface that owns the DTO.
119
+ - Augmentation files name the correct package and are real modules (`import` / `export {}`).
119
120
  - Extended fields are requested with `+field.*` so they actually arrive.
120
121
  - Shipped types imported from `@mercurjs/types`; one-off shapes inferred from Zod.
122
+
123
+ ## Next steps
124
+
125
+ <CardGroup cols={2}>
126
+ <Card title="Custom fields" href="/rc/resources/best-practices/custom-fields">
127
+ Add fields, rows, actions, and columns to a built-in model, and declare a `link`.
128
+ </Card>
129
+ <Card title="Module links" href="/rc/resources/best-practices/module-links">
130
+ Link a custom module to a built-in entity and fetch its data alongside.
131
+ </Card>
132
+ <Card title="Panel extensions" href="/references/panel-extensions/overview">
133
+ See how the panel fetches linked module data alongside the entity.
134
+ </Card>
135
+ </CardGroup>
@@ -1,21 +1,22 @@
1
1
  ---
2
- title: "Workflows"
3
- description: "Where all business logic and mutations live — composition-function constraints, one-mutation-per-step with compensation, reusing built-in steps, and the query engine."
2
+ title: "How to Create a Workflow"
3
+ sidebarTitle: "Create a Workflow"
4
+ description: "Coordinate a business operation across modules as workflow steps, each with automatic rollback, reusing built-in steps and reading through the query engine."
4
5
  ---
5
6
 
6
- A workflow is the orchestration layer: it coordinates a business operation across one or more modules as a series of **steps**, with automatic rollback (**compensation**) if any step fails. Every mutation in Mercur runs inside a workflow this is the single most important rule in the [architecture](/rc/resources/best-practices/overview).
7
+ A workflow is the orchestration layer. It coordinates a business operation across one or more modules as a series of **steps**, with automatic rollback (**compensation**) when any step fails. Every mutation in Mercur runs inside a workflow. This is the single most important rule in the [architecture](/rc/resources/best-practices/overview).
7
8
 
8
9
  <Warning>
9
- **All mutations go through a workflow.** API routes, subscribers, and scheduled jobs never write to the database directly they *run a workflow*. That is what gives every mutation validation, event emission, and rollback.
10
+ **All mutations go through a workflow.** API routes, subscribers, and scheduled jobs never write to the database directly. They run a workflow. That is what gives every mutation validation, event emission, and rollback.
10
11
  </Warning>
11
12
 
12
13
  <Note>
13
- Workflows are a [Medusa framework primitive](https://docs.medusajs.com/learn/fundamentals/workflows). This page focuses on the constraints and conventions that trip people (and agents) up.
14
+ Workflows are a [Medusa framework primitive](https://docs.medusajs.com/learn/fundamentals/workflows). This page focuses on the constraints and conventions that trip people, and agents, up.
14
15
  </Note>
15
16
 
16
17
  ## The composition function is not normal JavaScript
17
18
 
18
- The function you pass to `createWorkflow` is a **composition function**. It runs once at build time to wire steps together it does **not** execute your business logic at request time. Because of that, it has hard constraints:
19
+ The function you pass to `createWorkflow` is a **composition function**. It runs once at build time to wire steps together. It does **not** execute your business logic at request time. Because of that, it has hard constraints.
19
20
 
20
21
  <Warning>
21
22
  Inside a `createWorkflow` composition function you must **not**:
@@ -26,12 +27,12 @@ The function you pass to `createWorkflow` is a **composition function**. It runs
26
27
  - use `new Date()`, `Math.random()`, or any non-deterministic call
27
28
  - access properties of a step's output directly (e.g. `result.id`)
28
29
 
29
- These run at composition time, not execution time, so they either do nothing useful or break replay/rollback.
30
+ These run at composition time, not execution time, so they either do nothing useful or break replay and rollback.
30
31
  </Warning>
31
32
 
32
33
  Anything that looks like normal logic goes into a **step** (for side effects) or a **`transform`** (for shaping data between steps):
33
34
 
34
- ```ts title="src/workflows/create-brands.ts"
35
+ ```ts src/workflows/create-brands.ts
35
36
  import {
36
37
  createWorkflow,
37
38
  transform,
@@ -56,14 +57,14 @@ export const createBrandsWorkflow = createWorkflow(
56
57
  ```
57
58
 
58
59
  <Tip>
59
- Need a conditional or a computed value? Use `transform` to derive data, `when` to run a step conditionally, and put date/random/id generation **inside a step**. Never branch in the composition body itself.
60
+ Need a conditional or a computed value? Use `transform` to derive data, `when` to run a step conditionally, and put date, random, or id generation **inside a step**. Never branch in the composition body itself.
60
61
  </Tip>
61
62
 
62
- ## One mutation per step + compensation
63
+ ## One mutation per step and compensation
63
64
 
64
- A step is the unit of work and the unit of rollback. The rule: **each step performs a single mutation and defines how to undo it.** `createStep` takes an invoke function and a compensation function; the invoke returns a `StepResponse` whose second argument is the data the compensation needs.
65
+ A step is the unit of work and the unit of rollback. The rule: **each step performs a single mutation and defines how to undo it.** `createStep` takes an invoke function and a compensation function. The invoke returns a `StepResponse` whose second argument is the data the compensation needs.
65
66
 
66
- ```ts title="src/workflows/steps/create-brands.ts"
67
+ ```ts src/workflows/steps/create-brands.ts
67
68
  import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
68
69
  import BrandModuleService from "../../modules/brand/service"
69
70
  import { BRAND_MODULE } from "../../modules/brand"
@@ -86,7 +87,7 @@ export const createBrandsStep = createStep(
86
87
  )
87
88
  ```
88
89
 
89
- If a later step in the workflow throws, Medusa runs the compensation functions of the already-completed steps in reverse the `createBrands` above is undone by `deleteBrands`. Splitting mutations one-per-step is what makes this reliable: a step that does two writes can only half-compensate.
90
+ When a later step in the workflow throws, Medusa runs the compensation functions of the already-completed steps in reverse. The `createBrands` above is undone by `deleteBrands`. Splitting mutations one per step is what makes this reliable: a step that does two writes can only half-compensate.
90
91
 
91
92
  ## Reuse built-in steps
92
93
 
@@ -98,7 +99,7 @@ Don't hand-roll what the framework already ships. Medusa's `core-flows` exports
98
99
  | Emit a domain event | `emitEventStep` |
99
100
  | Call another workflow as a step | `otherWorkflow.runAsStep({ input })` |
100
101
 
101
- ```ts title="Composing built-in steps + a sub-workflow"
102
+ ```ts
102
103
  import { emitEventStep } from "@medusajs/medusa/core-flows"
103
104
 
104
105
  export const createBrandsWorkflow = createWorkflow(
@@ -118,12 +119,12 @@ export const createBrandsWorkflow = createWorkflow(
118
119
  ```
119
120
 
120
121
  <Tip>
121
- Prefer `runAsStep` over duplicating logic. If two workflows need the same sequence, extract it into its own workflow and call it as a step from both you get one place to maintain and correct compensation for free.
122
+ Prefer `runAsStep` over duplicating logic. When two workflows need the same sequence, extract it into its own workflow and call it as a step from both. You get one place to maintain, and correct compensation for free.
122
123
  </Tip>
123
124
 
124
- ## Hooks let others extend your workflow
125
+ ## Hooks let others extend your workflow
125
126
 
126
- Expose extension points with `createHook` so consumers can inject behaviour (validation, side effects) without forking the workflow. Add a `validate` hook before the mutation and a `brandsCreated` hook after it:
127
+ Expose extension points with `createHook` so consumers can inject behaviour such as validation or side effects without forking the workflow. Add a `validate` hook before the mutation and a `brandsCreated` hook after it:
127
128
 
128
129
  ```ts
129
130
  const validate = createHook("validate", { input })
@@ -140,13 +141,13 @@ return new WorkflowResponse(brands, {
140
141
  })
141
142
  ```
142
143
 
143
- Consumers register a handler on the hook to run custom logic at that point. This is the sanctioned way to extend a workflow see [Extend a workflow](/rc/resources/customization/extend-a-workflow).
144
+ Consumers register a handler on the hook to run custom logic at that point. This is the sanctioned way to extend a workflow. See [Extend a workflow](/rc/resources/customization/extend-a-workflow).
144
145
 
145
146
  ## The query engine
146
147
 
147
- Reads inside a step (and anywhere else) go through **Query** the graph engine that resolves data across modules and links. Resolve it from the container and call `query.graph`:
148
+ Reads inside a step, and anywhere else, go through **Query**, the graph engine that resolves data across modules and links. Resolve it from the container and call `query.graph`:
148
149
 
149
- ```ts title="Reading across modules inside a step"
150
+ ```ts
150
151
  import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
151
152
 
152
153
  const query = container.resolve(ContainerRegistrationKeys.QUERY)
@@ -159,7 +160,7 @@ const { data: brands } = await query.graph({
159
160
  ```
160
161
 
161
162
  <Warning>
162
- Query is for **reads**. Never try to mutate through it, and never resolve another module's service inside a step to read its data go through Query so module isolation and links are respected.
163
+ Query is for **reads**. Never try to mutate through it, and never resolve another module's service inside a step to read its data. Go through Query so module isolation and links are respected.
163
164
  </Warning>
164
165
 
165
166
  ## Checklist for a workflow
@@ -171,3 +172,14 @@ const { data: brands } = await query.graph({
171
172
  - Built-in steps (`createRemoteLinkStep`, `emitEventStep`) and `runAsStep` are reused instead of reimplemented.
172
173
  - Reads use `query.graph`; no cross-module service calls.
173
174
  - Extension points are exposed as hooks, not by forking.
175
+
176
+ ## Next steps
177
+
178
+ <CardGroup cols={2}>
179
+ <Card title="Extend a workflow" href="/rc/resources/customization/extend-a-workflow">
180
+ Register handlers on a workflow's hooks to add behaviour without forking.
181
+ </Card>
182
+ <Card title="Best practices overview" href="/rc/resources/best-practices/overview">
183
+ See how workflows fit the wider Mercur architecture.
184
+ </Card>
185
+ </CardGroup>