Guides and Examples
...
Quotes
Multi-Attribute Pricing
25 min
multi attribute pricing multi attribute pricing allows the pricing engine to select different price book entries (pbes) based on attribute values instead of resolving a single pbe per sku + uom combination, the engine evaluates additional fields such as account type, region, or custom pricing attributes to find the pbe that matches the current context this is useful when the same product has different prices for different customer segments, partner tiers, or configuration options how multi attribute pbe resolution works when the pricing engine resolves a price book entry, it follows this order account field auto resolution the engine reads attribute values from the account record linked to the opportunity (e g , account type ) if a pbe has a matching pricingattribute value, that pbe is selected custom pricing attributes if the request includes custompricingattributes , those values override or supplement the account level values for pbe resolution any fallback if a pbe is configured with the special value any for a pricing attribute, it acts as a universal fallback when no specific match is found default pbe if no pricing attribute match is found and no any pbe exists, the engine falls back to the default pbe (one with no pricing attribute constraints) resolution priority priority source description 1 (highest) custompricingattributes in request explicit override sent in the api call 2 account field auto resolution read from the account record on the opportunity 3 any pbe universal fallback pbe 4 (lowest) default pbe pbe with no pricing attribute constraints and logic for multiple attributes when a pbe has multiple pricing attribute fields, all fields must match for that pbe to be selected this is and logic every attribute must satisfy its condition example price book entries the examples below reference a product nue on salesforce (sku) with the following pbe configurations pbe uom pricingattribute 1 (account type) pricingattribute 2 list price pbe default user/month (none) (none) $29 90 pbe tech user/month technology partner (none) $19 90 pbe channel user/month channel partner (none) $24 90 pbe any user/month any (none) $27 50 pbe tech ent user/month technology partner enterprise $14 90 authentication const myheaders = new headers(); myheaders append("nue api key", "your api key here"); myheaders append("content type", "application/json"); use case 1 auto resolution from account type when the opportunity's account has type = "technology partner" , the pricing engine automatically resolves to the pbe whose pricingattribute 1 matches "technology partner" no extra fields are needed in the request const myheaders = new headers(); myheaders append("nue api key", "your api key here"); myheaders append("content type", "application/json"); const quotedata = { opportunityid "006xx000001abc123", name "tech partner auto resolve quote", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "nue on salesforce", uom "user/month", quantity 10 } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const line = result quotelineitems\[0]; console log(`resolved unit price $${line listunitprice}`); // $19 90 (technology partner pbe) console log(`total $${line totalprice}`); }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000001abc123", "name" "tech partner auto resolve quote", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "nue on salesforce", "uom" "user/month", "quantity" 10 } ] }' the pricing engine reads account type from the opportunity's account, finds "technology partner" , and selects pbe tech at $19 90/user/month use case 2 different account type values resolving to different pbes the same product sku resolves to a different unit price depending on the account's type an opportunity linked to a "channel partner" account gets channel pricing const quotedata = { opportunityid "006xx000002def456", name "channel partner quote", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "nue on salesforce", uom "user/month", quantity 10 } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const line = result quotelineitems\[0]; console log(`resolved unit price $${line listunitprice}`); // $24 90 (channel partner pbe) }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000002def456", "name" "channel partner quote", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "nue on salesforce", "uom" "user/month", "quantity" 10 } ] }' the opportunity 006xx000002def456 is linked to an account with type = "channel partner" , so the engine selects pbe channel at $24 90/user/month use case 3 null account type (default pbe fallback) when the account's type field is null or empty, and no any pbe exists for that attribute, the engine falls back to the default pbe with no pricing attribute constraints const quotedata = { opportunityid "006xx000003ghi789", name "default pricing fallback", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "nue on salesforce", uom "user/month", quantity 10 } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const line = result quotelineitems\[0]; console log(`resolved unit price $${line listunitprice}`); // $29 90 (default pbe) }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000003ghi789", "name" "default pricing fallback", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "nue on salesforce", "uom" "user/month", "quantity" 10 } ] }' with a null account type , no attribute specific pbe matches if a any pbe exists it would be used; otherwise the engine selects pbe default at $29 90/user/month use case 4 bundle parent auto resolution multi attribute pricing works the same way for bundle parent products the engine resolves the parent's pbe based on the account's attributes, and each add on resolves independently const quotedata = { opportunityid "006xx000001abc123", name "bundle with attribute resolution", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "enterprise bundle", uom "license/month", quantity 50, addons \[ { productsku "addon support", uom "license/month", quantity 1 }, { productsku "addon storage", uom "gb/month", quantity 200 } ] } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const parent = result quotelineitems\[0]; console log(`parent unit price $${parent listunitprice}`); parent childrenlineitems foreach(child => { console log(` ${child product sku} $${child listunitprice}`); }); }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000001abc123", "name" "bundle with attribute resolution", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "enterprise bundle", "uom" "license/month", "quantity" 50, "addons" \[ { "productsku" "addon support", "uom" "license/month", "quantity" 1 }, { "productsku" "addon storage", "uom" "gb/month", "quantity" 200 } ] } ] }' each product in the bundle hierarchy resolves its own pbe based on the account's attributes the parent, addon support, and addon storage each independently match their attribute specific pbes use case 5 custom pricing attributes (explicit override) use custompricingattributes to explicitly specify attribute values for pbe resolution this overrides the auto resolved values from the account record each entry in the array requires name the api name of the pricing attribute field on the pbe (e g , ruby pricingattribute 1 c ) value the value to match against const quotedata = { opportunityid "006xx000001abc123", name "custom attribute override", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "nue on salesforce", uom "user/month", quantity 10, custompricingattributes \[ { name "ruby pricingattribute 1 c", value "technology partner" } ] } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const line = result quotelineitems\[0]; console log(`resolved unit price $${line listunitprice}`); // $19 90 (technology partner pbe) console log(`total $${line totalprice}`); }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000001abc123", "name" "custom attribute override", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "nue on salesforce", "uom" "user/month", "quantity" 10, "custompricingattributes" \[ { "name" "ruby pricingattribute 1 c", "value" "technology partner" } ] } ] }' even if the account's type is different (or null), the custompricingattributes value takes precedence and forces resolution to pbe tech at $19 90/user/month use case 6 any universal pbe fallback a pbe configured with any as its pricing attribute value matches any attribute value that does not have a dedicated pbe this provides a catch all price for attribute values you have not explicitly configured for example, if account type = "reseller" and there is no pbe specifically for "reseller", the engine falls back to pbe any (with any ) at $27 50/user/month const quotedata = { opportunityid "006xx000004jkl012", name "any fallback quote", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "nue on salesforce", uom "user/month", quantity 10 } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const line = result quotelineitems\[0]; console log(`resolved unit price $${line listunitprice}`); // $27 50 ( any pbe) }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000004jkl012", "name" "any fallback quote", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "nue on salesforce", "uom" "user/month", "quantity" 10 } ] }' the account's type is "reseller", which has no dedicated pbe the engine matches the any pbe at $27 50 instead of falling through to the no attribute default at $29 90 use case 7 no matching pbe (error scenario) if the pricing engine cannot find any pbe that matches the resolved attributes no specific match, no any fallback, and no default pbe it returns an error const quotedata = { opportunityid "006xx000001abc123", name "no matching pbe", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "limited product", uom "user/month", quantity 10, custompricingattributes \[ { name "ruby pricingattribute 1 c", value "nonexistent tier" } ] } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { if (result errors && result errors length > 0) { result errors foreach(err => { console log(`error ${err code} ${err message}`); // pricebook entry mismatch no matching pbe for the given attributes }); } }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000001abc123", "name" "no matching pbe", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "limited product", "uom" "user/month", "quantity" 10, "custompricingattributes" \[ { "name" "ruby pricingattribute 1 c", "value" "nonexistent tier" } ] } ] }' expected error { "status" 400, "error" "failed to create quote", "message" "no matching price book entry found for product limited product with the given pricing attributes" } the response contains a pricebook entry mismatch error indicating that no pbe could be resolved for the given product, uom, and attribute combination use case 8 multi field custom attributes (and logic) when a pbe requires multiple pricing attributes to match, provide all of them in the custompricingattributes array the engine uses and logic all attributes must match for the pbe to be selected const quotedata = { opportunityid "006xx000001abc123", name "multi attribute and logic", subscriptionstartdate "2026 01 01", subscriptionenddate "2027 01 01", subscriptiontermdimension "month", subscriptionterm 12, products \[ { productsku "nue on salesforce", uom "user/month", quantity 10, custompricingattributes \[ { name "ruby pricingattribute 1 c", value "technology partner" }, { name "ruby pricingattribute 2 c", value "enterprise" } ] } ] }; fetch('https //api nue io/cpq/quotes\ preview', { method 'post', headers myheaders, body json stringify(quotedata) }) then(response => response json()) then(result => { const line = result quotelineitems\[0]; console log(`resolved unit price $${line listunitprice}`); // $14 90 (tech + enterprise pbe) console log(`total $${line totalprice}`); }) catch(error => console log('error ', error)); curl x post 'https //api nue io/cpq/quotes\ preview' \\ h 'nue api key your api key here' \\ h 'content type application/json' \\ d '{ "opportunityid" "006xx000001abc123", "name" "multi attribute and logic", "subscriptionstartdate" "2026 01 01", "subscriptionenddate" "2027 01 01", "subscriptiontermdimension" "month", "subscriptionterm" 12, "products" \[ { "productsku" "nue on salesforce", "uom" "user/month", "quantity" 10, "custompricingattributes" \[ { "name" "ruby pricingattribute 1 c", "value" "technology partner" }, { "name" "ruby pricingattribute 2 c", "value" "enterprise" } ] } ] }' both attributes must match pricingattribute 1 = "technology partner" and pricingattribute 2 = "enterprise" this resolves to pbe tech ent at $14 90/user/month if only one attribute matches, the engine does not select this pbe and falls back to the next best match key behaviors behavior description auto resolution pricing attributes are automatically read from the account record on the opportunity custom override custompricingattributes overrides auto resolved values for the specific product and logic multiple pricing attributes on a pbe require all attributes to match any fallback a pbe with any matches any attribute value without a dedicated pbe default fallback if no attribute specific or any pbe exists, the default pbe (no constraints) is used per product scope custompricingattributes are set per product in the products array bundle support each product in a bundle hierarchy resolves its own pbe independently error on no match if no pbe can be resolved, the api returns a pricebook entry mismatch error custompricingattribute field reference field type required description name string yes the api field name of the pricing attribute on the price book entry (e g , ruby pricingattribute 1 c ) value string yes the value to match against the pbe's pricing attribute field troubleshooting symptom likely cause resolution unexpected price resolved account type auto resolution overriding expectations use custompricingattributes to explicitly set the desired attribute value pricebook entry mismatch error no pbe matches the resolved attributes verify pbe configurations in the price book; add a any or default pbe wrong pbe selected in bundle add on resolving against parent's attributes each product resolves independently; check the add on's own pbe configurations custompricingattributes ignored incorrect field name use the full api field name (e g , ruby pricingattribute 1 c ), not a label