Store → Discount Codes
Run promos on your Store listing without building redemption logic. You create a code through the Store MCP, a parent types it at checkout, and the Store validates it, prices it, and records which purchase it applied to.
Scope
Every discount code belongs to exactly one of your apps. storeAppId is required on create_discount_code, and the tool checks your app:manage_store grant on that app before writing — so you can create codes for your own listings and only those. A code presented at checkout for a different app is rejected as app_mismatch.
Codes apply to storefront checkout only. In-app purchases never carry a discount — they charge the catalog item price as configured.
Creating a Code
create_discount_code takes:
| Field | Required | Notes |
|---|---|---|
code |
yes | The string parents type. Case-insensitive and unique across the whole Store. |
discountType |
yes | flat (fixed cents off) or percent (basis points off). |
amountCents |
for flat |
Positive integer. Must be omitted for percent. |
percentBps |
for percent |
1–10000, where 100 = 1%. Must be omitted for flat. |
currency |
for flat |
ISO 4217. Must be omitted for percent, since a percentage is currency-free. |
storeAppId |
yes | The app the code applies to. |
expiresAt |
no | ISO 8601. Must be in the future — Stripe rejects a past expiry. Omit for none. |
maxRedemptions |
no | Global cap across all parents. Omit for unlimited. |
firstTimeCustomerOnly |
no, defaults false |
When true, only valid for a parent with no previous paid purchase. |
A flat code only applies to a plan in the same currency; mismatches are rejected at checkout rather than converted.
Codes Are Immutable
There is no update tool. A code's terms are fixed the moment it is created.
This is deliberate. Your code string is also the identifier of the underlying Stripe coupon, and Stripe coupons cannot be edited — changing a discount means deleting and recreating the coupon. Because Stripe's discount records point at the coupon by ID rather than storing a copy of its terms, recreating one would retroactively rewrite what every past purchase appears to have been discounted by. Freezing terms at creation keeps your historical promo reporting honest.
To change a promo: deactivate the old code and create a new one with the new terms.
Retiring a Code
deactivate_discount_code takes the code and retires it. A retired code stops validating at checkout immediately, and purchases that already used it keep their attribution — your revenue history is unaffected.
Two consequences worth planning around:
- Deactivation is permanent. There is no reactivation tool. Since terms are fixed at creation, a revived code would come back with exactly the terms you retired it for.
- The string is spent. A retired code's string stays claimed forever and cannot be reused for a new code. Version your promo codes (
LAUNCH20,LAUNCH20-Q2) rather than planning to recycle one.
Deactivating an already-retired code succeeds and changes nothing, so retries are safe.
Listing Your Codes
list_discount_codes requires storeAppId and returns that app's codes, including retired ones and their current state. discountType is the only optional filter. Each row carries redeemedCount so you can see how a promo is performing against its maxRedemptions cap.
Why a Code Was Rejected
Validation runs when the parent enters the code and again authoritatively when the checkout session is created. A rejection carries one of:
| Reason | Meaning |
|---|---|
unknown |
No code with that string exists. |
inactive |
The code was deactivated. |
expired |
expiresAt has passed. |
limit_reached |
maxRedemptions has been reached. |
not_first_time |
firstTimeCustomerOnly is set and the parent has purchased before. |
currency_mismatch |
A flat code's currency differs from the plan's currency. |
app_mismatch |
The code belongs to a different app. |
Related Docs
Store → Integration
The full MCP tool surface and the runtime entitlement check at app launch.
Store → MCP Setup
Connect Cursor / VS Code / Claude Code / ChatGPT to the Store MCP.
Store → In-App Purchases
Server-to-server purchases, which discount codes do not apply to.
Store → Refunds
What happens to a discounted purchase when it is refunded.
