Skip to main content

Overview

Promotion Management API

POST PATCHhttps://openapi.doordash.com/marketplace/api/v2/promotions/stores/{store_location_id}


The Promotion Management API enables merchants to create and update promotions in real-time. Promotions are discounts on in-store items that auto-apply in a customer's cart when purchase requirements are met.

Authentication and credentials are managed through the DoorDash Developer Portal.

note

The API currently supports one promotion per individual item. Promotions cannot be applied to modifier items, as the promo update will drop modifiers on the store.


Supported Promotion Types

DoorDash supports the following promotion types:

Same SKU Promotions

TypeDescription
BUY_X_FOR_YBuy X items for a fixed total price
BUY_X_SAVE_YBuy X items and save a flat dollar amount
BUY_X_GET_Y_Z_PERCENT_OFFBuy X items, get Y items at Z% off

Multi-SKU / Mix & Match Promotions

TypeDescription
BUY_X_FOR_YBuy X items across multiple SKUs for a fixed total price
BUY_X_SAVE_YBuy X items across multiple SKUs and save a flat amount
BUY_X_GET_Y_Z_PERCENT_OFFBuy X across multiple SKUs, get Y items at Z% off

To enable Mix & Match, include multiple SKU IDs in purchase_items and set promotion_options.promotion_conditions to ["MIX_AND_MATCH"].


Endpoints

POST — Create Promotion

Creates new promotions for a store. Send the full promotion payload — not incremental updates.

Request

Headers

Refer to the JWT authentication documentation for authentication details.

Body Parameters

ParameterTypeRequiredDescription
promotion_idString✅ RequiredPrimary key identifying a merchant-provided promotion
promotion_typeString (Enum)✅ RequiredOne of: BUY_X_FOR_Y, BUY_X_SAVE_Y, BUY_X_GET_Y_Z_PERCENT_OFF
purchase_criteriaObject✅ RequiredDefines what the customer must buy
purchase_criteria.purchase_itemsString[]✅ RequiredList of Merchant Supplied Item IDs (MSIDs) eligible to trigger this promotion. Use multiple MSIDs for Mix & Match
purchase_criteria.purchase_quantityInteger✅ RequiredNumber of items customer must purchase to redeem the promotion
redemption_limitObjectOptionalLimits how many times the promo applies per order
redemption_limit.limit_per_orderIntegerOptionalMax redemptions allowed in a single order. Default: 3
discount_optionsObject✅ RequiredDefines the discount value
discount_options.discount_price_offLong (cents)ConditionalFlat discount amount. Required when promotion_type = BUY_X_SAVE_Y. Example: 100 = $1.00 off
discount_options.discount_total_priceLong (cents)ConditionalBundle total price. Required when promotion_type = BUY_X_FOR_Y. Example: 300 = $3.00 total
discount_options.discount_percentageLongConditionalPercentage discount. Required when promotion_type = BUY_X_GET_Y_Z_PERCENT_OFF. Example: 50 = 50% off
discount_options.discount_quantityLongConditionalNumber of items to discount. Used with BUY_X_GET_Y_Z_PERCENT_OFF
promotion_optionsObjectOptionalAdditional promotion configuration
promotion_options.promotion_conditionsEnum[]OptionalSet to ["MIX_AND_MATCH"] to allow cross-SKU eligibility
start_timeTimestamp (UTC)✅ RequiredPromotion start time. Example: 2023-07-07T14:48:00.000Z
end_timeTimestamp (UTC)✅ RequiredPromotion end time. Example: 2023-07-08T14:48:00.000Z
Response

202 Accepted — Success

{
"operation_id": "string",
"operation_status": "SUCCESS",
"message": "string"
}

operation_status possible values: QUEUED · IN_PROGRESS · SUCCESS · FAILED · PARTIAL_SUCCESS

Error Codes

HTTP CodeCodeMessagefield_errors.fieldfield_errors.error
400validation_errorOne or more request values couldn't be validatedName of the field that failed validationThe validation error message
401authentication_errorThe [exp] is in the past; the JWT is expired
403authorization_errorAuthorization error: credentials provided are invalid
404unknown_business_id
422request_rate_limited
429request_rate_limited
500service_faultInternal service failure, please try again later

PATCH — Update Promotion

Updates existing promotions for a store. Send the full promotion payload — not incremental updates.

Request

Same headers and body parameters as POST Create Promotion.

⚠️ Always send the complete promotions payload. Incremental updates (modifying a single attribute) are not supported.

Response

Same response structure as POST.

⚠️ If you PATCH a promotion that doesn't exist, you will receive a 202 Accepted but the promotion will be dropped during async validation. Additional feedback mechanisms are planned for a future release.


Rate Limits & Batch Sizes

EndpointBatch SizeRate Limit
Promotion Management API (POST)1,000 items5–10 QPS

Error Reference

