Before creating new rules, check what already exists to avoid conflicts.
Endpoint: POST https://www.wixapis.com/ecom/v1/discount-rules/query
Request:
Response:
Note existing rules and their scopes to avoid stacking conflicts.
Endpoint: POST https://www.wixapis.com/ecom/v1/discount-rules
Request — 20% off all products:
Response:
Request — 15% off a specific collection:
Request — $5 off specific products:
Endpoint: PATCH https://www.wixapis.com/ecom/v1/discount-rules/{discountRuleId}
Request:
The revision field is required and must match the current revision.
To deactivate without deleting:
To delete permanently:
Endpoint: DELETE https://www.wixapis.com/ecom/v1/discount-rules/{discountRuleId}
| Field | Required | Notes |
|---|---|---|
name | Yes | Internal name for the rule |
active | Yes | Whether the rule is currently applied |
activeTimeInfo.start | No | ISO 8601 start time. Omit for immediate activation |
activeTimeInfo.end | No | ISO 8601 end time. Omit for no expiration |
discounts[].discount.discountType | Yes | PERCENTAGE or FIXED_AMOUNT |
discounts[].discount.percentage | If PERCENTAGE | Integer 1-100 |
discounts[].discount.fixedAmount | If FIXED_AMOUNT | Decimal string (e.g., "5.00") |
discounts[].scope.type | Yes | CATALOG, COLLECTION, or SPECIFIC_PRODUCTS |
discounts[].scope.id | Yes | "catalog" for CATALOG type, or the collection/product UUID |
| Scope Type | scope.id value | Description |
|---|---|---|
CATALOG | "catalog" | Applies to all products in the store |
COLLECTION | Collection UUID | Applies to all products in a specific collection |
SPECIFIC_PRODUCTS | Product UUID | Applies to a single product (use multiple discount entries for multiple products) |
When creating a discount rule from a recommendation output, use this mapping to convert the recommendation's simplified JSON into the actual Discount Rules API payload.
215238eb-22a5-4c36-9e7b-e7c08025e04e — used in all scope constructions below.active: false with status: "PENDING". The merchant must approve before the rule goes live.The recommendation's scope field maps to the API's internal scope structure. The scope ID uses a prefix convention:
| Recommendation scope | API scope type | Scope ID prefix | How to build |
|---|---|---|---|
SITE | CATALOG_ITEM | all_ | Set catalogItemFilter.catalogAppId to the store catalog app ID. No item IDs. |
ITEMS | CATALOG_ITEM | specific_ | Set catalogItemFilter.catalogAppId + catalogItemFilter.catalogItemIds to the product UUIDs from productIds. |
CATEGORY | CUSTOM_FILTER | collections_ | Set customFilter.appId to the store catalog app ID + customFilter.params.collectionIds to the category UUIDs from categoryIds. |
Example — SITE scope:
Example — ITEMS scope (with product IDs):
Example — CATEGORY scope (with collection IDs):
Recommendation discountType | API field to set | Value format |
|---|---|---|
PERCENTAGE | discount.percentage | Integer (e.g., 15) |
FIXED_AMOUNT | discount.fixedAmount | String (e.g., "5.00") |
FIXED_PRICE | discount.fixedPrice | String (e.g., "29.99") |
All discount entries use targetType: "SPECIFIC_ITEMS" with the scope wrapped in specificItemsInfo.scopes[].
Triggers determine WHEN the discount activates. They are built from the recommendation's conditions fields. If no conditions exist (both minSubTotal and minItemQuantity are 0), do NOT set a trigger — the discount applies unconditionally.
| Condition | Trigger type | How to build |
|---|---|---|
minItemQuantity > 0 only | ITEM_QUANTITY_RANGE | Set itemQuantityRange.from to the value. No upper bound. Include the same scope as the discount target. |
minSubTotal > 0 only | SUBTOTAL_RANGE | Set subtotalRange.from to the value as a string. No upper bound. Include the same scope. |
| Both conditions > 0 | AND | Combine both triggers in and.triggers[] array. |
| Neither condition | No trigger | Leave trigger field unset entirely. |
Example — minSubTotal trigger (upsell boost: spend $200+):
Example — minItemQuantity trigger (bundle: buy 3+):
Example — AND trigger (both conditions):
| Recommendation field | API mapping |
|---|---|
startDate is a date string (e.g., "2026-06-01") | Convert to ISO 8601 timestamp: activeTimeInfo.start |
startDate is empty "" | Default to current time (now) |
endDate is a date string | Convert to ISO 8601 timestamp: activeTimeInfo.end |
endDate is empty "" | Omit activeTimeInfo.end — rule has no expiration |
All recommendation-created rules use these fixed settings:
| Error | Cause | Fix |
|---|---|---|
DISCOUNT_RULE_NOT_FOUND | The discount rule ID doesn't exist | Re-query discount rules to get current IDs |
REVISION_MISMATCH | The revision doesn't match the current version | Re-fetch the rule to get the latest revision, then retry |
INVALID_DISCOUNT_TYPE | Unsupported discount type | Use PERCENTAGE or FIXED_AMOUNT |
Both productIds and categoryIds set | Scope mutual exclusivity violation | Use only one: ITEMS with productIds OR CATEGORY with categoryIds |
productIds empty when scope is ITEMS | Missing required IDs | Query products and provide at least 1 product UUID |
categoryIds empty when scope is CATEGORY | Missing required IDs | Call getCategoryIds to convert category names to GUIDs |