1. Promotions, sale prices, and discounts

Discounts

Discounts let you apply percentage-based price reductions with quantity tiers at the catalog, category, or product level. They support party-based assignments to buyer groups, buyers, and user groups.

When multiple discounts match the same item, OrderCloud applies the discount that gives the lowest price. Discounts do not stack.

Beta feature

This feature is currently in beta. Behavior and properties might change before general availability.

Discount model

{
	"ID": "",
	"Description": "",
	"DiscountBreaks": [
		{
			"Quantity": 10,
			"Amount": 10.0
		},
		{
			"Quantity": 20,
			"Amount": 15.0
		}
	],
	"CatalogID": "",
	"CategoryID": "",
	"ProductID": "",
	"ProductFilter": "",
	"xp": {}
}

Discount properties

PropertyTypeDescriptionRequired
IDstringUnique identifier.Yes
DescriptionstringOptional description. Maximum length is 2000 characters.No
DiscountBreaksarrayQuantity tiers for discount percentages.Yes
CatalogIDstringLimits the discount to a catalog.No
CategoryIDstringLimits the discount to a category.No
ProductIDstringLimits the discount to a product.No
ProductFilterstringLimits the discount to products that match a filter expression.No
xpobjectExtended properties.No

If CatalogID, CategoryID, and ProductID are all null, the discount is applied globally across all products available to the assigned party or parties.

Use ProductFilter to target products with specific values on an xp key. For example, create a discount for products where xp.color=red.

DiscountBreak properties

PropertyTypeDescriptionRequired
QuantityintMinimum quantity for this break.Yes
AmountdecimalDiscount percentage greater than 0 and less than or equal to 100.Yes

For each discount, OrderCloud selects the highest break where Quantity <= LineItem.Quantity.

Assignment model

{
	"DiscountID": "",
	"BuyerGroupID": "",
	"BuyerID": "",
	"UserGroupID": ""
}

Assignment properties

PropertyTypeDescriptionRequired
DiscountIDstringDiscount to assign.Yes
BuyerGroupIDstringBuyer group to assign.Conditionally
BuyerIDstringBuyer to assign.Conditionally
UserGroupIDstringUser group to assign.Conditionally

Assignment rules

Use one of these valid combinations:

  1. Buyer group assignment: BuyerGroupID
  2. Buyer assignment: BuyerID
  3. User group assignment: BuyerID and UserGroupID

API endpoints

Discount management

OperationEndpoint
List discountsGET /v1/discounts
Get discountGET /v1/discounts/{discountID}
Create discountPOST /v1/discounts
Replace discountPUT /v1/discounts/{discountID}
Update discountPATCH /v1/discounts/{discountID}
Delete discountDELETE /v1/discounts/{discountID}

Assignment management

OperationEndpoint
List assignmentsGET /v1/discounts/assignments
Create assignmentPOST /v1/discounts/assignments
Delete assignmentDELETE /v1/discounts/{discountID}/assignments

When you call DELETE /v1/discounts/{discountID}/assignments, pass the target party in query parameters:

  1. buyerGroupID for a buyer group assignment
  2. buyerID for a buyer assignment
  3. buyerID and userGroupID for a user group assignment

Authorization

RoleAccess
PriceScheduleAdminFull CRUD access to discounts and assignments
PriceScheduleReaderRead-only access to discounts and assignments

Create and assign discounts

Create a volume discount

POST /v1/discounts

{
	"ID": "enterprise-volume",
	"Description": "Enterprise customer volume pricing",
	"DiscountBreaks": [
		{ "Quantity": 1, "Amount": 10.0 },
		{ "Quantity": 50, "Amount": 15.0 },
		{ "Quantity": 100, "Amount": 20.0 }
	],
	"CatalogID": "industrial-equipment"
}

Assign to a buyer group

POST /v1/discounts/assignments

{
	"DiscountID": "enterprise-volume",
	"BuyerGroupID": "enterprise-customers"
}