HTTP CodeError ReasonMessageHow to Fix
400validation_errorscode: validation_errors; fieldErrors: {}One or more fields failed validation. Verify all required fields are present and correctly typed. Check the request body against the parameter table above.
401authentication_errorThe [exp] is in the past; the JWT is expiredYour JWT token has expired. Regenerate your token and retry the request.
403authorization_errorAuthorization error: credentials provided are invalidVerify your credentials in the DoorDash Developer Portal. Ensure the token has the correct permissions for this store.
404unknown_business_idThe store_location_id in the URL does not exist or is not associated with your account. Verify the store ID.
422request_rate_limitedReduce request frequency and retry.
429RATE_LIMIT_EXCEEDEDDeveloper account endpoint rate limit exceededYou have exceeded the allowed rate (5–10 QPS). Implement exponential backoff, reduce request frequency, and batch promotions (up to 1,000 items per request).
500service_faultInternal service failure, please try again laterRetry the request. If the issue persists, contact DoorDash Developer Support.

Payload Examples

BUY_X_FOR_Y — Buy 2 Coke for $3 (Same SKU)
{
"promotion": {
"promotion_id": "101",
"promotion_type": "BUY_X_FOR_Y",
"purchase_criteria": {
"purchase_quantity": 2,
"purchase_items": ["coke_msid"]
},
"redemption_limit": {
"limit_per_order": 3
},
"discount_options": {
"discount_total_price": 300
},
"start_time": "2023-07-07T14:48:00.000Z",
"end_time": "2023-07-08T14:48:00.000Z"
}
}
BUY_X_FOR_Y + MIX_AND_MATCH — Buy 2 Coke OR Buy 2 Sprite for $3 (Multi-SKU)
{
"promotion": {
"promotion_id": "101",
"promotion_type": "BUY_X_FOR_Y",
"purchase_criteria": {
"purchase_quantity": 2,
"purchase_items": ["coke_msid", "sprite_msid"]
},
"redemption_limit": {
"limit_per_order": 3
},
"discount_options": {
"discount_total_price": 300
},
"promotion_options": {
"promotion_conditions": ["MIX_AND_MATCH"]
},
"start_time": "2023-07-07T14:48:00.000Z",
"end_time": "2023-07-08T14:48:00.000Z"
}
}
BUY_X_SAVE_Y — Buy 2 Coke, Save $1
{
"promotion": {
"promotion_id": "101",
"promotion_type": "BUY_X_SAVE_Y",
"purchase_criteria": {
"purchase_quantity": 2,
"purchase_items": ["coke_msid"]
},
"redemption_limit": {
"limit_per_order": 3
},
"discount_options": {
"discount_price_off": 100
},
"start_time": "2023-07-07T14:48:00.000Z",
"end_time": "2023-07-08T14:48:00.000Z"
}
}
BUY_X_GET_Y_Z_PERCENT_OFF — Buy 1 Coke, Get 1 Coke 50% Off
{
"promotion": {
"promotion_id": "101",
"promotion_type": "BUY_X_GET_Y_Z_PERCENT_OFF",
"purchase_criteria": {
"purchase_quantity": 1,
"purchase_items": ["coke_msid"]
},
"redemption_limit": {
"limit_per_order": 3
},
"discount_options": {
"discount_quantity": 1,
"discount_percentage": 50
},
"start_time": "2023-07-07T14:48:00.000Z",
"end_time": "2023-07-08T14:48:00.000Z"
}
}
BUY_X_FOR_Y + MIX_AND_MATCH — Buy Any 2 Lays Chips for $5 (Multi-SKU)
{
"promotion_id": "123",
"promotion_type": "BUY_X_FOR_Y",
"purchase_criteria": {
"purchase_items": ["a100", "a200", "a300"],
"purchase_quantity": 2
},
"redemption_limit": {
"limit_per_order": 2
},
"discount_options": {
"discount_total_price": 500
},
"promotion_options": {
"promotion_conditions": ["MIX_AND_MATCH"]
},
"start_time": "2025-01-01T00:00:00Z",
"end_time": "2026-01-01T00:00:00Z"
}

Mix & Match Discount Distribution

When a Mix & Match promotion applies across items with different prices, DoorDash distributes the discount as follows:

  • Higher-priced items are discounted first — the system selects the highest-priced eligible items to maximize customer savings.
  • Discount is distributed proportionally — the discount amount on each item is calculated based on the weight of (price × discounted quantity) / total price.
  • Rounding — fractional amounts are rounded up to the nearest cent to avoid penny discrepancies.
  • Equal-price items — when items have the same price, discounts are applied using "First In" logic (items added first to the cart receive the discount).

Example — Buy 2 for $6, cart has 3 items:

  • Item A: $4 (added 1st), Item B: $4 (added 2nd), Item C: $5 (added 3rd)
  • Discounted items: Item C ($5) and Item A ($4) → discount = $9 − $6 = $3
  • Item B remains at full price
