Update Order Overview
Update Order Overview
The Update Order API changes an existing Draft order and re-runs the same pricing and tax cascade the Nue Line Editor runs internally. It is the write counterpart to Create Order, and the exact mirror of Update Quote.
Quote and Order share the same Line Editor, the same pricing engine, and the same tax cascade, so every operation, action name, payload shape, ordering rule, recalc mode, and error code is identical. Read Update Quote Overview for the full contract. This page covers the endpoint and the handful of things that differ on orders.
Endpoints
Endpoint | Method | Description |
|---|---|---|
/cpq/orders/{orderId}:update | POST | Apply one or more update actions to an existing Draft order |
There is no separate preview endpoint. Preview is isCommit: false on the same call.
Action-oriented, not payload-oriented
You do not send the order you want. You send an ordered list of actions naming what to do, each with its own payload. Fields you omit are untouched; lines you omit are untouched. A line is deleted only when you ask for it by id in a deleteLineItems action -- the server never infers a deletion from a shorter line list.
This is what separates the Update Order API from PATCH /api/objects/Order/{id}. The Objects API writes the field you send and recalculates nothing, leaving totals and tax stale. Partner payout is an exception: PATCH /api/objects/Order/{id} with partnerPayoutPercentage or partnerPayoutAmount re-prices the order, with the same validation as Update Order, while partner fields are not supported on PATCH /api/objects/OrderList and POST /api/objects/composite/*List, which return 400 wherever your API role can reach them. This API applies the change and re-runs pricing, roll-ups, and tax in the same transaction.
See Action-oriented, not payload-oriented on the Quote page for the full explanation.
Request envelope
{
"isCommit": true,
"recalc": "AUTO",
"expectedLastModified": "2026-08-07T18:22:41.000Z",
"actions": [
{ "action": "<actionName>", "payload": { /* action-specific */ } }
]
}The order is identified by the {orderId} path parameter. Do not put orderId in the body.
The six actions
Identical to the Quote API:
Action | Payload |
|---|---|
updateHeaderFields | { "fieldValues": { ... } } |
updateLineItems | { "lineItems": [ { "lineItemId": "...", "fieldValues": { ... } } ] } |
deleteLineItems | { "lineItemIds": [ "..." ] } |
addLineItems | { "products": [ ProductInput ] } |
replacePriceTags | { "priceTags": [ { "<old>": "<new>" } ], "target": [...], "includeChildren": true } |
removePriceTags | { "priceTags": [ "<idOrCode>" ], "target": [...], "includeChildren": true } |
Actions are re-ordered server-side into the canonical sequence -- header fields, deletes, price-tag actions, line updates, adds. updateHeaderFields may appear at most once, and each line may be targeted at most once; both violations return CONFLICTING_ACTIONS.
The sort is stable by stage: within a stage, actions keep the order you sent them. That is immaterial everywhere except the price-tag stage, where removePriceTags and replacePriceTags share a stage and their target line ids are not covered by the conflict check -- so two overlapping tag actions run in submitted order and the result depends on it. See Action ordering.
What differs from the Quote API
 | Quote | Order |
