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

# Discounts

> Create promotional codes to drive conversions, run campaigns, and reward customers with percentage discounts across one‑time and subscription purchases.

<Frame>
  <img src="https://mintcdn.com/dodopayments/mOQO5ej_lx0yH9p-/images/discount-codes/discount-code-cover.png?fit=max&auto=format&n=mOQO5ej_lx0yH9p-&q=85&s=57f1a644801ddc5efc506b50541eaaba" alt="Discount codes overview cover" style={{ maxHeight: '500px', width: 'auto' }} width="1200" height="630" data-path="images/discount-codes/discount-code-cover.png" />
</Frame>

Discount codes let you run targeted promotions and incentives. Create percentage-based discounts, set limits and expirations, restrict to products, and apply them seamlessly in checkout.

<CardGroup cols={2}>
  <Card title="Checkout Sessions" icon="cart-shopping" href="/developer-resources/checkout-session">
    Apply one or more stacked codes during hosted checkout with `discount_codes` and UI controls.
  </Card>

  <Card title="Validate Discount" icon="code" href="/api-reference/discounts/validate-discount">
    Check if a discount is valid by its ID.
  </Card>

  <Card title="Get Discount by Code" icon="tag" href="/api-reference/discounts/get-discount-by-code">
    Look up and validate a discount using its code name (e.g., "SAVE20").
  </Card>

  <Card title="Create Discount (API)" icon="code" href="/api-reference/discounts/create-discount">
    Programmatically create new discount codes.
  </Card>

  <Card title="List & Update Discounts" icon="code" href="/api-reference/discounts/list-discounts">
    Browse and manage existing discounts; update or delete as needed.
  </Card>

  <Card title="Plan Change Discounts" icon="arrows-rotate" href="/api-reference/subscriptions/change-plan">
    Apply discount codes when upgrading or downgrading subscription plans.
  </Card>
</CardGroup>

## What Are Discount Codes?

Discount codes are promotional tokens that reduce order totals at checkout. They’re ideal for:

* **Seasonal campaigns**: Black Friday, product launches, or anniversaries
* **Acquisition offers**: First‑purchase incentives or referral rewards
* **Retention**: Win‑back or loyalty rewards for existing customers
* **B2B deals**: Contracted or negotiated pricing via private codes

## Key Benefits

* **Flexible discounts**: Percentage-based or flat-amount discounts
* **Stackable codes**: Apply up to 20 codes per checkout, payment, or subscription — combine campaigns (e.g. `WELCOME10` + `BLACKFRIDAY20`) without creating bespoke codes
* **Targeted control**: Restrict by product, subscription cycles, and customer eligibility
* **Campaign governance**: Scheduled start dates, expiration dates, overall and per-customer usage limits
* **Per-currency pricing**: Set the flat deduction, amount cap, and minimum subtotal per currency
* **Seamless checkout**: UI field and API support via checkout sessions

## Creating Discount Codes

Create discount codes in your Dodo Payments dashboard, then apply them in hosted checkout or via API.

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-details.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=895a9dbe429c2ae57ecc1b317dd2b10d" alt="Discounts list in the Dodo Payments dashboard showing existing discount codes" style={{ maxHeight: '500px', width: 'auto' }} width="3008" height="1626" data-path="images/discount-codes/discount-details.png" />
</Frame>

### Dashboard setup

