← Files WixARCHIVED FILE
skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-coupon.md
12 KB · Oct 8, 2026 · 12:02 UTC
---
name: "Pricing: Create Coupon"
description: "PREFERRED recipe for converting a COUPON recommendation (mechanism: COUPON) into a Wix coupon. Use THIS entry — NOT the legacy setup-coupons entry which is superseded. Maps scope/discountType/conditions to Coupons V2 API fields."
---
# Pricing: Create Coupon
> **This skill is the single source for coupon creation.** Do NOT load `…/skills/setup-coupons` — that legacy slug pre-dates the routing tree migration and its content has been merged into this file. If the WixREADME index surfaces it, ignore it.
## Prerequisites
- Wix Stores (or another eCommerce business solution) installed on the site
## Required APIs
- [Create Coupon](https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/create-a-coupon) — `POST /v2/coupons`
- [Update Coupon](https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/update-a-coupon) — `PATCH /v2/coupons/{id}`
- [Query Coupons](https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/query-coupons) — `POST /v2/coupons/query`
- [Delete Coupon](https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/delete-a-coupon) — `DELETE /v2/coupons/{id}`
---
## Step 1: Query existing coupons
Before creating a coupon, check for code conflicts and existing promotions on the same scope.
**Endpoint**: `POST https://www.wixapis.com/stores/v2/coupons/query`
**Request**:
```json
{
"query": {
"paging": {
"limit": 100
}
}
}
```
**Response**:
```json
{
"coupons": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"specification": {
"name": "Summer Sale",
"code": "SUMMER20",
"percentOffRate": 20,
"scope": {
"namespace": "stores"
},
"startTime": 1717200000000,
"expirationTime": 1719792000000,
"usageLimit": 100,
"limitPerCustomer": 1,
"active": true
},
"numberOfUsages": 12,
"expired": false
}
]
}
```
Check for: duplicate codes, overlapping scopes with active coupons, and cross-mechanism stacking with active automatic discount rules.
### Guardrails — run before creating the coupon
- **Duplicate / conflicting code** — reject a code already in use.
- **Cross-mechanism stacking** — coupons stack with automatic discount rules at checkout. Query active discount rules on overlapping scope; if found, warn with the combined effective discount ("a 20% coupon during a 20% automatic sale = ~36% off"). Only one coupon applies per checkout, but automatic rules are unlimited.
- **% plausibility** — > 50% warn (show a $100 → $(100−pct) example); = 100% block unless confirmed; > 100% always block.
- **Margin** — if cost data is available, ensure the discount doesn't push price below cost; otherwise warn for > 40%.
- **User override** — explicit merchant values override these caps; document the override in your reasoning.
(The automatic-rule side of these checks lives in [Create Discount Rule](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/pricing-create-discount-rule) → "Guardrails".)
---
## Step 2: Create a percentage-off coupon
**Endpoint**: `POST https://www.wixapis.com/stores/v2/coupons`
**Request** — 15% off all store products:
```json
{
"specification": {
"name": "Spring Sale 15%",
"code": "SPRING15",
"percentOffRate": 15,
"scope": {
"namespace": "stores"
},
"startTime": 1714521600000,
"usageLimit": 200,
"limitPerCustomer": 1,
"active": true
}
}
```
**Response**:
```json
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}
```
**Request** — 20% off a specific collection:
```json
{
"specification": {
"name": "Electronics Deal",
"code": "ELEC20",
"percentOffRate": 20,
"scope": {
"namespace": "stores",
"group": {
"name": "collection",
"entityId": "collection-uuid-here"
}
},
"startTime": 1714521600000,
"expirationTime": 1717200000000,
"usageLimit": 50,
"limitPerCustomer": 1,
"active": true
}
}
```
**Request** — 10% off a specific product:
```json
{
"specification": {
"name": "VIP Product Discount",
"code": "VIP10",
"percentOffRate": 10,
"scope": {
"namespace": "stores",
"group": {
"name": "product",
"entityId": "product-uuid-here"
}
},
"startTime": 1714521600000,
"limitPerCustomer": 1,
"active": true
}
}
```
---
## Step 3: Create a fixed-amount coupon
**Request** — $10 off all products:
```json
{
"specification": {
"name": "Save $10",
"code": "SAVE10",
"moneyOffAmount": 10,
"scope": {
"namespace": "stores"
},
"startTime": 1714521600000,
"active": true
}
}
```
---
## Step 4: Create a minimum subtotal coupon
Instead of targeting a scope, you can require a minimum cart subtotal. This is a **oneOf** with scope — you set EITHER `scope` OR `minimumSubtotal`, not both.
**Request** — 15% off orders over $100:
```json
{
"specification": {
"name": "Spend $100, Save 15%",
"code": "SPEND100",
"percentOffRate": 15,
"minimumSubtotal": 100,
"startTime": 1714521600000,
"usageLimit": 500,
"limitPerCustomer": 1,
"active": true
}
}
```
---
## Key field rules
| Field | Required | Notes |
|---|---|---|
| `name` | Yes | Display name shown to customers |
| `code` | Yes | Unique coupon code. Max 20 characters. Case-insensitive at checkout. |
| `startTime` | Yes | UNIX epoch in **milliseconds** (not seconds). E.g., `1714521600000` for 2024-05-01T00:00:00Z |
| `expirationTime` | No | UNIX epoch in milliseconds. Omit for no expiration. |
| `scope` OR `minimumSubtotal` | One required | **OneOf** — set scope to target items, OR minimumSubtotal for cart threshold. Cannot set both. Exception: freeShipping type ignores scope. |
| `usageLimit` | No | Total uses across all customers. Omit for unlimited. |
| `limitPerCustomer` | No | Max uses per customer. Omit for unlimited. |
| `limitedToOneItem` | No | If true, discount applies only to lowest-priced item when customer buys multiple. |
| `active` | No | Default `true`. Set `false` to create as draft. |
## Coupon types (oneOf — set exactly one)
| Type | Field | Value | Example |
|---|---|---|---|
| Percentage off | `percentOffRate` | Double (e.g., `15` for 15%) | `"percentOffRate": 15` |
| Fixed amount off | `moneyOffAmount` | Double (e.g., `10` for $10 off) | `"moneyOffAmount": 10` |
| Fixed price | `fixedPriceAmount` | Double (e.g., `29.99`) | `"fixedPriceAmount": 29.99` |
| Free shipping | `freeShipping` | Boolean `true` | `"freeShipping": true` |
| Buy X Get Y | `buyXGetY` | Object with `x` and `y` fields | `"buyXGetY": {"x": 2, "y": 1}` |
> **Note**: Free shipping as an **automatic discount rule** is NOT supported — use the `freeShipping` coupon type here, or configure a free-shipping rate via the Shipping Options API.
## Scope values for Wix Stores
| Scope target | `namespace` | `group` |
|---|---|---|
| All store products | `"stores"` | Omit the `group` field entirely — a present-but-empty `group: {}` is rejected the same as a populated one missing `entityId` |
| Specific product | `"stores"` | `{ "name": "product", "entityId": "<product UUID>" }` |
| Specific collection | `"stores"` | `{ "name": "collection", "entityId": "<collection UUID>" }` |
---
## Recommendation → Coupon API Mapping
When the recommendation output has `mechanism: "COUPON"`, use this mapping to convert it into a Coupons API payload.
### Scope mapping
| Recommendation `scope` | Coupon `scope` |
|---|---|
| `SITE` | `{ "namespace": "stores" }` (all products — omit `group` entirely) |
| `CATEGORY` | `{ "namespace": "stores", "group": { "name": "collection", "entityId": "<first categoryId>" } }` |
| `ITEMS` | `{ "namespace": "stores", "group": { "name": "product", "entityId": "<first productId>" } }` |
**Important limitation**: The Coupons API scope supports only **one entityId** (one product or one collection). If the recommendation targets multiple products or categories:
- For CATEGORY with multiple categoryIds: Create one coupon per collection, or pick the primary collection.
- For ITEMS with multiple productIds: Create one coupon per product, or use `minimumSubtotal` instead of `scope` if the intent is cart-level.
### Discount type mapping
| Recommendation `discountType` | Coupon field |
|---|---|
| `PERCENTAGE` | `"percentOffRate": <value>` |
| `FIXED_AMOUNT` | `"moneyOffAmount": <value>` |
| `FIXED_PRICE` | `"fixedPriceAmount": <value>` |
### Condition mapping
| Recommendation condition | Coupon approach |
|---|---|
| `minSubTotal > 0` | Use `minimumSubtotal` instead of `scope` (they are oneOf — cannot use both) |
| `minItemQuantity > 0` | **Not natively supported by Coupons API**. Mention in the coupon name (e.g., "Buy 3+, use code BUNDLE15") but the API cannot enforce item quantity. |
| `startDate` | Convert to UNIX epoch milliseconds: `Date.parse("2026-06-01") → 1748736000000`. Set as `startTime`. |
| `endDate` | Convert to UNIX epoch milliseconds. Set as `expirationTime`. |
### Code generation
| Recommendation field | Coupon field | Rules |
|---|---|---|
| `code` from recommendation | `specification.code` | Max 20 characters. Must be unique on the site. Suggest memorable, brand-relevant codes (e.g., `SUMMER25`, `SAVE15`, `VIP20`). |
| `usageLimit` from recommendation | `specification.usageLimit` | Total times the coupon can be used. `0` in recommendation → omit (unlimited). |
| `limitPerCustomer` from recommendation | `specification.limitPerCustomer` | Max per customer. `0` → omit (unlimited). Recommend setting to `1` for most campaigns. |
### Full mapping example
**Recommendation output**:
```json
{
"mechanism": "COUPON",
"scope": "CATEGORY",
"categoryIds": ["electronics-collection-uuid"],
"name": "Tech Insider Deal",
"discountType": "PERCENTAGE",
"discount": 20,
"code": "TECH20",
"usageLimit": 100,
"limitPerCustomer": 1,
"conditions": {
"startDate": "2026-06-01",
"endDate": "2026-06-30"
}
}
```
**Coupon API request**:
```json
{
"specification": {
"name": "Tech Insider Deal",
"code": "TECH20",
"percentOffRate": 20,
"scope": {
"namespace": "stores",
"group": {
"name": "collection",
"entityId": "electronics-collection-uuid"
}
},
"startTime": 1748736000000,
"expirationTime": 1751328000000,
"usageLimit": 100,
"limitPerCustomer": 1,
"active": true
}
}
```
### Key differences from Automatic Discounts
| Aspect | Automatic Discount (Discount Rules API) | Coupon (Coupons API) |
|---|---|---|
| Scope supports multiple items | Yes (multiple IDs in arrays) | No (one entityId per scope) |
| minSubTotal + scope together | Yes (scope + trigger) | No (oneOf — choose one) |
| minItemQuantity enforcement | Yes (ITEM_QUANTITY_RANGE trigger) | Not supported by API |
| Date format | ISO 8601 timestamps | UNIX epoch milliseconds |
| Created state | `active: false`, `status: PENDING` | `active: true` by default |
| Triggers/conditions | Separate trigger object with typed ranges | Only minimumSubtotal (simple threshold) |
---
## Error Handling
| Error | Cause | Fix |
|---|---|---|
| `"When scope or minimumSubtotal is not used - only FreeShipping coupon is allowed"` | Coupon sent without `scope` or `minimumSubtotal` | Add `scope: { "namespace": "stores" }` for site-wide (omit `group` — do not send `group: {}`, which is rejected identically), or set `minimumSubtotal` |
| Duplicate code | Another coupon uses the same code | Generate a different code |
| Invalid startTime | Value too low (must be epoch ms, not seconds) | Multiply by 1000 if in seconds |
| Both scope and minimumSubtotal set | These are oneOf — cannot use both | Choose scope OR minimumSubtotal |
| Code exceeds 20 characters | Code is too long | Shorten the code |
## References
- [Coupons API](https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/create-a-coupon)
- [Valid Scope Values](https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/valid-scope-values)
SHA-256: 12028332d6a3344b7cb9bacfe46158fc5e778f38277391b5c5c9b431b1ebf906