Example order payload showing discount distribution
{
"categories": [
{
"merchant_supplied_id": "Drinks",
"name": "Drinks",
"items": [
{
"name": "Coke Soda Bottle (20 fl oz)",
"quantity": 1,
"price": 379,
"merchant_supplied_id": "8010333",
"applied_item_discount": {
"discount_amount": 77,
"promo_id": "83867509-6f27-38f9-952f-fe141bd8e43a",
"promo_quantity": {
"discount_item_promo_quantity": 1
}
},
"applied_item_discount_details": [
{
"total_discount_amount": 77,
"promo_id": "83867509-6f27-38f9-952f-fe141bd8e43a",
"promo_quantity": {
"discount_item_promo_quantity": 1
},
"doordash_funded_discount_amount": 0,
"merchant_funded_discount_amount": 77
}
]
},
{
"name": "Diet Mountain Dew Citrus Soda Bottle (20 fl oz)",
"quantity": 2,
"price": 359,
"merchant_supplied_id": "8050480",
"applied_item_discount": {
"discount_amount": 71,
"promo_id": "83867509-6f27-38f9-952f-fe141bd8e43a",
"promo_quantity": {
"discount_item_promo_quantity": 1
}
},
"applied_item_discount_details": [
{
"total_discount_amount": 71,
"promo_id": "83867509-6f27-38f9-952f-fe141bd8e43a",
"promo_quantity": {
"discount_item_promo_quantity": 1
},
"doordash_funded_discount_amount": 0,
"merchant_funded_discount_amount": 71
}
]
}
]
}
]
}

FAQ

How long will it take for a new promotion to be created or updated?

It can take up to 15 minutes for a new or updated promotion to go live.

What are the rate limits and batch sizes?
EndpointBatch SizeRate Limit
Promotion Management API (POST)1,0005–10 QPS
What will happen if we PATCH a promotion that doesn't exist?

If you send the request in the correct format, we will return a 202 Accepted response but drop the promotion during async validation. Additional feedback mechanisms are planned for a future release.

How do I remove a promotion that is no longer running intra-day?

Share the expiration as the current day minus 1 (i.e., set end_time to yesterday).

How do I verify whether a promotion went through correctly in advance?

At the moment, you cannot submit promotions in advance and receive confirmation of future activation. Additional feedback is planned for a future release.

Can we leverage the same OpenAPI provider type as other endpoints?

Yes, you will use the same provider type across all endpoints.

If a new promo is created, should we send all promos again via POST or can we send a PATCH?

Send the complete promotions payload — incremental updates on a single attribute are not supported.

Do you support Mix & Match deals?

Yes. Mix & Match is supported. To enable it:

  1. Include multiple SKU IDs in purchase_items
  2. Add "MIX_AND_MATCH" to promotion_options.promotion_conditions

There is no upper limit on the number of SKUs, but at least two SKUs are required.

Can the same item be part of multiple deals?

No. We only support one deal per item in the integration. If multiple promotions are submitted for a single item, the promo will be dropped.

Can customers stack multiple discounts?

Yes. Customers will see stacked promotions when applicable. Promos can stack regardless of funding source, but must be different deal types (e.g., STP vs. complex deal). If multiple deals of the same type are live on one item, the system surfaces the deal based on funding source priority:

CPG funded → Ibotta → Merchant Ingested → DoorDash funded

How are promos gated by loyalty handled?

Your DoorDash technical account manager can enable the loyalty-gated feature if it is required for your integration.

If an item is adjusted in the cart, will the promotion still apply?

Yes. If the cart still qualifies for a promotion after an item adjustment, the promotion will continue to apply to the remaining eligible items.

What rounding methodology is used for fractional discount calculations?

We round up to the nearest cent. This ensures no penny discrepancies ($0.01–$0.02 variances) between item-level deductions and the total promotion amount shown to the customer.

When all items are the same price, which items get the discount if the cart exceeds the deal quantity?

When items have identical prices, the system uses "First In" logic — the discount applies to items based on the order they were added to the cart (the order in the payload).

Example — Buy 3 for $4, cart has 4 items:

  • 2× Item A ($X, added 1st), 1× Item B ($X, added 2nd), 1× Item C ($X, added 3rd)
  • Discounted: 2× Item A + 1× Item B
  • Item C remains at full price (last added)
If I submit a future-dated promo for a SKU that already has a live promo running, will the original promo keep showing until the new one starts?

No. Submitting a new promo immediately replaces the existing one, regardless of the new promo's start date. If the new promo is future-dated, it won't render on the storefront yet — and the original live promo is already gone. The item will show no deal until the new promo's start date arrives.

Fix: Wait until your current promo period ends before submitting the next one. There is no promo queuing — it's last-write-wins.