Overview
The Promotion Module manages promotional campaigns, discount codes, and automated promotions. It provides rule-based promotion targeting and supports various discount types including percentage, fixed amount, and free shipping.
Key Features:
Promotional campaigns with budgets
Discount codes and automatic promotions
Rule-based targeting (customer groups, products, etc.)
Multiple application methods (order, item, shipping)
Budget tracking and limits
Time-based promotions
Usage limits per customer
Buy X Get Y promotions
When to Use
Use the Promotion Module when you need to:
Create discount codes for customers
Run promotional campaigns
Offer free shipping promotions
Implement BOGO (Buy One Get One) offers
Target specific customer groups
Set promotion budgets and limits
Schedule time-limited sales
Track promotion usage
Data Models
Represents a discount or promotional offer.
Unique promotion identifier
Discount code (e.g., “SUMMER20”)
Whether promotion applies automatically (default: false)
ID of the associated campaign
ApplicationMethod
Defines how and where a promotion discount applies.
Unique application method identifier
Method type: fixed, percentage
What to discount: order, items, shipping_methods
Discount value (amount for fixed, percentage for percentage)
Maximum items to discount
Number of items to apply discount to
Minimum quantity to buy (for buyget promotions)
How to allocate: each, across
Defines targeting conditions for promotions.
Rule attribute (e.g., “customer_group_id”, “product_id”)
Comparison operator: eq, ne, in, gt, gte, lt, lte
values
PromotionRuleValue[]
required
Rule values to match
Campaign
Groups promotions with budget management.
Unique campaign identifier
Unique campaign identifier (e.g., “summer-2024”)
Campaign budget configuration
Promotions in this campaign
CampaignBudget
Tracks campaign budget and usage.
Budget type: spend, usage
Service Interface
The Promotion Module service is available at @medusajs/medusa/promotion.
Create a new promotion.
Percentage Discount
Fixed Amount
Free Shipping
import { MedusaRequest , MedusaResponse } from "@medusajs/framework/http"
import { IPromotionModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
export async function POST (
req : MedusaRequest ,
res : MedusaResponse
) {
const promotionModuleService : IPromotionModuleService = req . scope . resolve (
Modules . PROMOTION
)
const promotion = await promotionModuleService . createPromotions ({
code: "SUMMER20" ,
type: "standard" ,
is_automatic: false ,
application_method: {
type: "percentage" ,
target_type: "order" ,
value: 20 , // 20% off
},
rules: [
{
attribute: "customer_group_id" ,
operator: "in" ,
values: [ "cgroup_retail" ],
},
],
})
res . json ({ promotion })
}
const promotion = await promotionModuleService . createPromotions ({
code: "SAVE10" ,
type: "standard" ,
application_method: {
type: "fixed" ,
target_type: "order" ,
value: 1000 , // $10 off
},
})
const promotion = await promotionModuleService . createPromotions ({
code: "FREESHIP" ,
type: "standard" ,
application_method: {
type: "fixed" ,
target_type: "shipping_methods" ,
value: 100 , // 100% discount on shipping
},
rules: [
{
attribute: "subtotal" ,
operator: "gte" ,
values: [ 5000 ], // Minimum $50 order
},
],
})
data
CreatePromotionDTO | CreatePromotionDTO[]
required
Promotion data Discount code (required for non-automatic promotions)
Promotion type: standard, buyget
Whether promotion applies automatically
application_method
CreateApplicationMethodDTO
required
How to apply the discount
promotion
PromotionDTO | PromotionDTO[]
The created promotion(s)
Create Campaign
Create a promotional campaign with budget.
const campaign = await promotionModuleService . createCampaigns ({
name: "Summer Sale 2024" ,
description: "Annual summer promotion" ,
campaign_identifier: "summer-2024" ,
starts_at: new Date ( "2024-06-01" ),
ends_at: new Date ( "2024-08-31" ),
budget: {
type: "spend" ,
limit: 10000 , // $100 budget
},
})
data
CreateCampaignDTO | CreateCampaignDTO[]
required
Associate a promotion with a campaign.
const promotion = await promotionModuleService . updatePromotions (
"promo_123" ,
{
campaign_id: "camp_summer2024" ,
}
)
Compute Actions
Calculate promotion discounts for a cart or order.
const result = await promotionModuleService . computeActions (
[ "promo_summer20" ],
{
items: [
{
id: "item_123" ,
product_id: "prod_456" ,
variant_id: "variant_789" ,
quantity: 2 ,
subtotal: 4000 ,
},
],
shipping_methods: [
{
id: "sm_123" ,
subtotal: 500 ,
},
],
}
)
for ( const action of result . actions ) {
console . log ( `Apply ${ action . amount } discount to ${ action . item_id } ` )
}
IDs of promotions to compute
Computed discount actions
Register Usage
Track promotion usage.
await promotionModuleService . registerUsage ({
promotion_id: "promo_123" ,
amount: 1000 ,
customer_id: "cus_456" ,
})
Revert Usage
Revert promotion usage (e.g., on order cancellation).
await promotionModuleService . revertUsage ({
promotion_id: "promo_123" ,
amount: 1000 ,
})
Basic discount promotions.
Percentage Off
Fixed Amount Off
Item Discount
{
code : "SAVE20" ,
type : "standard" ,
application_method : {
type : "percentage" ,
target_type : "order" ,
value : 20 ,
},
}
{
code : "10OFF" ,
type : "standard" ,
application_method : {
type : "fixed" ,
target_type : "order" ,
value : 1000 ,
},
}
{
code : "ITEM5OFF" ,
type : "standard" ,
application_method : {
type : "fixed" ,
target_type : "items" ,
value : 500 ,
allocation : "each" , // $5 off each item
},
rules : [
{
attribute: "product_id" ,
operator: "in" ,
values: [ "prod_123" , "prod_456" ],
},
],
}
Buy X Get Y (BOGO)
Buy 2 Get 1 Free
Buy 3 Get 50% Off 4th
{
code : "BOGO" ,
type : "buyget" ,
application_method : {
type : "percentage" ,
target_type : "items" ,
value : 100 , // 100% off
buy_rules_min_quantity : 2 ,
apply_to_quantity : 1 ,
},
rules : [
{
attribute: "product_id" ,
operator: "in" ,
values: [ "prod_tshirt" ],
},
],
}
{
code : "BUY3GET50" ,
type : "buyget" ,
application_method : {
type : "percentage" ,
target_type : "items" ,
value : 50 ,
buy_rules_min_quantity : 3 ,
apply_to_quantity : 1 ,
},
}
{
type : "standard" ,
is_automatic : true ,
application_method : {
type : "percentage" ,
target_type : "shipping_methods" ,
value : 100 ,
},
rules : [
{
attribute: "subtotal" ,
operator: "gte" ,
values: [ 5000 ], // Free shipping over $50
},
],
}
Rule Examples
Customer Group Targeting
rules : [
{
attribute: "customer_group_id" ,
operator: "in" ,
values: [ "cgroup_vip" ],
},
]
Product Targeting
Specific Products
Product Category
rules : [
{
attribute: "product_id" ,
operator: "in" ,
values: [ "prod_123" , "prod_456" ],
},
]
rules : [
{
attribute: "product_category_id" ,
operator: "in" ,
values: [ "pcat_summer" ],
},
]
Order Value Targeting
rules : [
{
attribute: "subtotal" ,
operator: "gte" ,
values: [ 10000 ], // $100 minimum
},
]
Integration Examples
With Cart Module
Apply promotions to cart.
import { Modules } from "@medusajs/framework/utils"
const cartModule = container . resolve ( Modules . CART )
const promotionModule = container . resolve ( Modules . PROMOTION )
// Get cart
const cart = await cartModule . retrieveCart ( "cart_123" , {
relations: [ "items" ],
})
// Find applicable promotions
const promotions = await promotionModule . listPromotions ({
code: "SUMMER20" ,
})
// Compute discounts
const result = await promotionModule . computeActions (
promotions . map ( p => p . id ),
{
items: cart . items ,
customer: { id: cart . customer_id },
}
)
// Apply as line item adjustments
for ( const action of result . actions ) {
await cartModule . createLineItemAdjustments ({
item_id: action . item_id ,
amount: - action . amount ,
code: promotions [ 0 ]. code ,
})
}
With Customer Module
Customer group promotions.
import { Modules } from "@medusajs/framework/utils"
const customerModule = container . resolve ( Modules . CUSTOMER )
const promotionModule = container . resolve ( Modules . PROMOTION )
// Get customer groups
const customer = await customerModule . retrieveCustomer ( "cus_123" , {
relations: [ "groups" ],
})
// Find promotions for customer groups
const promotions = await promotionModule . listPromotions ({
is_automatic: true ,
rules: {
attribute: "customer_group_id" ,
value: customer . groups . map ( g => g . id ),
},
})
Best Practices
Code Uniqueness : Promotion codes must be unique. Use descriptive codes that are easy for customers to remember.
Automatic vs Manual : Use automatic promotions for always-on offers (e.g., free shipping over $50). Use codes for targeted campaigns.
Rule Combinations : Rules are AND conditions. All rules must match for a promotion to apply.
Budget Tracking : Set campaign budgets to control promotion costs. Monitor usage regularly.
Target Specificity : Use specific targeting rules to prevent unintended discount application.
Testing : Always test promotions with sample carts before making them active.
Allocation :
each: Apply discount to each matching item individually
across: Apply total discount split across all matching items