> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/medusajs/medusa/llms.txt
> Use this file to discover all available pages before exploring further.

# Promotion Module

> Learn about the Promotion Module and how to create and manage promotions, discounts, and campaigns in Medusa.

## 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.

<ResponseField name="id" type="string" required>
  Unique promotion identifier
</ResponseField>

<ResponseField name="code" type="string">
  Discount code (e.g., "SUMMER20")
</ResponseField>

<ResponseField name="type" type="PromotionType" required>
  Type: `standard`, `buyget`
</ResponseField>

<ResponseField name="is_automatic" type="boolean" required>
  Whether promotion applies automatically (default: false)
</ResponseField>

<ResponseField name="campaign_id" type="string">
  ID of the associated campaign
</ResponseField>

<ResponseField name="application_method" type="ApplicationMethod">
  How discount is applied
</ResponseField>

<ResponseField name="rules" type="PromotionRule[]">
  Targeting rules
</ResponseField>

### ApplicationMethod

Defines how and where a promotion discount applies.

<ResponseField name="id" type="string" required>
  Unique application method identifier
</ResponseField>

<ResponseField name="type" type="string" required>
  Method type: `fixed`, `percentage`
</ResponseField>

<ResponseField name="target_type" type="string" required>
  What to discount: `order`, `items`, `shipping_methods`
</ResponseField>

<ResponseField name="value" type="BigNumber" required>
  Discount value (amount for fixed, percentage for percentage)
</ResponseField>

<ResponseField name="max_quantity" type="number">
  Maximum items to discount
</ResponseField>

<ResponseField name="apply_to_quantity" type="number">
  Number of items to apply discount to
</ResponseField>

<ResponseField name="buy_rules_min_quantity" type="number">
  Minimum quantity to buy (for buyget promotions)
</ResponseField>

<ResponseField name="allocation" type="string">
  How to allocate: `each`, `across`
</ResponseField>

### PromotionRule

Defines targeting conditions for promotions.

<ResponseField name="id" type="string" required>
  Unique rule identifier
</ResponseField>

<ResponseField name="promotion_id" type="string" required>
  ID of the promotion
</ResponseField>

<ResponseField name="attribute" type="string" required>
  Rule attribute (e.g., "customer\_group\_id", "product\_id")
</ResponseField>

<ResponseField name="operator" type="string" required>
  Comparison operator: `eq`, `ne`, `in`, `gt`, `gte`, `lt`, `lte`
</ResponseField>

<ResponseField name="values" type="PromotionRuleValue[]" required>
  Rule values to match
</ResponseField>

### Campaign

Groups promotions with budget management.

<ResponseField name="id" type="string" required>
  Unique campaign identifier
</ResponseField>

<ResponseField name="name" type="string" required>
  Campaign name
</ResponseField>

<ResponseField name="description" type="string">
  Campaign description
</ResponseField>

<ResponseField name="currency" type="string">
  Campaign currency code
</ResponseField>

<ResponseField name="campaign_identifier" type="string" required>
  Unique campaign identifier (e.g., "summer-2024")
</ResponseField>

<ResponseField name="starts_at" type="DateTime">
  When campaign starts
</ResponseField>

<ResponseField name="ends_at" type="DateTime">
  When campaign ends
</ResponseField>

<ResponseField name="budget" type="CampaignBudget">
  Campaign budget configuration
</ResponseField>

<ResponseField name="promotions" type="Promotion[]">
  Promotions in this campaign
</ResponseField>

### CampaignBudget

Tracks campaign budget and usage.

<ResponseField name="id" type="string" required>
  Unique budget identifier
</ResponseField>

<ResponseField name="type" type="string">
  Budget type: `spend`, `usage`
</ResponseField>

<ResponseField name="limit" type="BigNumber">
  Budget limit
</ResponseField>

<ResponseField name="used" type="BigNumber">
  Amount used so far
</ResponseField>

## Service Interface

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

### Create Promotion

Create a new promotion.

<CodeGroup>
  ```typescript Percentage Discount theme={null}
  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 })
  }
  ```

  ```typescript Fixed Amount theme={null}
  const promotion = await promotionModuleService.createPromotions({
    code: "SAVE10",
    type: "standard",
    application_method: {
      type: "fixed",
      target_type: "order",
      value: 1000, // $10 off
    },
  })
  ```

  ```typescript Free Shipping theme={null}
  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
      },
    ],
  })
  ```
</CodeGroup>

<ParamField path="data" type="CreatePromotionDTO | CreatePromotionDTO[]" required>
  Promotion data

  <ParamField path="code" type="string">
    Discount code (required for non-automatic promotions)
  </ParamField>

  <ParamField path="type" type="PromotionType" required>
    Promotion type: `standard`, `buyget`
  </ParamField>

  <ParamField path="is_automatic" type="boolean">
    Whether promotion applies automatically
  </ParamField>

  <ParamField path="application_method" type="CreateApplicationMethodDTO" required>
    How to apply the discount
  </ParamField>

  <ParamField path="rules" type="CreatePromotionRuleDTO[]">
    Targeting rules
  </ParamField>
</ParamField>

<ResponseField name="promotion" type="PromotionDTO | PromotionDTO[]">
  The created promotion(s)
</ResponseField>

### Create Campaign

