Overview: Create Quote & Create Order Global Methods
19 min
overview create quote & create order global methods the nue quote api suite nue on salesforce exposes a suite of apex global methods under ruby globalquoteserviceapi and ruby globalorderserviceapi that allow you to programmatically create fully priced quotes and orders — with the same pricing fidelity as the nue ui these methods are part of the broader apex global methods and services https //docs nue io/apex global methods and services surface, which also includes services for recalculation, price tag reapplication, order generation, change orders, and more the quote apis include create a new business quote ( ruby globalquoteserviceapi createquote ) — the comprehensive method documented in this guide it supports both preview and commit modes preview mode allows users or integrated ai systems to simulate quote creation, verifying structure and pricing without persisting any data commit mode finalizes the quote as an official record the method handles the full lifecycle product resolution, bundle expansion, pricing engine execution, discount propagation, price tag evaluation, and optional database persistence — all in a single call create a new business order ( ruby globalorderserviceapi createorder ) — the order side equivalent, creating orders directly without going through the quote to order conversion flow these global methods sit alongside other quote and order services you may use in your workflow service method description ruby globalquoteservice recalculate(quoteid) recalculate pricing on an existing quote ruby globalquoteservice reapplypricetags(quoteid) reload applicable price tags by date range ruby globalquoteservice generateorders(quoteids, activate) convert quotes to orders with optional activation ruby globalquoteservice syncprimaryquotetoopportunity(quoteid) sync primary quote line items to opportunity products ruby globalquoteservice copyquotefromtemplate(templateid, targetid) copy a template quote into an empty target quote ruby globalorderservice recalculate(orderid) recalculate pricing on a draft order ruby globalorderservice reapplypricetags(orderid) reload applicable price tags on a draft order ruby globalorderservice cancelactiveorders(orderids) cancel active orders note on scope while createquote and createorder are robust for initiating quotes/orders — adding products and performing pricing calculations — they are specifically engineered for the initial creation of quotes and orders for more intricate modifications after creation, such as detailed line item adjustments, advanced discounting changes, or complex product reconfigurations, the line editor remains the designated tool the line editor offers granular control for fine tuning quotes beyond the scope of these api methods design philosophy semantic rich, not line by line the create quote method is not a low level "insert a quotelineitem" api it is a semantic rich global method designed so that creating a quote via apex is as intuitive as creating one through the nue ui you describe what you want — products, quantities, terms, discounts — and the pricing engine handles everything else product resolution — find products by sku or name, resolve the correct price book entry https //docs nue io/price books based on uom https //docs nue io/unit of measures uoms , currency, and pricing attributes https //docs nue io/multi attribute pricing bundle expansion — automatically include bundled https //docs nue io/product bundle items, auto populate required add ons, resolve dynamic product options https //docs nue io/dynamic product options pricing engine execution — calculate the full pricing formula chain (list price → system discount → subtotal → discretionary discount → total price → tax) discount propagation — apply header level discounts to all undiscounted lines, propagate parent level discounts to children, respect explicit overrides price tag evaluation — evaluate tiered, volume, and ramp price/discount tags https //docs nue io/price and discount tags ; apply auto attached tags from product configuration pricing attribute matching — auto resolve multi attribute https //docs nue io/multi attribute pricing price book entries from account fields (e g , account type), match custom pricing attributes https //docs nue io/manage custom attributes in price book entries quantity tier attributes https //docs nue io/quantity tier attributes for price tags — dynamically resolve discount tiers based on field values (e g , account numberofemployees) validation — enforce business rules and return structured error messages with specific error codes built for ai integration a key design consideration for these apis is the emphasis on extensive and detailed warning and error messages this is critical given the intention for ai systems to utilize these methods for automated quote creation and management by providing highly detailed and comprehensive messages — with specific error codes, affected field names, and actionable descriptions — ai systems can comprehend outcomes — interpret whether a quote succeeded, partially succeeded with warnings, or failed with specific validation errors self correct — use error codes to automatically adjust inputs (e g , fix an invalid billing period, resolve a missing product sku) flag issues efficiently — surface warnings about discount precedence, duplicate price tags, or default values to human reviewers minimize manual intervention — improve accuracy and reliability of automated quote workflows the response always includes status "succeed" or "failure" — unambiguous outcome errors\[] structured error messages with errorcode , errortype , and human readable message warnings\[] non fatal messages (e g , product discount overrides header , duplicate price tag ) — present even on success data the fully priced quote with all line items, applied price tags, and calculated fields how it works create quote api flow key concepts preview vs commit every request includes an iscommit flag iscommit = false (preview) — runs the full pricing engine and returns calculated results, but does not save anything to the database the returned quote id is null use this for pricing previews, what if scenarios, ai driven validation, and integration testing iscommit = true (commit) — runs pricing and persists the quote/order and all line items to the database the returned quote id contains the new record's salesforce id see preview mode vs commit mode for details and code examples product identification products can be identified in three ways (in order of precedence) productsku — the product's stockkeepingunit (e g , 'nue platform' ) productname — the product's name (e g , 'nue platform' ) pricebookentryid — a specific pricebookentry id (overrides sku/name resolution) at least one of productsku or productname must be provided when both are given, productsku is used for lookup you can optionally pass productid (a salesforce product2 id ) to skip the sku/name lookup however, for products with multi attribute pricing https //docs nue io/multi attribute pricing , you must use the real product id — not the shadow product id why this matters shadow products in multi attribute pricing nue supports multiple price book entries per product within the same price book by using different pricing attributes (e g , uom, currency, region, account type) because salesforce natively allows only one pricebookentry per product per pricebook, nue works around this by creating shadow product2 records (with ruby productrecordtype c = 'multiattributes' ) each shadow product holds a single pricebookentry for one attribute combination, while the real product (with ruby productrecordtype c = 'product' ) remains the logical product that users see in quotes, orders, and subscriptions shadow products are not visible at operation time for more details on the object model, see multi attribute pricing https //docs nue io/multi attribute pricing this means pricebookentry product2id points to a shadow product (the clone) pricebookentry ruby realproduct c points to the real product subscription ruby product c points to the real product if you pass a shadow product id as productid , the api will return a "no pricebook entry found" error because internally it groups price book entries by the real product id ( realproduct c ) but looks them up using the productid you provided — causing a key mismatch recommended input combinations combination works? notes productsku + uom ✅ recommended sku always resolves to the real product productsku + pricebookentryid ✅ simplest when you know the exact pbe no uom needed productid ( real real ) + pricebookentryid ✅ use ruby product c from the subscription, not pricebookentry product2id productid ( shadow shadow ) + uom no no fails with "no pricebook entry found" when in doubt, omit productid entirely and use productsku instead — the api will resolve it to the correct real product automatically uom (unit of measure) each product can have multiple pricebookentries at different uoms (e g , user/month at $9 90, user/year at $99) specify the desired uom via uom — uom name string (e g , 'user/month' , 'user/year' ) uomid — uom record id (alternative to name) pricebookentryid — directly specify the price book entry (bypasses uom resolution) pricing formula https //docs nue io/understanding price calculations the api calculates pricing using this formula chain list price x quantity x subscription term = list total price \ system discount amount = subtotal (= sales price x qty x term) \ discount amount = total price (= net sales price x qty x term) \+ tax amount = total amount where system discount comes from price tags and discount tags (read only, admin configured) discount comes from discretionary discounts (header level or product level) subscription term https //docs nue io/understanding subscription term basis and proration is forced to 1 for one time and credit revenue models minimal examples the two examples below show the same quote request in preview and commit modes both execute the identical pricing pipeline — the only difference is whether the result is persisted to the database preview mode ( iscommit = false ) is ideal for validating structure and pricing before creating a real record; commit mode ( iscommit = true ) saves the quote and all quotelineitems as salesforce records see preview mode vs commit mode for a deeper comparison preview mode ( iscommit = false ) // 1 find an opportunity with an account opportunity opp = \[select id, accountid from opportunity where accountid != null limit 1]; // 2 build the request ruby globalapitypes createquoterequest request = new ruby globalapitypes createquoterequest(); request quote = new quote( opportunityid = opp id, name = 'my first api quote', ruby subscriptionstartdate c = date today(), ruby subscriptionenddate c = date today() addmonths(12), ruby subscriptionterm c = 12, ruby subscriptiontermdimension c = 'month' ); ruby globalapitypes productinput product = new ruby globalapitypes productinput(); product productsku = 'nue platform'; product uom = 'user/month'; product quantity = 10; request products = new list\<ruby globalapitypes productinput>{ product }; request iscommit = false; // preview only — no database writes // 3 call the api ruby globalapitypes createquoteresponse response = ruby globalquoteserviceapi createquote(request); // 4 inspect results system debug('status ' + response status); if (response status == 'succeed') { // quote id is null in preview mode — nothing was saved system assertequals(null, response data quote id); // check for warnings if (response warnings != null) { for (ruby globalapitypes message w response warnings) { system debug('warning ' + w\ errortype + ' — ' + w\ message); } } // pricing is fully calculated even without persisting system debug('line items ' + response data lineitems size()); for (ruby globalapitypes quotelineitemdto li response data lineitems) { quotelineitem qli = li quotelineitem; system debug(qli product2 stockkeepingunit + ' | qty=' + qli quantity + ' | listprice=' + qli ruby listprice c + ' | totalprice=' + qli ruby totalprice c); } } else { // handle errors with structured error codes for (ruby globalapitypes message err response errors) { system debug('error \[' + err errortype + '] ' + err message); } } commit mode ( iscommit = true ) // 1 find an opportunity with an account opportunity opp = \[select id, accountid from opportunity where accountid != null limit 1]; // 2 build the request (same structure as preview) ruby globalapitypes createquoterequest request = new ruby globalapitypes createquoterequest(); request quote = new quote( opportunityid = opp id, name = 'my first api quote', ruby subscriptionstartdate c = date today(), ruby subscriptionenddate c = date today() addmonths(12), ruby subscriptionterm c = 12, ruby subscriptiontermdimension c = 'month' ); ruby globalapitypes productinput product = new ruby globalapitypes productinput(); product productsku = 'nue platform'; product uom = 'user/month'; product quantity = 10; request products = new list\<ruby globalapitypes productinput>{ product }; request iscommit = true; // commit — persist quote and quotelineitems to database // 3 call the api ruby globalapitypes createquoteresponse response = ruby globalquoteserviceapi createquote(request); // 4 inspect results system debug('status ' + response status); if (response status == 'succeed') { // quote id is a real salesforce record id after commit system assertnotequals(null, response data quote id); system debug('created quote ' + response data quote id); // line items are also persisted with salesforce ids for (ruby globalapitypes quotelineitemdto li response data lineitems) { quotelineitem qli = li quotelineitem; system debug(qli product2 stockkeepingunit + ' | qty=' + qli quantity + ' | listprice=' + qli ruby listprice c + ' | totalprice=' + qli ruby totalprice c); } // re query to confirm persistence quote savedquote = \[select id, name, ruby totalprice c from quote where id = \ response data quote id]; system debug('saved quote ' + savedquote name + ' | total ' + savedquote ruby totalprice c); } else { for (ruby globalapitypes message err response errors) { system debug('error \[' + err errortype + '] ' + err message); } } creating a primary quote ( ruby isprimaryquote c = true ) when you set ruby isprimaryquote c = true on the quote header in the createquote request, the committed quote is automatically designated as the primary quote for its opportunity this triggers an asynchronous sync that creates opportunitylineitems matching all non summary quotelineitems — giving the sales team and reporting dashboards an accurate view of the deal's products and pricing directly on the opportunity this is especially valuable when combined with automating order generation from primary quote at key opportunity stages https //docs nue io/automating order generation from primary quote at key opportunity stages when the opportunity reaches a configured stage (e g , "closed won"), nue can automatically generate an order from the primary quote — completing the full quote to cash cycle without manual intervention how it works the createquote call with iscommit = true persists the quote and all quotelineitems because ruby isprimaryquote c = true , the quotelineitem trigger enqueues an opportunityproductsyncqueueable the async job creates one opportunitylineitem per non summary quotelineitem, copying pricing fields (quantity, totalprice, discount, etc ) each opportunitylineitem has a ruby primaryquoteline c back reference to its source quotelineitem the opportunity's ruby primaryquote c lookup is set to the committed quote note "summary" line items ( summaryitem , splititem ) are excluded from sync — these are internal rollup lines created for ramp pricing only lineitem and rampitem types sync to the opportunity note the account and opportunity must exist in a separate transaction before calling createquote with iscommit = true salesforce does not allow callouts (which the pricing engine uses) after uncommitted dml in the same transaction // prerequisites account and opportunity already exist (created in a prior transaction) opportunity opp = \[select id, accountid from opportunity where name = 'enterprise deal q1' limit 1]; // build the request — same as a normal committed quote, plus isprimaryquote ruby globalapitypes createquoterequest request = new ruby globalapitypes createquoterequest(); request quote = new quote( opportunityid = opp id, name = 'enterprise deal — primary quote', ruby subscriptionstartdate c = date today(), ruby subscriptionterm c = 12, ruby subscriptiontermdimension c = 'month', ruby isprimaryquote c = true // ← designate as primary quote ); ruby globalapitypes productinput platform = new ruby globalapitypes productinput(); platform productsku = 'nue platform'; platform uom = 'user/month'; platform quantity = 25; ruby globalapitypes productinput bundle = new ruby globalapitypes productinput(); bundle productsku = 'nue gem edition'; bundle uom = 'user/month'; bundle quantity = 10; request products = new list\<ruby globalapitypes productinput>{ platform, bundle }; request accountid = opp accountid; request iscommit = true; ruby globalapitypes createquoteresponse response = ruby globalquoteserviceapi createquote(request); system debug('status ' + response status); if (response status == 'succeed') { system debug('created primary quote ' + response data quote id); system debug('line items ' + response data lineitems size()); // the quote is now persisted as the primary quote for this opportunity // the async sync will create opportunitylineitems automatically // after the sync completes, you can verify // // list\<opportunitylineitem> olis = \[ // select id, product2 stockkeepingunit, quantity, ruby totalprice c, // ruby primaryquoteline c // from opportunitylineitem // where opportunityid = \ opp id // ]; // system assertequals(response data lineitems size(), olis size()); } what gets synced to each opportunitylineitem opportunitylineitem field source (quotelineitem) quantity quantity ruby totalprice c ruby totalprice c discount discount ruby discount c ruby discount c ruby discountamount c ruby discountamount c ruby subscriptionstartdate c ruby subscriptionstartdate c ruby subscriptionenddate c ruby subscriptionenddate c ruby subscriptionterm c ruby subscriptionterm c ruby deltaacv c ruby deltaacv c ruby deltaarr c ruby deltaarr c ruby deltatcv c ruby deltatcv c ruby primaryquoteline c id (back reference) see preview mode vs commit mode for the full comparison of preview vs commit behavior, including the async oli sync when to use this api use case example automated quoting generate quotes from an external cpq, crm, or sales tool ai driven quote generation nue ai leverages this api to create and validate quotes using structured error feedback bulk quote creation batch create quotes from imported deal data pricing preview calculate pricing without saving anything — what if scenarios integration middleware connect nue pricing to external systems (e commerce, partner portals) automated workflows create quotes in salesforce flows via @invocablemethod wrappers testing & validation programmatically verify pricing rules, discount tags, and bundle configurations order creation create orders directly (bypassing the quote to order flow) for system integrations articles in this guide \# article description 00 overview this article 01 api reference complete type definitions for requests, responses, and dtos 02 preview mode vs commit mode when to use iscommit=false vs iscommit=true 03 standalone products creating quotes with one or more standalone products 04 bundles bundles, bundled items, required add ons, optional add ons 05 price tags & discount tags applying price/discount tags by code or id 06 discounts header, product, and parent level discretionary discounts 07 subscription terms & dates term overrides, future start dates, evergreen, auto renew 08 multi attribute pricing account type auto resolution and custom pricing attributes 09 quantity tier attributes dynamic discount tiers based on field values (e g , headcount) 10 advanced bundle scenarios dynamic options, nested bundles, per addon overrides 11 custom fields passing custom fields on the quote header 12 multi currency creating quotes in eur, jpy, cad, and other currencies 13 validation & error handling error codes, warning codes, and troubleshooting 14 create order api creating orders directly (bypassing quote to order)