* **Discount Name** (required): Internal and dashboard display name
* **Code** (required): The string customers enter at checkout, or generate a random code using the button provided
* **Type** (required): Choose **Percentage** (a percent off) or **Amount** (a flat deduction)
* **Amount** (required): The percentage value, or the flat amount for an Amount discount
* **Start Date** (optional): Schedule the code to become active later; leave empty to activate immediately
* **Expiration Date** (optional): Date after which the code becomes invalid
* **Usage Limit** (optional): Max total redemptions across all customers
* **Per-Customer Usage Limit** (optional): Max redemptions by any single customer. Must be less than or equal to the overall usage limit when both are set
* **Customer Eligibility** (optional): Restrict who may redeem the code — all customers, first-time customers, existing customers, or a hand-picked list
* **Currency Options** (optional): Per-currency values — see [Per-Currency Options](#per-currency-options)
* **Product Restriction** (optional): Limit applicability to selected products
* **Subscription Cycle Limit** (optional): Number of billing cycles the discount applies to
* **Preserve on Plan Change** (optional): Keep the discount active when the subscription's plan changes (`preserve_on_plan_change`)
* **Metadata** (optional): Attach custom key–value pairs for internal tracking or integrations

The discount details — type, percentage or amount, name, code, and currency options:

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-create.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=e46f67a9771a1c04ff247b135a0cdc9e" alt="New Discount Code form showing type, percentage amount, name, code, and currency options" style={{ maxHeight: '500px', width: 'auto' }} width="3010" height="1703" data-path="images/discount-codes/discount-create.png" />
</Frame>

Usage and expiry limits, fully configured:

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/configure-usage-limit.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=cca71de89bba3d1909e6b761302f5a29" alt="Usage and expiry limits panel with start date, expiration date, and usage limits configured" style={{ maxHeight: '500px', width: 'auto' }} width="3226" height="1226" data-path="images/discount-codes/configure-usage-limit.png" />
</Frame>

Further down the form — eligibility, restrictions, and advanced options:

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-create-eligibility.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=26e5e6971ac541336c16cec478672a0d" alt="Eligibility, restrictions, and advanced options for a discount code" style={{ maxHeight: '500px', width: 'auto' }} width="3006" height="1342" data-path="images/discount-codes/discount-create-eligibility.png" />
</Frame>

<Tip>
  Use cycle limits for introductory pricing on subscriptions (e.g., "50% off for 3 months").
</Tip>

<Info>
  A percentage `amount` is expressed in **basis points** over the API — `1500` means 15%. A flat `amount` is a money value and is denominated by the code's currency options.
</Info>

## Discount Types

| Type       | API value    | Behaviour                                                                                                |
| ---------- | ------------ | -------------------------------------------------------------------------------------------------------- |
| Percentage | `percentage` | Reduces the price by a percent. Optionally capped per currency with a maximum discount.                  |
| Amount     | `flat`       | Deducts a fixed amount. The deduction is pooled across the whole cart rather than applied per line item. |

Both types can be combined in the same `discount_codes` array and are applied in array order.

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-flat-discount-option.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=38ce7f39a1ccbd26c61718f685fc4e71" alt="Discount code editor with the Amount type selected, showing a flat 500 INR deduction" style={{ maxHeight: '500px', width: 'auto' }} width="3474" height="1968" data-path="images/discount-codes/discount-flat-discount-option.png" />
</Frame>

## Customer Eligibility

Set `customer_eligibility` to control who may redeem a code:

| Value        | Who can redeem                                      |
| ------------ | --------------------------------------------------- |
| `any`        | Any customer. This is the default.                  |
| `first_time` | Customers who have not purchased from you before.   |
| `existing`   | Customers who have purchased from you before.       |
| `specific`   | Only customers you attach to the code's allow list. |

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-restriction.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=3c01240807a7ca2f13b33a9f4cf4ce43" alt="Customer eligibility dropdown showing Any, First-time, Existing, and Specific customer options" style={{ maxHeight: '500px', width: 'auto' }} width="2832" height="830" data-path="images/discount-codes/discount-restriction.png" />
</Frame>

<Warning>
  A `specific` code starts with **zero** eligible customers and rejects every redemption until you attach customers to it. Attach them in the dashboard, or with the discount customers API.
</Warning>

Pair eligibility with **Per-Customer Usage Limit** to cap how often each individual customer can redeem the code, independently of the overall usage limit.

## Per-Currency Options

Currency options let one code behave correctly across the currencies you sell in. Each entry sets, for a single currency:

* **Amount** — for an Amount discount this is the deduction itself; for a Percentage discount it caps how much the code can take off. Maps to `max_amount_possible` over the API.
* **Minimum subtotal** — the code only applies when the cart reaches this subtotal. `0` means no minimum.
* **Default** — one entry can be marked as the default that other, unconfigured currencies convert from.

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-currency-options.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=8e4d0db1a3cfdd256d285a576c9eed34" alt="Currency options panel with per-currency amount and minimum subtotal for INR and USD" style={{ maxHeight: '500px', width: 'auto' }} width="2832" height="614" data-path="images/discount-codes/discount-currency-options.png" />
</Frame>

An Amount discount requires at least one currency option with a resolvable default, since a flat deduction has no meaning without a currency. Currency options are optional for Percentage discounts.

<Info>
  The minimum subtotal is always measured against the cart's original prices, never the running total part-way through a stack. Stacking order therefore never changes whether a minimum is met.
</Info>

## Checkout Experience

1. Shoppers enter the code in the checkout field.
2. Eligible discounts are applied and totals update immediately.

<Frame>
  <img src="https://mintcdn.com/dodopayments/Rh05LkBeJE32G3qq/images/discount-codes/discount-on-checkout.png?fit=max&auto=format&n=Rh05LkBeJE32G3qq&q=85&s=ac92cb27be2e2e7dd5364f941e709966" alt="Checkout page showing a discount applied to the order total" style={{ maxHeight: '500px', width: 'auto' }} width="2458" height="1448" data-path="images/discount-codes/discount-on-checkout.png" />
</Frame>

<Info>
  In Checkout Sessions, pass `discount_codes` (an array) to pre‑apply one or more codes. The discount input field is shown by default — `feature_flags.allow_discount_code` defaults to `true`, so set it to `false` only if you want to hide it. Codes are applied in array order, up to a maximum of 20.
</Info>

## Stacking Discount Codes

Checkout sessions, payments, and subscriptions accept up to **20 stacked codes** via the `discount_codes` array (max 20 entries). Codes are applied **in array order**, so the first eligible code reduces the starting price first, the next code reduces the already-discounted price, and so on. When [Purchasing Power Parity](/features/purchasing-power-parity) is enabled, the starting price is the PPP-adjusted amount, not the base price. The full set of applied discounts is returned in the response under `discount_ids` (on payments/subscriptions) and `discounts` (richer per-discount detail, including position and remaining subscription cycles).

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [{ product_id: 'pdt_abc', quantity: 1 }],
  discount_codes: ['WELCOME10', 'BLACKFRIDAY20'], // applied in this order
  customer: { email: 'user@example.com' },
  return_url: 'https://yoursite.com/return'
});
```

<Info>
  The singular `discount_code` field is **deprecated** but still fully supported for backward compatibility — existing integrations continue to work without changes. It cannot be combined with `discount_codes` in the same request. We recommend migrating to `discount_codes` (the array form) when convenient, even for single codes, to take advantage of stacking and the richer response shape.
</Info>

## API Management

<AccordionGroup>
  <Accordion title="Create discounts">
    Create discount codes programmatically with type and amount.

    <Card title="API Reference" icon="code" href="/api-reference/discounts/create-discount">
      View the create discount API.
    </Card>
  </Accordion>

  <Accordion title="List and retrieve">
    List all discounts or retrieve details for management and auditing.

    <Card title="API Reference" icon="code" href="/api-reference/discounts/list-discounts">
      Browse listing and retrieval APIs.
    </Card>
  </Accordion>

  <Accordion title="Get discount by code">
    Look up a discount using its human-readable code (e.g., "SAVE20") instead of the internal ID.

    <Card title="API Reference" icon="code" href="/api-reference/discounts/get-discount-by-code">
      Retrieve discount by code name.
    </Card>
  </Accordion>

  <Accordion title="Update discounts">
    Modify discount configuration such as amount, expiration, or restrictions.

    <Card title="API Reference" icon="code" href="/api-reference/discounts/update-discount">
      Learn how to update discount details.
    </Card>
  </Accordion>

  <Accordion title="Validate discounts">
    Check whether a discount is valid and applicable before applying.

    <Card title="API Reference" icon="code" href="/api-reference/discounts/validate-discount">
      Validate discount usage.
    </Card>
  </Accordion>

  <Accordion title="Delete discounts">
    Deactivate or remove discounts that are no longer needed.

    <Card title="API Reference" icon="code" href="/api-reference/discounts/delete-discount">
      Delete a discount.
    </Card>
  </Accordion>

  <Accordion title="Manage the customer allow list">
    For a discount with `customer_eligibility` set to `specific`, manage the customers that may redeem it:

    * `GET /discounts/{discount_id}/customers` — list the attached customers (paginated, up to 100 per page).
    * `POST /discounts/{discount_id}/customers` — attach customers by ID. The call is idempotent and accepts up to 1000 IDs, all of which must already exist in your business. The response echoes only the IDs submitted in that request, so list the endpoint to read the full allow list.
    * `DELETE /discounts/{discount_id}/customers/{customer_id}` — detach a single customer.
  </Accordion>
</AccordionGroup>

## Common Use Cases

* **Intro offers**: Limited‑time launch promotions for new products
* **Bulk or B2B**: Contracted discounts for select product sets
* **Retention plays**: Win‑back codes in churn‑prevention workflows
* **Seasonal campaigns**: Holiday or event‑based promotions

## Integration Examples

### Create a discount with metadata

Attach custom key–value pairs for internal tracking.

```typescript theme={null}
const discount = await client.discounts.create({
  type: 'percentage',
  amount: 1500, // 15%
  code: 'SUMMER2025',
  metadata: {
    campaign: 'summer_promo',
    source: 'email_blast'
  }
});
```

<Tip>
  Use metadata to tag discounts by campaign, source, or internal reference ID so you can reconcile usage and measure ROI later.
</Tip>

### Apply discounts in Checkout Sessions

Pre‑apply one or more stacked discounts and show the code input UI.

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_abc', quantity: 1 }
  ],
  discount_codes: ['BLACKFRIDAY2024', 'NEWUSER5'], // stacked in array order
  customer: { email: 'user@example.com', name: 'Jane Doe' },
  return_url: 'https://yoursite.com/return'
});
```