Create a promotional campaign with budget.

<CodeGroup>
  ```typescript Example theme={null}
  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
    },
  })
  ```
</CodeGroup>

<ParamField path="data" type="CreateCampaignDTO | CreateCampaignDTO[]" required>
  Campaign data

  <ParamField path="name" type="string" required>
    Campaign name
  </ParamField>

  <ParamField path="campaign_identifier" type="string" required>
    Unique identifier
  </ParamField>

  <ParamField path="starts_at" type="Date">
    Campaign start date
  </ParamField>

  <ParamField path="ends_at" type="Date">
    Campaign end date
  </ParamField>

  <ParamField path="budget" type="CreateCampaignBudgetDTO">
    Budget configuration
  </ParamField>
</ParamField>

### Add Promotion to Campaign

Associate a promotion with a campaign.

<CodeGroup>
  ```typescript Example theme={null}
  const promotion = await promotionModuleService.updatePromotions(
    "promo_123",
    {
      campaign_id: "camp_summer2024",
    }
  )
  ```
</CodeGroup>

### Compute Actions

Calculate promotion discounts for a cart or order.

<CodeGroup>
  ```typescript Example theme={null}
  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}`)
  }
  ```
</CodeGroup>

<ParamField path="promotionIds" type="string[]" required>
  IDs of promotions to compute
</ParamField>

<ParamField path="context" type="object" required>
  Cart/order context

  <ParamField path="items" type="object[]">
    Cart or order items
  </ParamField>

  <ParamField path="shipping_methods" type="object[]">
    Shipping methods
  </ParamField>

  <ParamField path="customer" type="object">
    Customer data
  </ParamField>
</ParamField>

<ResponseField name="result" type="ComputeActionsResult">
  Computed discount actions
</ResponseField>

### Register Usage

Track promotion usage.

<CodeGroup>
  ```typescript Example theme={null}
  await promotionModuleService.registerUsage({
    promotion_id: "promo_123",
    amount: 1000,
    customer_id: "cus_456",
  })
  ```
</CodeGroup>

### Revert Usage

Revert promotion usage (e.g., on order cancellation).

<CodeGroup>
  ```typescript Example theme={null}
  await promotionModuleService.revertUsage({
    promotion_id: "promo_123",
    amount: 1000,
  })
  ```
</CodeGroup>

## Promotion Types

### Standard Promotions

Basic discount promotions.

<CodeGroup>
  ```typescript Percentage Off theme={null}
  {
    code: "SAVE20",
    type: "standard",
    application_method: {
      type: "percentage",
      target_type: "order",
      value: 20,
    },
  }
  ```

  ```typescript Fixed Amount Off theme={null}
  {
    code: "10OFF",
    type: "standard",
    application_method: {
      type: "fixed",
      target_type: "order",
      value: 1000,
    },
  }
  ```

  ```typescript Item Discount theme={null}
  {
    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"],
      },
    ],
  }
  ```
</CodeGroup>

### Buy X Get Y (BOGO)

<CodeGroup>
  ```typescript Buy 2 Get 1 Free theme={null}
  {
    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"],
      },
    ],
  }
  ```

  ```typescript Buy 3 Get 50% Off 4th theme={null}
  {
    code: "BUY3GET50",
    type: "buyget",
    application_method: {
      type: "percentage",
      target_type: "items",
      value: 50,
      buy_rules_min_quantity: 3,
      apply_to_quantity: 1,
    },
  }
  ```
</CodeGroup>

### Automatic Promotions

<CodeGroup>
  ```typescript Free Shipping (Auto) theme={null}
  {
    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
      },
    ],
  }
  ```
</CodeGroup>

## Rule Examples

### Customer Group Targeting

<CodeGroup>
  ```typescript VIP Customers Only theme={null}
  rules: [
    {
      attribute: "customer_group_id",
      operator: "in",
      values: ["cgroup_vip"],
    },
  ]
  ```
</CodeGroup>

### Product Targeting

<CodeGroup>
  ```typescript Specific Products theme={null}
  rules: [
    {
      attribute: "product_id",
      operator: "in",
      values: ["prod_123", "prod_456"],
    },
  ]
  ```

  ```typescript Product Category theme={null}
  rules: [
    {
      attribute: "product_category_id",
      operator: "in",
      values: ["pcat_summer"],
    },
  ]
  ```
</CodeGroup>

### Order Value Targeting

<CodeGroup>
  ```typescript Minimum Order Value theme={null}
  rules: [
    {
      attribute: "subtotal",
      operator: "gte",
      values: [10000], // $100 minimum
    },
  ]
  ```
</CodeGroup>

## Integration Examples

### With Cart Module

Apply promotions to cart.

<CodeGroup>
  ```typescript Example theme={null}
  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,
    })
  }
  ```
</CodeGroup>

### With Customer Module

Customer group promotions.

<CodeGroup>
  ```typescript Example theme={null}
  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),
    },
  })
  ```
</CodeGroup>

## 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

## Related Modules

* [Cart Module](/modules/cart) - Apply promotions to carts
* [Order Module](/modules/order) - Track promotion usage
* [Customer Module](/modules/customer) - Customer group targeting
* [Product Module](/modules/product) - Product targeting
* [Pricing Module](/modules/pricing) - Work with price lists