|---|---|---|
Path | /cpq/quotes/{quoteId}:update | /cpq/orders/{orderId}:update |
Editability gate | Quote must not be activated, ordered, or locked for approval -- QUOTE_NOT_EDITABLE | Order must be in Draft status -- ORDER_NOT_DRAFT |
Not-found error | QUOTE_NOT_FOUND | ORDER_NOT_FOUND |
Concurrency error | QUOTE_MODIFIED | ORDER_MODIFIED |
Header object validated against | Quote | Order |
Line object validated against | QuoteLineItem | OrderProduct |
Invalid-field message | Invalid field for quote: ... | Invalid field for order: ... |
Invalid line-field message | Invalid field for quote line item: ... | Invalid field for order product: ... |
Response keys | quote, quoteLineItems | order, orderProducts |
Contract-value fields | ACV, TCV | orderACV, orderTCV |
Apex global method | GlobalQuoteServiceAPI.updateQuote | GlobalOrderServiceAPI.updateOrder |
Draft only. An activated order cannot be edited through this API. Changes to live, activated subscriptions -- renewals, quantity changes, cancellations, and bundle reconfigurations -- go through the Change Order process instead.
Field naming: use Nue field names over REST
fieldValues keys are validated against the Nue object describe for Order (header actions) and OrderProduct (line actions). Use the Nue camelCase API names -- name, paymentTerm, billCycleDay, subscriptionTerm, quantity, discount, netSalesPrice, includedUnits, evergreen. Custom fields use their Salesforce API name (Region__c).
An unknown key fails at the proxy with 400 Invalid field for order: <name>. Salesforce field API names such as SubscriptionTerm__c are for the Apex global method, not for REST.
Recalculation modes
AUTO (default), PRICING_ONLY, TAX_ONLY, NONE -- case-sensitive, same semantics as on quotes. See Recalculation modes.
Pricing engine plugins run on the update exactly as they do on create -- but only when the request actually reaches the pricing pass. They do not run under recalc: NONE or TAX_ONLY, nor under AUTO when you only touched non-repricing fields. Preview runs them too. See Pricing engine plugins and recalc.
Examples
Patch order header fields
curl -X POST 'https://api.nue.io/cpq/orders/801cU000004AbcDEFG: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 Order",
"poNumber": "PO-2026-0417",
"paymentMethod": "Invoice"
}
}
}
]
}'Edit a line and preview the impact
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: "802cU00000AbcDeFGH", fieldValues: { quantity: 30, discount: 15 } }
]
}
}
]
};
const res = await fetch(
'https://api.nue.io/cpq/orders/801cU000004AbcDEFG:update',
{ method: 'POST', headers: myHeaders, body: JSON.stringify(edit) }
);
const result = await res.json();
console.log(`Preview total: ${result.order.totalAmount}`);
console.log(`Preview order ACV: ${result.order.orderACV}`);Add a product and delete another in one call
curl -X POST 'https://api.nue.io/cpq/orders/801cU000004AbcDEFG: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": ["802cU00000ZzzYyXWV"] }
}
]
}'Add a bundle option (reconfigure)
There is no reconfigure action -- you add, re-quantify, or delete the bundle's option lines. parentLineId points at the bundle line.
{
"isCommit": true,
"recalc": "AUTO",
"actions": [
{
"action": "addLineItems",
"payload": {
"products": [
{
"parentLineId": "802cU00000BundleAA",
"productSku": "ADDON_SUPPORT",
"uom": "License/Month",
"quantity": 2
}
]
}
}
]
}Line-level custom fields on an added line
{
"action": "addLineItems",
"payload": {
"products": [
{
"productSku": "NUE_ON_SALESFORCE",
"uom": "User/Month",
"quantity": 25,
"customFields": { "CostCenter__c": "CC-1042" }
}
]
}
}See Custom Fields.
Response
{
"order": {
"id": "801cU000004AbcDEFG",
"name": "Acme - Q3 Expansion Order",
"status": "Draft",
"totalAmount": 48250.00,
"orderACV": 41000.00,
"orderTCV": 41000.00,
"tax": 3862.50,
"currencyIsoCode": "USD"
},
"orderProducts": [
{ "id": "802cU00000AbcDeFGH", "quantity": 30, "discount": 15, "netSalesPrice": 1254.60, "totalPrice": 37638.00 },
{ "id": "802cU00000NewLineA", "quantity": 5, "netSalesPrice": 2270.00, "totalPrice": 11350.00, "changeType": "NewProduct" }
],
"warnings": []
}Field | Type | Description |
|---|---|---|
order | object | The order header with recalculated pricing, metrics, and tax |
orderProducts | array | Every order product after the update, with hierarchy |
warnings | array | Non-blocking advisories -- { level, code, message } |
Failures return 400 or 500 with { status, error, message, errorDetails[] }. Each errorDetails entry has an errorCode and a message. See Response on the Quote page.
What you can change
The rules are identical to quotes, with OrderProduct field names in place of QuoteLineItem names.
Header fields never updatable -- FIELD_NOT_UPDATABLE: totalAmount, totalAmountWithoutTax, subtotal, listTotal, grandTotal, systemDiscount, systemDiscountAmount, totalCommittedAmount, sellerTotalAmount, tax, orderACV, orderTCV, todayARR, todayCMRR, id.
Deliberately updatable inputs: discount, discountAmount, totalPrice, partnerPayoutPercentage, partnerPayoutAmount.
Partner payout. Send one of the two partner fields; Nue derives the other. Sending both returns PARTNER_PAYOUT_MUTUALLY_EXCLUSIVE. Use the Nue names -- the Ruby__PartnerPayout*__c names return 400 Invalid field. Beyond the quote rules in Partner payout:
Condition | Error |
|---|---|
partnerPayoutPercentage outside 0-100, or a payout amount that is negative, greater than the total price, or greater than zero on an order whose total price is 0 | INVALID_PARTNER_PAYOUT_RANGE |
Partner field with recalc: TAX_ONLY or NONE | RECALC_MODE_NOT_SUPPORTED |
Partner field together with a change to status (for example activating the order) in the same request (re-sending the current status is fine). Update the partner payout first, then change the status | CONFLICTING_ACTIONS |
Change orders follow the change-quote rules: the header percentage applies to every change line, an amount converts to a percentage against the change total (summary line excluded), and a negative line's payout is the percentage x its total. The summary line's percentage is its payout divided by its own total. Set the payout here after the change order is created: POST /cpq/change-order and POST /cpq/change-order:preview ignore partner payout fields (the request succeeds and nothing is written). Under recalc: PRICING_ONLY, a change order keeps its stored tax and totalAmount = totalPrice + that tax.
Guarded separately: currencyIsoCode cannot change; the account cannot change; priceBookId cannot change once the order has priced lines (PRICEBOOK_CHANGE_NOT_ALLOWED).
Line fields that re-price: quantity, netSalesPrice, discount, discountAmount, subscriptionStartDate, subscriptionEndDate, subscriptionTerm, includedUnits, evergreen, plus line-direct Quantity Tier Attribute custom fields.
Line fields saved without re-pricing: billingPeriod, billingTiming, paymentTerm, billCycleDay, billCycleStartMonth, autoRenew, defaultRenewalTerm, description, cancellationDate.
Line fields never updatable: listPrice, listTotalPrice, unitPrice, netSellerPrice, subtotal, totalPrice, totalAmount, totalAmountWithoutTax, sellerTotalAmount, tax, systemDiscount, systemDiscountAmount, every delta* metric, actualQuantity, actualSubscriptionTerm, proratedQuantity, partnerPayoutPercentage, partnerPayoutAmount -- plus identity and relationship fields id, orderId, priceBookEntryId, productId, parentOrderProductId, productOptionId.
Line-type rules are the same: summary, split, ramp, and subscription-context lines are not editable; required and bundled children cannot be removed alone; removing a bundle root removes its whole descendant closure; change lines are removed only via their summary line.
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 address-only path. The taxable-address override (five taxation* fields, activated by taxationCountry) and the taxation-customer identity resolve independently, Order first, then Account. See Addresses and taxation.
Scope and limits
- Draft orders only. Activated orders go through the Change Order API.
- Large records are supported; a single request is bounded by Apex governor limits rather than by a fixed line-item count.
- Pricing and tax are synchronous callouts; budget at least one of each per recalc.
- Tax failures surface as warnings, not errors.
Apex global method
GlobalAPITypes.UpdateOrderResponse GlobalOrderServiceAPI.updateOrder(GlobalAPITypes.UpdateOrderRequest req)The order id is req.orderId, and fieldValues keys are Salesforce field API names (SubscriptionTerm__c), not Nue names. The response is { status, data, warnings, errors }. There is also an in-org apexrest surface at POST /services/apexrest/Ruby/updateOrder taking the same JSON body.
Full Apex documentation lives in the Nue Knowledge Center under Global Methods.
Related pages
- Create Order Overview -- the create counterpart
- Update Quote Overview -- the full contract
- Change Quotes and Orders -- for activated subscriptions