### Apply discounts during plan changes

Offer promotional pricing when customers upgrade or downgrade their subscription.

```typescript theme={null}
await client.subscriptions.changePlan('sub_123', {
  product_id: 'pdt_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  discount_codes: ['UPGRADE20']
});
```

| `discount_codes` value              | Behavior on plan change                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `undefined` / `null` (not provided) | Existing discounts with `preserve_on_plan_change=true` are preserved if applicable to the new product. |
| `[]` (empty array)                  | **All** existing discounts are removed from the subscription.                                          |
| `['CODE_A', 'CODE_B', ...]`         | Replaces any existing discounts with this stacked set, applied in array order.                         |

<Info>
  Read all applied discounts off the subscription via the new `discounts` array on the subscription response. Each entry includes `discount_id`, `position`, `cycles_remaining` (for subscriptions), and the original code.
</Info>

### Hide the discount code field

The discount input is shown by default, so customers can always enter a code without you passing one upfront. Set `allow_discount_code` to `false` to hide the field entirely.

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_abc', quantity: 1 }
  ],
  feature_flags: {
    allow_discount_code: false
  },
  return_url: 'https://yoursite.com/return'
});
```

## Best Practices

* **Name clearly**: Use recognizable codes that match campaign names
* **Time‑box**: Add expirations to drive urgency and prevent misuse
* **Scope wisely**: Limit to specific products to avoid margin leakage
* **Validate early**: Check code applicability before confirming checkout
* **Monitor impact**: Track usage and conversion by campaign

<Info>
  Discount codes are powerful levers for acquisition and retention. Start with simple, well‑named offers, validate thoroughly, and iterate based on performance.
</Info>