Buyer-side discount data

When a buyer requests products through GET /v1/me/products, OrderCloud evaluates applicable assignments and returns the selected discount in the product pricing payload.

{
  "ID": "product-123",
  "Name": "Industrial Widget",
  "PriceSchedule": {
    "ID": "standard-pricing",
    "Name": "Standard Price Schedule",
    "Discount": {
      "ID": "enterprise-volume",
      "Description": "Enterprise customer volume pricing"
    },
    "PriceBreaks": [
      {
        "Quantity": 1,
        "Price": 100.00,
        "SalePrice": null,
        "Discounted": {
          "Price": 90.00,
          "SalePrice": null,
          "Percent": 10.0
        }
      },
      {
        "Quantity": 50,
        "Price": 100.00,
        "SalePrice": null,
        "Discounted": {
          "Price": 85.00,
          "SalePrice": null,
          "Percent": 15.0
        }
      }
    ]
  }
}

Selection logic

  1. OrderCloud evaluates discounts assigned to the buyer group, buyer, and user group.
  2. If multiple discounts match, OrderCloud picks the discount that returns the lowest price.
  3. For the selected discount, OrderCloud picks the highest matching break where Quantity <= LineItem.Quantity.
  4. The ID of the selected discount is available in PriceSchedule.Discount.ID; PriceSchedule.Discount.Description is also available for display purposes.
  5. If no discount matches, PriceBreak.Discounted is null.

Derived Price Breaks

When a discount's quantity tiers don't align with the price schedule's quantity tiers, the buyer response includes derived price breaks at the discount-break quantities. This ensures buyers always see distinct pricing for every meaningful quantity threshold, without requiring sellers to duplicate price break definitions.

Example: A price schedule has one price break at qty 1 ($100.00). A discount applies 10% at qty 1 and 15% at qty 20. The buyer sees two price breaks:

QuantityPriceDiscounted.PriceDiscounted.Percent
1$100.00$90.0010
20$100.00$85.0015

The qty-20 break is derived - its base Price is inherited from the applicable price tier (qty 1). The number of PriceBreaks on BuyerPriceSchedule equals the number of distinct quantity values across all price breaks and discount breaks.

Constraints on Derived Breaks

A discount-break quantity is not derived as an additional price break if any of the following apply:

  • RestrictedQuantity is true - buyers can only order at exact price break quantities, so intermediate breaks are meaningless. No derivation occurs at all; the discount is still applied to matching price break quantities.
  • Quantity is below MinQuantity - the schedule prohibits ordering at that quantity.
  • Quantity is above MaxQuantity - the schedule prohibits ordering at that quantity.
  • No applicable price break tier exists - the discount-break quantity falls below the lowest price break quantity (no base price can be inherited).

If the above constraints eliminate all discount coverage across every price break, PriceSchedule.Discount is set to null.

Discount persistence on line items and orders

When a line item is created or updated, OrderCloud calculates the discount and stores the result on the line item and order.

Line item fields

PropertyTypeDescription
DiscountIDstringID of the applied discount. Read-only.
BaseDiscountdecimalDiscount amount for the line item in currency units. Read-only.

BaseDiscount uses this calculation:

LineSubtotal * (DiscountPercentage / 100)

Order field

PropertyTypeDescription
BaseDiscountdecimalSum of all LineItem.BaseDiscount values. Read-only.

Persistence example

{
	"ID": "ORDER-123",
	"Subtotal": 200.0,
	"BaseDiscount": 40.0,
	"Total": 160.0,
	"LineItems": [
		{
			"ID": "LI-001",
			"ProductID": "product-123",
			"Quantity": 2,
			"UnitPrice": 100.0,
			"LineSubtotal": 200.0,
			"DiscountID": "enterprise-volume",
			"BaseDiscount": 40.0,
			"LineTotal": 160.0
		}
	]
}
If you have suggestions for improving this article, let us know!