Customer-specific pricing
Propeller supports pricing that varies by customer, company or user group. This page covers how customer-specific pricing works and how to handle it in your frontend.
How customer-specific pricing works
When a user is authenticated, the GraphQL API automatically returns prices based on price sheets assigned to their company or customer group. All of this is handled server-side. When you query a product with an authenticated session, the price field already reflects the customer's specific pricing. You don't need to apply any discounts in your frontend code.
Price sheets
Price sheets are the core mechanism for customer-specific pricing. A price sheet contains discount items that target specific products, categories or product groups. In B2B, price sheets typically represent negotiated pricing agreements between the merchant and the buyer's company. These agreements are often tied to annual contracts and may cover specific product categories, volume commitments or the entire catalog. When a contact logs in and belongs to a company with an assigned price sheet, all product prices automatically reflect the agreed rates.
Each price sheet has:
- Priority — a number that determines precedence. Lower numbers take priority over higher numbers.
- Discount items — individual pricing rules within the sheet. Each item specifies a discount type, value, optional volume thresholds and optional date-range validity.
Discount calculation types
Each discount item uses one of three calculation methods:
| Type | Description | Example |
|---|---|---|
COST_PRICE_PLUS | Cost price plus a percentage margin | Cost €40 + 25% = €50 |
LIST_PRICE_MIN | List price minus a percentage | List €100 − 20% = €80 |
NET_PRICE | Fixed amount (direct price) | Price = €75 |
How overlapping discounts resolve
When multiple price sheet items apply to the same product:
- The item from the price sheet with the lowest priority number wins
- If priority numbers are equal, the item that produces the lowest price is selected
- Orders outside configured volume ranges fall back to the list price
Displaying customer prices
For anonymous users, the product price reflects the default pricing. For authenticated users, it reflects their price sheet pricing:
query GetProductPrice($productId: Int, $taxZone: String) {
product(productId: $productId) {
price(input: { taxZone: $taxZone }) {
gross
net
type
discountType
discount {
value
quantityFrom
}
}
priceData {
list
}
}
}
If the customer has a price sheet, price.gross and price.net will reflect the price sheet price. The price.type field will return PRICESHEET to indicate customer-specific pricing is active.
Compare price.gross against priceData.list to show savings. When the list price is higher than the customer price, display the difference as a discount. In B2B scenarios, consider showing a "Log in for your price" prompt for anonymous users.
Some B2B storefronts hide prices entirely for anonymous visitors and only show them after login. This is common in industries where pricing is confidential and varies significantly between customers. The price.type field helps you decide what label to show: "Your price" when the type is PRICESHEET (contract pricing) or "List price" when the type is DEFAULT.
Action codes (discount codes)
Action codes are promotional codes that apply discounts at the cart level. Apply them with cartAddActionCode:
mutation {
cartAddActionCode(
id: "018dcc9a-f965-7434-8fad-369aa9a8c276"
input: { actionCode: "PROMO15" }
) {
actionCode
total {
totalGross
totalNet
discount
discountPercentage
}
}
}
Remove a code with cartRemoveActionCode:
mutation {
cartRemoveActionCode(
id: "018dcc9a-f965-7434-8fad-369aa9a8c276"
input: { actionCode: "PROMO15" }
) {
actionCode
total {
totalGross
totalNet
}
}
}
The mutation returns an error if the action code cannot be applied.
Custom price overrides in the cart
When integrating with an external pricing system, you can override the calculated price per unit when adding an item to the cart:
mutation {
cartAddItem(
id: "018dcc9a-f965-7434-8fad-369aa9a8c276"
input: {
productId: 67890
quantity: 10
price: 42.50
}
) {
items {
itemId
productId
quantity
price
priceNet
}
total {
totalGross
totalNet
}
}
}
The price field in the input overrides the calculated price for this item. This should only be used when integrating with external pricing systems.
Next steps
- Understanding pricing layers — price types, tax zones and surcharges
- Tiered and volume pricing — bulk discounts based on quantity
- Cart management — how prices and discounts flow into the cart