Search Documentation
Search for a documentation page...
Coupons API
REST API endpoints for managing checkout discount codes.
Coupons are discount codes customers enter at checkout. Each coupon takes a percentage off, a fixed amount off, or gives free shipping, and can be limited by date range, total redemptions, cart product count, a minimum order value, specific products, single variants of a product, collections, categories, or brands. A coupon can skip products already on sale, can be limited to one-time purchases, and can carry extra rules: percentages on narrower scopes, with an optional minimum-quantity condition, and buy X get Y offers.
List Coupons
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 10 | Coupons per page (1-100) |
offset | number | 0 | Coupons to skip |
query | string | – | Search by coupon code |
Response
Create Coupon
Creates a discount code. Codes are matched case-insensitively and are unique per store, so creating save10 when SAVE10 exists returns a 409. For fixed coupons on multi-currency stores, supply per-currency amounts via currencyAmounts.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | The code customers enter at checkout. Letters A to Z, digits, and hyphens, up to 64 characters, matched case-insensitively |
type | "percentage" | "fixed" | "free_shipping" | Yes | A percentage off, a fixed amount off, or free delivery |
value | number | Conditional | Percentage (e.g. 10 = 10%, max 100) or fixed amount in store currency (min 1). Required for percentage and fixed; omit it for free_shipping |
freeShipping | boolean | No | Also make delivery free on top of a percentage or fixed discount (default false). Ignored for free_shipping coupons, which always free delivery and read back as true |
startDate | string | No | ISO 8601 datetime the coupon becomes valid |
endDate | string | No | ISO 8601 datetime the coupon expires. Must be after startDate when both are set |
maxUses | number | No | Maximum total redemptions across all customers |
oncePerCustomer | boolean | No | Limit to one use per customer (default false). Each checkout email, matched case-insensitively, can place one order with the code; a cancelled order gives the use back. Combine with maxUses for a code capped both in total and per customer. Applying the code to a cart whose email already used it fails with couponAlreadyUsed |
minProductCount | number | No | Minimum cart product count required |
maxProductCount | number | No | Maximum cart product count the coupon applies to. Must be >= minProductCount when both are set |
products | string[] | No | Product UUIDs the coupon is restricted to, covering every variant of each product – including variants added later (empty = no product restriction) |
variants | string[] | No | Variant UUIDs the coupon is restricted to, when only some variants of a product should be discounted. Other variants of the same product pay full price. A variant whose product is also in products is dropped, since the product already covers it |
collections | string[] | No | Collection UUIDs the coupon is restricted to; membership is evaluated at checkout |
categories | string[] | No | Category UUIDs the coupon is restricted to; membership is evaluated at checkout |
brands | string[] | No | Brand UUIDs the coupon is restricted to; membership is evaluated at checkout |
currencyAmounts | object | No | Per-currency fixed amounts for multi-currency stores, keyed by currency code (fixed coupons only) |
minSubtotal | number | null | No | Minimum order value in the store currency, greater than 0. YNS compares it with the cart's products at their gross price after promotions, before a subscription plan discount or the coupon. Shipping is not counted. Below it, applying the code returns 422 with reason: "couponMinSubtotalNotMet". Omit it or send null for no minimum |
currencyMinSubtotals | object | No | Minimum order values per currency, keyed by currency code, each greater than 0. When minSubtotal is set, set one for every other currency the store sells in. In a currency without its own minimum, applying the code returns 422 with reason: "couponIsNotApplicable" |
excludeSaleItems | boolean | No | Skip cart lines whose price an active promotion lowered (default false). Those lines keep the promotional price. Volume pricing, subscription plan discounts, and bundles are not a sale |
appliesOnSubscription | boolean | No | Purchase type (default true). true also takes the coupon off the plan price on the first order of a subscription. false limits the coupon to one-time purchases, so subscription lines get only their plan discount. Renewals never get a coupon |
additionalRules | object[] | No | Up to 10 extra rules, tried in order before the main rule (default []). See Extra rules |
Response (201)
Free shipping coupons
A free_shipping coupon makes the shopper's chosen delivery method cost nothing (and carry no tax) while product prices stay the same. It works alongside any free-delivery threshold the store already has, and value is reported as 0 when the coupon is read back.
Scope restrictions still apply: with products, variants, collections, categories or brands set, delivery becomes free as soon as any cart line matches one of them.
Discount plus free shipping
To give money off and free delivery with one code, create a percentage or fixed coupon with freeShipping: true. The discount applies to qualifying products as usual, and the delivery method costs nothing, under the same scope and cart-item rules.
PATCH replaces the coupon's fields, so send freeShipping: true again when updating a coupon that should keep free delivery.
Limit a coupon to specific variants
List variant UUIDs in variants to discount only those variants – for example the "Start" plan of a product sold in several plans. Leave the product itself out of products: a product there covers all its variants. A cart line qualifies if its product is in products or its variant is in variants; if no line in the cart qualifies, applying the code returns 422 with reason: "couponIsNotApplicable".
The response lists the variants in variantIds.
Extra rules
additionalRules gives one code different deals on different products. The coupon's own type, value, and scope are its main rule. Each entry in additionalRules is one of two kinds, set by type.
A scope below means the five arrays products, variants, collections, categories, and brands, each a list of UUIDs (default []). A product in products covers all its variants. Every scope in a rule needs at least one non-empty array.
Percentage rule
| Field | Type | Required | Description |
|---|---|---|---|
type | "percentage" | No | The default when type is left out |
value | number | Yes | Percent off, from 0.001 to 100, with up to 3 decimals (33.333 = 33.333%) |
products, variants, collections, categories, brands | string[] | One of them | The products the rule discounts |
condition | object | null | No | Apply the rule only while the cart holds enough of some products. See below |
condition has minQuantity (an integer, at least 1) and its own scope. The rule applies only while the cart holds at least minQuantity units from that scope. The counted products can differ from the discounted ones.
Buy X get Y rule
| Field | Type | Required | Description |
|---|---|---|---|
type | "buyXGetY" | Yes | |
buys | object | Yes | quantity (integer, at least 1) and a scope: what the customer buys at full price |
gets | object | Yes | quantity (integer, at least 1), value (percent off, 0.001 to 100, 100 = free) and a scope: what the customer then gets discounted |
usesPerOrderLimit | number | null | No | How many times the offer can apply in one order. Default null, no limit |
The offer applies once for every buys.quantity units from the buy scope plus gets.quantity more units from the get scope. The discounted units are the cheapest ones, the most expensive ones count as bought, and no unit counts as both. With the same product on both sides, buy 2 get 1 gives 1 free unit for 3 in the cart, still 1 for 5, and 2 for 6.
How rules combine
Each cart line takes one rule. YNS tries the extra rules in array order, then the main rule. A line takes the first rule that covers it and is active:
- A percentage rule is active when it has no
condition, or the cart meets it. - A buy X get Y rule is active when the cart holds at least one full set. It covers the lines in both its scopes, so those lines take no other discount, including units left over after the last full set.
- The main rule is always active.
Rules never add up on one line. A line that no rule takes pays full price. The code applies when at least one line gets a discount from it.
With excludeSaleItems: true, lines on sale are left out of every rule. With appliesOnSubscription: false, subscription lines are too. A left-out line is not discounted, does not count as a bought unit, and does not count toward a minQuantity.
Responses list each rule with its scopes as productIds, variantIds, collectionIds, categoryIds, and brandIds. A percentage rule always has condition, null when it has none:
Example: code K3BA
This coupon gives a free tub of creatine (…c1) for every two. A cart with at least three tubs also gets 10% off an add-on product (…b1). The main value is 0, so the code takes nothing off other products.
With 3 tubs and the add-on product, the cart pays for 2 tubs and gets the add-on 10% cheaper. With 2 tubs, neither extra rule is active and the code does not apply.
A value of 0 is allowed only on a coupon with additionalRules: it means no main discount. For "5% on everything, 33% on creatine", leave the main scope empty and set value to 5, then add one percentage rule with "value": 33 on the creatine product. Creatine lines take 33%, and every other line takes 5%.
Get Coupon
Returns a single coupon, addressable by its UUID or by its code.
Response
Update Coupon
Replaces the coupon's fields. Uses the same body as Create Coupon. A field you leave out takes its default, so send every field the coupon should keep, including minSubtotal, currencyMinSubtotals, excludeSaleItems, appliesOnSubscription, oncePerCustomer, and additionalRules. Renaming onto a code another coupon already owns returns a 409.
Request Body
Same fields as Create Coupon.
Response (200)
Delete Coupon
Hard-deletes the coupon by UUID or code. A coupon that is currently assigned to the store's enabled welcome offer cannot be deleted – disable the offer or reassign it first.