Update Quote Overview
Update Quote Overview
The Update Quote API changes an existing quote and re-runs the same pricing and tax cascade the Nue Line Editor runs internally. It is the write counterpart to Create Quote: same pricing engine, same product shape, same preview semantics -- applied to a quote that already exists.
Use it to change header attributes, edit or delete lines, add products and bundles, reconfigure a bundle's options, swap or remove price tags, and correct addresses -- with totals, metrics, and tax recalculated in the same call.
Everything on this page has an exact Order equivalent. See Update Order Overview.
Endpoints
Endpoint | Method | Description |
|---|---|---|
/cpq/quotes/{quoteId}:update | POST | Apply one or more update actions to an existing quote |
There is no separate preview endpoint. Preview is isCommit: false on the same call.
Action-oriented, not payload-oriented
This is the most important thing to understand about the API, and the way it differs from every other write surface in Nue.
You do not send the quote you want. You send what you want done.
| Payload-oriented (what this API is not) | Action-oriented (what this API is) |
|---|---|---|
You send | The full desired record and its lines | An ordered list of actions, each naming an operation and its own payload |
Server does | Diffs your document against stored state and infers intent | Executes exactly the operations you named |
Omitting a field means | Ambiguous -- clear it, or leave it alone? | Unambiguous -- it is untouched |
Omitting a line means | Ambiguous -- delete it, or leave it alone? | Untouched. Deletion only happens if you send deleteLineItems |
Concurrency | Last writer clobbers the whole record | Only the fields and lines you named are written |
Concretely: a PUT-style API that received a quote with three lines when the stored quote has five would have to guess whether you meant to delete two. This API never guesses -- you delete a line by asking for it, in a deleteLineItems action, by id.
Three consequences worth internalizing:
- Every action is a true patch. updateHeaderFields changes only the keys you include in fieldValues. updateLineItems changes only the keys you include on the lines you name. Everything else on the quote is left exactly as it was.
- There is no "desired line set." A lineSet-style whole-tree diff was designed and deliberately not built. Line changes are always delta actions: updateLineItems, deleteLineItems, addLineItems.
- Reconfiguring a bundle is not a verb. There is no reconfigureBundle action. You add, delete, or re-quantify the bundle's option lines, and the server rewires the bundle tree. See Reconfiguring a bundle.
Contrast with PATCH /api/objects/{objectName}/{id}. The generic Objects API writes the field you send and recalculates nothing -- a start date or address changed that way leaves the quote with stale totals and stale tax. The Update Quote API exists precisely to close that gap: it applies the change and re-runs pricing, roll-ups, and tax. Use the Objects API for administrative bookkeeping; use this API for anything that touches money, dates, quantity, or the line set.
Request envelope
{
"isCommit": true,
"recalc": "AUTO",
"expectedLastModified": "2026-08-07T18:22:41.000Z",
"actions": [
{ "action": "<actionName>", "payload": { /* action-specific */ } }
]
}Field | Type | Required | Default | Description |
|---|---|---|---|---|
isCommit | boolean | No | true | true persists the result. false runs the full cascade in memory and persists nothing. Must be a real boolean -- a string is rejected with 400 isCommit must be a boolean. |
recalc | string | No | AUTO | AUTO, PRICING_ONLY, TAX_ONLY, or NONE. Case-sensitive. See Recalculation modes |
expectedLastModified | string | No | -- | Optimistic-concurrency guard. When set, the update fails with QUOTE_MODIFIED unless it matches the quote's current last-modified timestamp |
actions | array | Yes | -- | Ordered list of actions. Must be non-empty |
The quote is identified by the {quoteId} path parameter. Do not put quoteId in the body -- the REST body has no such field. (The Apex global method does; see Global methods.)
The six actions
Action | Payload | What it does |
|---|---|---|
updateHeaderFields | { "fieldValues": { ... } } | Patches header fields -- standard and custom |
updateLineItems | { "lineItems": [ { "lineItemId": "...", "fieldValues": { ... } } ] } | Patches fields on existing lines, addressed by line id |
deleteLineItems | { "lineItemIds": [ "...", "..." ] } | Removes lines by id, cascading to their children and private tags |
addLineItems | { "products": [ ProductInput ] } | Adds products and bundles using the same recursive shape as Create Quote. Set parentLineId to add an option under an existing bundle |
replacePriceTags | { "priceTags": [ { "<oldIdOrCode>": "<newIdOrCode>" } ], "target": [...], "includeChildren": true } | Swaps a tag for another on every scoped line that carries the old tag |
removePriceTags | { "priceTags": [ "<idOrCode>" ], "target": [...], "includeChildren": true } | Removes tags from the scoped lines and reprices without them |
A seventh action, repriceQuote, is internal. Sending it is rejected. An unrecognized action name returns 400 Unknown action: <name>.
Action ordering is not your problem
You may send actions in any order. The server normalizes them into a canonical sequence before executing:
header fields → deletes → price-tag actions → line updates → adds
This ordering exists so that header context (price book, currency, dates, discount) is established before adds resolve their Price Book Entry, and so deletes settle before edits and adds are targeted.
The re-ordering is a stable sort by stage: actions move into their stage, and within a stage they keep the order you sent them in. For most requests that makes the result fully order-independent, because the conflict rules force each line to be targeted at most once, so the actions inside a stage cannot overlap.
The exception is the price-tag stage. removePriceTags and replacePriceTags occupy the same stage, and the CONFLICTING_ACTIONS check does not inspect their target line ids — only updateLineItems and deleteLineItems targets are checked. So two price-tag actions may legitimately touch the same lines, and when they do they execute in the order you sent them and the outcome depends on that order. If a removePriceTags and a replacePriceTags in one request overlap on the same lines or the same tag, order them deliberately, or split them into two requests.
Two conflicts are rejected before anything is written
Conflict | Result |
|---|---|
updateHeaderFields appears more than once in one request | CONFLICTING_ACTIONS -- combine them into a single action |
The same line is targeted by more than one action in one request | CONFLICTING_ACTIONS -- each line may be touched at most once |
A header pricing change and a line pricing change may coexist. The header discount is distributed across the lines that this request did not explicitly price.
Field naming: use Nue field names over REST
REST fieldValues keys are validated against the Nue object describe for Quote (header actions) and QuoteLineItem (line actions). Use the Nue camelCase API names -- quantity, discount, subscriptionTerm, netSalesPrice, paymentTerm, includedUnits, evergreen.
An unknown key fails fast at the proxy, before anything reaches Salesforce:
400 Invalid field for quote: PaymentTerm__c
400 Invalid field for quote line item: Quantity__cCustom fields are addressed by their Salesforce API name, exactly as on the Create Quote path -- Region__c, CostCenter__c.
Salesforce field API names (SubscriptionTerm__c, Discount__c) are for the Apex global methods, not for REST. The REST proxy rejects them. If you are porting Apex sample code to REST, translate every fieldValues key to its Nue name first.
Recalculation modes
recalc selects which callouts run. AUTO is the default and the right answer almost always.
Mode | What runs | Use it when |
|---|---|---|
AUTO | Re-prices and/or re-taxes based on which fields you actually changed | Default. You do not want to decide what to recompute |
PRICING_ONLY | Re-prices and rolls up header totals; skips tax | Tax is disabled or owned downstream; batch edits that defer tax to a final call; a fast price-only preview |
TAX_ONLY | Re-runs tax and tax roll-ups; leaves pricing untouched | An address, tax-code, or exemption change -- without disturbing negotiated prices |
NONE | Persists the change and recomputes nothing | Administrative, non-pricing fields only. Can leave stale totals |
An invalid value returns RECALC_INVALID. The check is case-sensitive: auto is not AUTO.
Under AUTO, whether pricing runs is decided by which fields you touched:
Header fields that drive a re-price: discount, discountAmount, totalPrice, partnerPayoutPercentage, partnerPayoutAmount, subscriptionStartDate, subscriptionEndDate, subscriptionTerm, subscriptionTermDimension.
Header fields that drive tax only: shippingStreet, shippingCity, shippingState, shippingPostalCode, shippingCountry, isShippingAddressSameAsBilling, the five taxation* address fields, taxationAccount, entityUseCode, taxCompanyCode.
Line fields that drive a re-price: quantity, netSalesPrice, discount, discountAmount, subscriptionStartDate, subscriptionEndDate, subscriptionTerm, includedUnits, evergreen -- plus any custom field mapped as a line-direct Quantity Tier Attribute, since editing one can move the resolved tier.
Everything else is persisted without a re-price. If you change a field that is not on these lists and you want it to affect price, that is what PRICING_ONLY is for.
Pricing engine plugins and recalc
Your active pricing engine plugins run as part of the update, exactly as they do on create. The reprice loads them and hands them to the engine on every pricing call, so a plugin that overrides a metric, adjusts a price, or emits a tag behaves the same whether the record was built by Create Quote or edited by Update Quote.
Which plugins run: every active plugin scoped HeaderObject, LineItem, or Any, on the BeforeCalculation, AfterCalculation, or AfterRampGeneration trigger events. Any-scoped plugins are included -- the update path deliberately uses the scope-inclusive lookup.
recalc decides whether your plugins run at all. Plugins execute only when the request actually triggers a pricing call. They do not run when recalc is NONE or TAX_ONLY, and they do not run under AUTO if you only touched fields that don't re-price (billing period, payment term, notes, a plain custom field). If you depend on a plugin to populate a custom metric or price, make sure the edit reaches the pricing pass -- send PRICING_ONLY if AUTO would skip it.
Preview runs them too. isCommit: false skips only the persist phase, so plugins execute on a preview exactly as on a commit -- worth knowing if a plugin of yours has side effects.
Two behaviors specific to the update path, as opposed to create:
- Only price tags already persisted on the line reach the engine; the Price Book Entry auto-tag channel is suppressed on a reprice, so a tag removed earlier is never resurrected by a later update.
- Tags emitted by a plugin are persisted with the result, including plugin tags that carry no identity of their own.
Preview vs commit
isCommit: false runs the identical code path -- load, apply actions, price, tax -- entirely in memory and stops before persistence.
| isCommit: false | isCommit: true (or omitted) |
|---|---|---|
Database writes | None | One bulk persist |
Pricing + tax callouts | Yes | Yes |
Response shape | Identical | Identical |
Side effects | None -- no change history, no result persistence | Full |
salesforceUrl in the response | Absent | Present |
Preview and commit return the same computed amounts field for field. Use preview to answer "what would +10 quantity do to ACV and tax?" without touching the record.
Transaction semantics
The whole update is one synchronous transaction, executed in three phases:
- Validate and apply, in memory. Every action is validated, then executed against an in-memory object graph. Header start/end dates are re-derived from line min/max, bundle trees are rewired, and the dirty-field analysis behind recalc: AUTO is computed. No callouts, no writes.
- Price and tax. Pricing rolls line subtotals up to header ACV/TCV/ARR/MRR/CMRR, re-applies tags, and propagates header-to-line values (auto-renew, payment term, billing frequency). Tax runs when taxation is enabled and the change touched an address, amount, quantity, or the line set.
- Persist. One bulk write of the fully computed result. Preview stops before this phase.
Because it is one transaction, a pricing failure rolls the entire update back -- nothing is written, not even the fields that would have succeeded.
Examples
All examples use the nue-api-key header and https://api.nue.io.
Patch header fields
Only the keys you send change. Everything else on the quote is untouched.
curl -X POST 'https://api.nue.io/cpq/quotes/0Q0cU000001AbcXUAV:update' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"isCommit": true,
"recalc": "AUTO",
"actions": [
{
"action": "updateHeaderFields",
"payload": {
"fieldValues": {
"name": "Acme - Q3 Expansion",
"paymentTerm": "Net 45",
"Region__c": "EMEA"
}
}
}
]
}'Edit a line, and preview it first
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
const edit = {
isCommit: false, // preview -- nothing is written
recalc: "AUTO",
actions: [
{
action: "updateLineItems",
payload: {
lineItems: [
{
lineItemId: "0QLcU00000AbcDeFGH",
fieldValues: { quantity: 30, discount: 15 }
}
]
}
}
]
};
const res = await fetch(
'https://api.nue.io/cpq/quotes/0Q0cU000001AbcXUAV:update',
{ method: 'POST', headers: myHeaders, body: JSON.stringify(edit) }
);
const result = await res.json();
console.log(`Preview total: ${result.quote.totalAmount}`);
console.log(`Preview ACV: ${result.quote.ACV}`);
// Happy with it? Send the identical body with isCommit: true.Add products and remove one, in a single call
The server orders the delete before the add, regardless of the order you send them in.
curl -X POST 'https://api.nue.io/cpq/quotes/0Q0cU000001AbcXUAV:update' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"isCommit": true,
"recalc": "AUTO",
"actions": [
{
"action": "addLineItems",
"payload": {
"products": [
{ "productSku": "NUE_PLATFORM", "uom": "User/Month", "quantity": 5 }
]
}
},
{
"action": "deleteLineItems",
"payload": { "lineItemIds": ["0QLcU00000ZzzYyXWV"] }
}
]
}'Reconfiguring a bundle
There is no reconfigure action. You add an option under the bundle (parentLineId), re-quantify an existing option, or delete one.
# Add an optional add-on to a configured bundle
curl -X POST 'https://api.nue.io/cpq/quotes/0Q0cU000001AbcXUAV:update' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"isCommit": true,
"recalc": "AUTO",
"actions": [
{
"action": "addLineItems",
"payload": {
"products": [
{
"parentLineId": "0QLcU00000BundleAA",
"productSku": "ADDON_SUPPORT",
"uom": "License/Month",
"quantity": 2
}
]
}
}
]
}'# Change a bundle option's quantity
{ "action": "updateLineItems",
"payload": { "lineItems": [ { "lineItemId": "0QLcU00000OptionAA",
"fieldValues": { "quantity": 5 } } ] } }
# Drop an optional add-on
{ "action": "deleteLineItems", "payload": { "lineItemIds": ["0QLcU00000OptionAA"] } }The server compares the resulting selection against the bundle's current children, rewires parent/root/summary links, and re-applies price tags, pricing attributes, and price dimensions to the rebuilt lines. Quantity-linked options recompute against the bundle quantity.
Remove and replace price tags
{
"isCommit": true,
"actions": [
{
"action": "removePriceTags",
"payload": {
"priceTags": ["VOLUME_TIER_2026"],
"target": ["0QLcU00000AbcDeFGH"],
"includeChildren": true
}
}
]
}{
"isCommit": true,
"actions": [
{
"action": "replacePriceTags",
"payload": {
"priceTags": [ { "PARTNER_DISC_10": "PARTNER_DISC_15" } ]
}
}
]
}Tags are referenced by id or code. target scopes the action to specific lines; omit it to apply to every line. includeChildren defaults to true and extends a targeted parent to its children. Removing a tag that no scoped line carries returns 200 with a NO_APPLICABLE_LINES_FOR_TAG warning, not an error.
Correct an address and re-tax without re-pricing
{
"isCommit": true,
"recalc": "TAX_ONLY",
"actions": [
{
"action": "updateHeaderFields",
"payload": {
"fieldValues": {
"shippingStreet": "500 Boren Ave N",
"shippingCity": "Seattle",
"shippingState": "WA",
"shippingPostalCode": "98109",
"shippingCountry": "US"
}
}
}
]
}TAX_ONLY re-runs tax and the tax roll-up and leaves every negotiated price alone. See Addresses and taxation.
Response
200 OK returns the recalculated quote, its lines, and any warnings.
{
"quote": {
"id": "0Q0cU000001AbcXUAV",
"name": "Acme - Q3 Expansion",
"totalAmount": 48250.00,
"ACV": 41000.00,
"TCV": 41000.00,
"tax": 3862.50,
"currencyIsoCode": "USD"
},
"quoteLineItems": [
{ "id": "0QLcU00000AbcDeFGH", "quantity": 30, "discount": 15, "netSalesPrice": 1254.60, "totalPrice": 37638.00 },
{ "id": "0QLcU00000NewLineA", "quantity": 5, "netSalesPrice": 2270.00, "totalPrice": 11350.00, "changeType": "NewProduct" }
],
"warnings": []
}Field | Type | Description |
|---|---|---|
quote | object | The quote header with recalculated pricing, metrics, and tax |
quoteLineItems | array | Every line on the quote after the update, with hierarchy |
warnings | array | Non-blocking advisories -- { level, code, message } |
Errors do not come back in the 200 body. A failed update returns 400 (validation or business-rule failure) or 500, with the standard error envelope:
{
"status": 400,
"error": "Failed to update quote",
"message": "Line '0QLcU00000AbcDeFGH' is a summary line and cannot be edited directly.",
"errorDetails": [
{ "code": "LINE_NOT_EDITABLE", "message": "..." }
]
}What you can change
Header fields
Header fields are patchable except for engine-owned outputs and identity.
Never updatable -- calculated by the pricing engine. Sending any of these returns FIELD_NOT_UPDATABLE: totalAmount, subtotal, listTotal, grandTotal, systemDiscount, systemDiscountAmount, totalCommittedAmount, sellerTotalAmount, tax, ACV, TCV, todayARR, todayCMRR, and id.
Guarded separately:
Field | Rule |
|---|---|
currencyIsoCode | Cannot be changed. Over REST the proxy rejects it first with currencyIsoCode is not updatable. |
customerId / account | Cannot be changed on an existing quote |
priceBookId | Cannot be changed once the quote has priced lines -- PRICEBOOK_CHANGE_NOT_ALLOWED |
Deliberately updatable inputs, despite looking like outputs: discount, discountAmount, totalPrice, partnerPayoutPercentage, and partnerPayoutAmount. Header totalPrice behaves as a target total -- the engine back-derives the discount that hits it. Header discountAmount is a conserved target, allocated across lines.
Cascades that fire on a header change, exactly as in the Line Editor:
- Header start/end dates re-derive from line min/max.
- A header start-date change pushes down to line dates; end dates are recomputed from term.
- A header term change co-terminates lines and re-annualizes metrics, converting per line UOM dimension.
- paymentTerm and billCycleDay push down to lines.
- autoRenew propagates to Recurring and Usage lines only.
- A header discount is distributed across lines that this request did not explicitly price. Bundled child lines are excluded.
- When both discount and discountAmount are sent, the percentage wins.
Line fields
Not every row in the Line Editor is editable, and the API follows the same rules.
Line type | Editable | Deletable | Rule |
|---|---|---|---|
Regular line item | Yes | Yes | Fully editable, including lines inside a bundle |
Ramp segment (RampItem) | No | No | System-generated from the ramp schedule. Sending a date returns RAMP_SEGMENT_DATES_LOCKED; anything else returns LINE_NOT_EDITABLE. Removable only with its parent |
Summary line (SummaryItem) | No | Yes -- cascades | A roll-up of the lines beneath it. Change the pieces, not the summary. Removing it removes the whole group |
Split line (SplitItem) | No | No | Managed by the split-quote flow |
Subscription-context line (changeAssetId set) | No | No | Belongs to the change-order flow |
Required / bundled child | Quantity follows the parent | No | Locked to the bundle. Remove or reconfigure the parent instead -- REMOVE_REQUIRED_CHILD |
Change line (cancel / upgrade / downgrade / swap / qty / term / renew) | No | Not on its own | Remove its summary line to remove the whole change -- REMOVE_CHANGE_LINE_RESTRICTED |
NewProduct line on a change order | Yes | Yes | A net-new line; behaves normally |
Line fields you may set:
Category | Fields | Re-prices |
|---|---|---|
Quantity and price levers | quantity, discount, discountAmount, netSalesPrice | Yes |
Term and dates | subscriptionTerm, subscriptionStartDate, subscriptionEndDate, evergreen, firstFullCreditPeriodStartDate | Yes |
Metering | includedUnits | Yes |
Billing and renewal | billingPeriod, billingTiming, paymentTerm, billCycleDay, billCycleStartMonth, autoRenew, defaultRenewalTerm, renewalUpliftPercent | No |
Structure and admin | description, milestones, lineBucketId, cancellationDate | No |
Custom fields | Any custom field by API name | Only if it feeds tiered pricing (a line-direct Quantity Tier Attribute) |
Line fields you may not set -- calculated and overwritten on recalc. Sending one returns FIELD_NOT_UPDATABLE: listPrice, listTotalPrice, salesPrice, unitPrice, netSellerPrice, subtotal, totalPrice, totalAmount, sellerTotalAmount, taxAmount, systemDiscount, systemDiscountAmount, deltaACV, deltaARR, deltaCMRR, deltaTCV, deltaCommittedARR, deltaCommittedCMRR, actualQuantity, actualSubscriptionTerm, proratedQuantity, partnerPayoutPercentage, partnerPayoutAmount.
Relationship and identity fields are blocked too -- id, quoteId, orderId, priceBookEntryId, product2Id, parentId, productOption. A legitimate edit changes pricing, quantity, or schedule; never a line's identity or its place in a bundle.
A line-total override does not exist. totalPrice is non-updatable on a line. To reach a target line total, send discount or netSalesPrice instead. The header-level totalPrice target does work -- that override lives only at the header.
Precedence when several price levers are sent on one line: netSalesPrice beats discount beats discountAmount. When netSalesPrice wins, a NETSALESPRICE_APPLIED warning is returned.
Adding and removing lines
addLineItems takes the same recursive ProductInput shape as Create Quote -- productSku or productName, uom, quantity, startDate, endDate, subscriptionTerm, discount, priceTags, customPricingAttributes, customFields, and nested addOns. See Quote Data Reference for the full field list.
Two fields matter specifically on the update path:
Field | Meaning |
|---|---|
parentLineId | Adds this product as an option child under the existing bundle line with that id -- resolved against that bundle's live option config. May target the root bundle or a nested sub-bundle. Omit it for a top-level add |
productOptionId | Targets a specific bundle option slot explicitly, for example a dynamic option. When set, productSku/productName become optional |
Rules:
- Adding a bundle automatically pulls in its required children and required add-ons recursively, all the way down. You specify only the top of the tree.
- New lines are stamped changeType: NewProduct.
- Deleting a bundle root removes its entire descendant closure, together with each line's private price tags and tiers. You do not list the children.
- If no Price Book Entry survives the currency, UOM, and pricing-attribute filter, the add is rejected with NO_ELIGIBLE_PRICE_BOOK_ENTRY rather than silently doing nothing.
- sortOrder is re-sequenced only on insert.
Line-level custom fields
customFields on a ProductInput sets custom fields directly on the line being created, in the same call:
{
"action": "addLineItems",
"payload": {
"products": [
{
"productSku": "NUE_ON_SALESFORCE",
"uom": "User/Month",
"quantity": 25,
"customFields": {
"CostCenter__c": "CC-1042",
"ContractRef__c": "MSA-2026-0417"
}
}
]
}
}To set a custom field on a line that already exists, put it in the line's fieldValues under updateLineItems. See Custom Fields.
Addresses and taxation
An address or taxation-override change always triggers a tax recalculation when taxation is enabled, then rolls the header total up. recalc: TAX_ONLY is the cheap path for an address-only edit.
The header carries a taxation override with two independent parts:
Override | Request fields | Turns on when |
|---|---|---|
Taxable address | taxationStreet, taxationCity, taxationState, taxationPostalCode, taxationCountry | taxationCountry is non-null |
Taxation customer identity | taxationAccount | it is non-null |
Either can be used alone or both together; clearing one does not affect the other. Each resolves through its own cascade, first match wins:
Step | Taxable address | Taxation customer identity |
|---|---|---|
1 | Quote taxation address -- if taxationCountry is set | Quote taxationAccount -- if set |
2 | Account taxation address -- if taxationCountry is set | Account taxation customer -- if set |
3 | Quote shipping address (default) | Account id and name (default) |
4 | Account shipping address (fallback) | -- |
Each level is atomic: the chosen level's fields are used as-is and are never merged across levels.
The taxable address is honored by AvaTax, Stripe Tax, Sphere, and Anrok. The taxation customer identity is honored by Anrok only, where it matches the exemption certificate. On the other integrations it is saved but ignored -- a no-op, not an error.
Resolved taxation values freeze when the invoice activates. Change orders and renewals carry their own copy; cancellations, credit memos, refunds, voids, and write-offs reuse the originating order's frozen values.
Editability gates
Before any action runs, the record itself must be editable.
Gate | Error |
|---|---|
Quote is activated, ordered, or locked for approval | QUOTE_NOT_EDITABLE |
Order is not in Draft status | ORDER_NOT_DRAFT |
Record id does not exist | QUOTE_NOT_FOUND / ORDER_NOT_FOUND |
expectedLastModified does not match | QUOTE_MODIFIED / ORDER_MODIFIED -- nothing is persisted |
Caller lacks edit permission | INSUFFICIENT_ACCESS |
Common error codes
Code | Meaning |
|---|---|
UNKNOWN_ACTION | The action name is not one of the six |
ACTION_NOT_ALLOWED | An internal-only action was sent in a public request |
CONFLICTING_ACTIONS | Duplicate updateHeaderFields, or one line targeted twice |
RECALC_INVALID | recalc is not AUTO, PRICING_ONLY, TAX_ONLY, or NONE |
INVALID_FIELD | The field does not exist on the target object |
FIELD_NOT_UPDATABLE | The field is system-managed or engine-calculated |
PRICEBOOK_CHANGE_NOT_ALLOWED | priceBookId changed on a quote that already has priced lines |
LINE_NOT_EDITABLE | Summary, split, ramp, or subscription-context line |
QUANTITY_NOT_EDITABLE | The line's quantity is controlled by its bundle parent |
RAMP_SEGMENT_DATES_LOCKED | Ramp-segment dates are owned by the ramp schedule |
REMOVE_REQUIRED_CHILD | A required or bundled child cannot be removed alone |
REMOVE_RAMP_SEGMENT | A ramp segment cannot be removed alone |
REMOVE_CHANGE_LINE_RESTRICTED | Remove the summary line to remove the whole change |
NO_ELIGIBLE_PRICE_BOOK_ENTRY | No PBE survived the currency / UOM / attribute filter |
INVALID_PRICE_TAG | The price tag was not found or is inactive |
INVALID_LINEITEM_REFERENCE | The targeted line does not belong to this record |
PRICING_ENGINE_ERROR | Pricing failed; nothing was committed |
Common warnings: NETSALESPRICE_APPLIED, HEADER_DISCOUNT_APPLIED, PRODUCT_DISCOUNT_OVERRIDES_HEADER, NO_APPLICABLE_LINES_FOR_TAG, TAX_CALCULATION_FAILED, QUANTITY_NOT_EDITABLE.
Scope and limits
This API is for quotes and for orders that have not been activated yet. Changes to live, activated subscriptions -- renewals, quantity changes, cancellations, and bundle reconfigurations on an activated order -- go through the Change Order process instead.
Limit | Detail |
|---|---|
Record size | Large records are supported. A single request is bounded by Apex governor limits -- pricing and tax run as synchronous callouts -- rather than by a fixed line-item count |
Callouts | Pricing and tax are synchronous HTTP callouts. Budget at least one of each per recalc. A header carrying partnerPayoutAmount without a percentage triggers a two-pass pricing call |
Tax failures | Surface as warnings, not hard errors, and leave amounts without tax |
Bundle reconfigure on a change quote | Not yet emitted as change lines under a Reconfigure parent. Pre-order, net-new bundles work today |
Editing the change carried by a change line | Not supported |
Apex global methods
In-org and managed-package callers can use the Apex global methods directly, with no HTTP:
GlobalAPITypes.UpdateQuoteResponse GlobalQuoteServiceAPI.updateQuote(GlobalAPITypes.UpdateQuoteRequest req)
GlobalAPITypes.UpdateOrderResponse GlobalOrderServiceAPI.updateOrder(GlobalAPITypes.UpdateOrderRequest req)Two differences from REST:
- The record id is a field on the request (req.quoteId / req.orderId), not a path parameter.
- fieldValues keys are Salesforce field API names (SubscriptionTerm__c, Discount__c), case-insensitive, with the namespace optional -- not the Nue camelCase names REST uses.
The response is { status, data, warnings, errors }, where status is SUCCESS or FAILURE and errors are returned in the payload rather than as an HTTP status.
There is also a Salesforce apexrest surface for in-org HTTP callers: POST /services/apexrest/Ruby/updateQuote and /services/apexrest/Ruby/updateOrder, taking the same JSON body as the global method.
Full Apex documentation, including request construction and worked examples, lives in the Nue Knowledge Center under Global Methods.
Related pages
- Create Quote Overview -- the create counterpart
- Update Order Overview -- the exact Order equivalent
- Change Quotes and Orders -- for activated subscriptions