Skip to main content

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

Promotion

Represents a discount or promotional offer.
id
string
required
Unique promotion identifier
code
string
Discount code (e.g., “SUMMER20”)
type
PromotionType
required
Type: standard, buyget
is_automatic
boolean
required
Whether promotion applies automatically (default: false)
campaign_id
string
ID of the associated campaign
application_method
ApplicationMethod
How discount is applied
rules
PromotionRule[]
Targeting rules

ApplicationMethod

Defines how and where a promotion discount applies.
id
string
required
Unique application method identifier
type
string
required
Method type: fixed, percentage
target_type
string
required
What to discount: order, items, shipping_methods
value
BigNumber
required
Discount value (amount for fixed, percentage for percentage)
max_quantity
number
Maximum items to discount
apply_to_quantity
number
Number of items to apply discount to
buy_rules_min_quantity
number
Minimum quantity to buy (for buyget promotions)
allocation
string
How to allocate: each, across

PromotionRule

Defines targeting conditions for promotions.
id
string
required
Unique rule identifier
promotion_id
string
required
ID of the promotion
attribute
string
required
Rule attribute (e.g., “customer_group_id”, “product_id”)
operator
string
required
Comparison operator: eq, ne, in, gt, gte, lt, lte
values
PromotionRuleValue[]
required
Rule values to match

Campaign

Groups promotions with budget management.
id
string
required
Unique campaign identifier
name
string
required
Campaign name
description
string
Campaign description
currency
string
Campaign currency code
campaign_identifier
string
required
Unique campaign identifier (e.g., “summer-2024”)
starts_at
DateTime
When campaign starts
ends_at
DateTime
When campaign ends
budget
CampaignBudget
Campaign budget configuration
promotions
Promotion[]
Promotions in this campaign

CampaignBudget

Tracks campaign budget and usage.
id
string
required
Unique budget identifier
type
string
Budget type: spend, usage
limit
BigNumber
Budget limit
used
BigNumber
Amount used so far

Service Interface

The Promotion Module service is available at @medusajs/medusa/promotion.

Create Promotion

Create a new promotion.
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 })
}
data
CreatePromotionDTO | CreatePromotionDTO[]
required
Promotion data
code
string
Discount code (required for non-automatic promotions)
type
PromotionType
required
Promotion type: standard, buyget
is_automatic
boolean
Whether promotion applies automatically
application_method
CreateApplicationMethodDTO
required
How to apply the discount
rules
CreatePromotionRuleDTO[]
Targeting rules
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
Campaign data
name
string
required
Campaign name
campaign_identifier
string
required
Unique identifier
starts_at
Date
Campaign start date
ends_at
Date
Campaign end date
budget
CreateCampaignBudgetDTO
Budget configuration

Add Promotion to Campaign

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}`)
}
promotionIds
string[]
required
IDs of promotions to compute
context
object
required
Cart/order context
items
object[]
Cart or order items
shipping_methods
object[]
Shipping methods
customer
object
Customer data
result
ComputeActionsResult
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,
})

Promotion Types

Standard Promotions

Basic discount promotions.
{
  code: "SAVE20",
  type: "standard",
  application_method: {
    type: "percentage",
    target_type: "order",
    value: 20,
  },
}

Buy X Get Y (BOGO)

{
  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"],
    },
  ],
}

Automatic Promotions

{
  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

rules: [
  {
    attribute: "product_id",
    operator: "in",
    values: ["prod_123", "prod_456"],
  },
]

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

  1. Code Uniqueness: Promotion codes must be unique. Use descriptive codes that are easy for customers to remember.
  2. Automatic vs Manual: Use automatic promotions for always-on offers (e.g., free shipping over $50). Use codes for targeted campaigns.
  3. Rule Combinations: Rules are AND conditions. All rules must match for a promotion to apply.
  4. Budget Tracking: Set campaign budgets to control promotion costs. Monitor usage regularly.
  5. Target Specificity: Use specific targeting rules to prevent unintended discount application.
  6. Testing: Always test promotions with sample carts before making them active.
  7. Allocation:
    • each: Apply discount to each matching item individually
    • across: Apply total discount split across all matching items

Build docs developers (and LLMs) love