Change Quotes and Orders
This guide introduces Lifecycle Manager Change Quotes and Orders and provides request examples for every change type. The same endpoint — POST /cpq/change-order — produces either a change quote or a change order depending on options.proceedOption, so it covers both. See the Create Change Order API reference for the full schema.
Lifecycle Manager — Change Orders
In Lifecycle Manager, a change order modifies a customer's existing subscriptions, assets, and entitlements after the initial sale — renewals, quantity and term changes, price adjustments, reconfigurations, cancellations, free-trial conversions, field updates, and cross-sell (adding brand-new products). A single change order can contain multiple changes and can also add new products in the same request.
Every change order is a POST /cpq/change-order with a top-level assetChanges array (the modifications to existing subscriptions) and, optionally, a top-level products array (brand-new products to add — see Cross-sell below).
Change quote vs. change order (proceedOption)
Two options fields control the outcome — the container (proceedOption) and whether it activates (activateOrder):
- proceedOption chooses the container the change lands in:
- CreateOrder — create a change order.
- CreateQuote — create a change quote for review/approval (optionally pass an opportunityId to tie it to an existing opportunity).
- UseExistingOrder / UseExistingQuote — append the change into an existing draft order or quote, identified by containedObjectId.
- activateOrder controls activation. Note that proceedOption: "CreateOrder" does not by itself activate — pair it with activateOrder to choose draft vs. activated.
proceedOption | activateOrder | Result |
|---|---|---|
CreateOrder | false | Draft change order |
CreateOrder | true | Activated change order |
CreateQuote | see note | Change quote for review/approval (optionally on an opportunityId) |
UseExistingOrder | false / true | Append into an existing draft order (containedObjectId); activate when true |
UseExistingQuote | see note | Append into an existing quote (containedObjectId) |
Quote modes + activateOrder: the published API contract states that with activateOrder: true a quote mode is "converted to an order and activated" — but the current runtime does not cleanly do that: quote modes return a quote ID, skip invoice generation, and the Apex activation helper only activates orders. Treat activateOrder on CreateQuote / UseExistingQuote as version-dependent and verify it in your target environment rather than relying on auto-activation.
// Draft change order — CreateOrder WITHOUT activation
{
"options": {
"proceedOption": "CreateOrder",
"activateOrder": false
},
"assetChanges": [...]
}
// Change quote tied to an opportunity
{
"options": {
"proceedOption": "CreateQuote",
"activateOrder": false,
"opportunityId": "006Ru00000K5bY9IAJ" // Optional: associate with opportunity
},
"assetChanges": [...]
}Note: the published Create Change Order API reference is intentionally minimal/generic — this guide is the practical examples page. See the Create Change Order API reference for schema field details only.
Cross-sell: adding new products
Beyond modifying existing subscriptions through assetChanges, a change order can add brand-new products in the same request using the top-level products array. For each entry in products, the behavior is exactly the same as adding new products in a new quote — same shape as Create Quote products, resolved and priced through the same pricing pipeline. The products array is additive to assetChanges, which remains required and non-empty.
The one difference is at the header: the new products' metrics — ARR, CMRR, TCV, and ACV — roll up into the change order header together with the metrics of the change itself. In other words, the header totals reflect both the assetChanges and the newly added products combined. Full details and examples are in New Product (Cross-Sell / Cancel + Replace).
Change types
Change type | changeType | What it does |
|---|---|---|
Update Quantity | UpdateQuantity | Increase or reduce quantity (delta) on a subscription, usage, credit, asset, or entitlement. |
Update Term | UpdateTerm | Add or subtract months from a subscription's term (term is a delta). |
Renew | Renew | Renew for another term, or convert to evergreen. |
Adjust Price | AdjustPrice | Apply a discount % or override the net sales price. |
Reconfigure | Reconfigure | Change a bundle's add-ons/configuration; also covers Upgrade/Downgrade/Swap. |
Cancel | Cancel | Cancel a subscription immediately or at end of term. |
New Product (Cross-Sell) | NewProduct / top-level products | Add a brand-new product or bundle; supports cancel-and-replace. |
Convert Free Trial | ConvertFreeTrial | Convert a free trial to a paid or evergreen subscription. |
Update Fields on Subscriptions | UpdateField | Update editable fields on an existing subscription. |
Co-Term | CoTerm | Align subscription end dates (see Update Term). |
Upgrade / Downgrade / Swap | Upgrade / Downgrade / Swap | Move a subscription to a different product/edition (see Reconfigure). |
Every assetChanges entry carries changeType and, for all types that modify an existing subscription, an assetNumber. NewProduct is the only type that does not require an assetNumber.
Update Quantity
UpdateQuantity isn't limited to subscription seats/licenses — the target assetNumber accepts a subscription number or an asset/entitlement ID, and it applies to any quantity-bearing line, including usage, credits, assets, and entitlements.
The quantity field in UpdateQuantity operations is a delta (change amount), not the final absolute quantity.
Current Quantity | Delta Value | Result |
|---|---|---|
10 seats | +25 | 35 seats |
100 licenses | -30 | 70 licenses |
50 users | +50 | 100 users |
Seat Expansion
Add 25 more user licenses to an existing subscription (e.g., from 10 → 35 seats):
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "UpdateQuantity",
"assetNumber": "SUB-086067",
"quantity": 25, // Delta: adds 25 to current quantity (10 + 25 = 35)
"startDate": "2024-01-01"
}
]
}Seat Reduction
Reduce quantity by 5 (e.g., from 35 → 30 seats):
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "UpdateQuantity",
"assetNumber": "SUB-086067",
"quantity": -5, // Delta: reduces current quantity (35 - 5 = 30)
"startDate": "2024-01-01"
}
]
}Update Term
term is a delta: the number of months to add (positive) or subtract (negative) from the current subscription term — not the final term. UpdateTerm changes the contract length; it does not convert billing frequency (e.g. monthly → annual).
Extend Subscription Term
Add 6 months to the current subscription:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "UpdateTerm",
"assetNumber": "SUB-086067",
"term": 6
}
]
}Extend the Term by a Year
Add 12 months to the current term. Because term is a delta, this extends the contract by a year — it does not change the billing frequency:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "UpdateTerm",
"assetNumber": "SUB-086067",
"term": 12
}
]
}Co-Terminate Subscriptions
Use changeType: "CoTerm" with a coTermDate to align one or more subscriptions to the same end date:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "CoTerm",
"assetNumber": "SUB-086067",
"coTermDate": "2024-12-31"
},
{
"changeType": "CoTerm",
"assetNumber": "SUB-086068",
"coTermDate": "2024-12-31"
}
]
}Renew
Standard Renewal
Renew a subscription for another 12 months:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Renew",
"assetNumber": "SUB-086067",
"renewalTerm": 12
}
]
}Convert to Evergreen
Change from a fixed-term to an evergreen subscription:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Renew",
"assetNumber": "SUB-086067",
"switchToEvergreen": true
}
]
}Adjust Price
Percentage Discount
Apply a 20% discount to a subscription (optionally cascading to bundle children):
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "AdjustPrice",
"assetNumber": "SUB-086067",
"startDate": "2024-01-01",
"discount": {
"discountPercentage": 20,
"applyToChildren": true
}
}
]
}discount.applyToChildren is not limited to the targeted line — when set on a bundle parent, the percentage discount cascades to the bundle's child subscriptions as well. Leave it off (or false) to discount only the line named by assetNumber.
Fixed Price Override
Set a specific net sales price for a subscription over a date range:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "AdjustPrice",
"assetNumber": "SUB-086067",
"netSalesPrice": 1200.00,
"startDate": "2024-01-01",
"endDate": "2024-12-31"
}
]
}True-up to Quantity Tier
When a subscription is priced by a quantity tier attribute (for example, total committed amount or number of hosts), set trueUpPrice: true to recompute the line to the correct tier for the attribute's current value, without sending a discount or a fixed price:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "AdjustPrice",
"assetNumber": "SUB-086067",
"trueUpPrice": true,
"startDate": "2024-01-01"
}
]
}Over REST, a true-up does not cascade to bundle children. The single-call applyToChildren cascade for trueUpPrice is available on the Apex Global Method only; the cpq REST API does not forward it yet. To true up a bundle over REST, send a separate trueUpPrice AdjustPrice for the parent and one for each child subscription. (The nested discount.applyToChildren shown under Percentage Discount is unaffected and still cascades a percentage change.)
Reconfigure
Reconfigure is a parent operation: its nested addOnChanges may contain the supported add-on operations, but the same bundle, its parent, or its child subscriptions must not also be targeted by separate top-level asset changes in the same request. Do not combine a top-level Reconfigure with other top-level change types against the same subscription tree.
Reconfigure does not auto-derive the diff for you — you submit the concrete add-on deltas yourself in addOnChanges, and Apex prices and persists what it receives. The supported add-on change types are exactly UpdateQuantity, Cancel, and NewProduct.
Adding Add-Ons to a Bundle
Add additional features to an existing bundle:
POST /cpq/change-order
{
"options": {
"activateOrder": false,
"proceedOption": "CreateQuote",
"opportunityId": "006Ru00000K5bY9IAJ"
},
"assetChanges": [
{
"changeType": "Reconfigure",
"assetNumber": "SUB-086067",
"startDate": "2024-01-15",
"addOnChanges": [
{
"changeType": "NewProduct",
"productOptionId": "a8fRu0000002bOmIAI",
"priceBookEntryId": "01uRu000004SyY7IAK",
"productOptionQuantity": 3,
"term": 12
}
]
}
]
}Complex Bundle Changes
Multiple add-on changes to a bundle in one operation:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Reconfigure",
"assetNumber": "SUB-086067",
"startDate": "2024-01-15",
"addOnChanges": [
{
"changeType": "NewProduct",
"productOptionId": "a8fRu0000002bOmIAI",
"priceBookEntryId": "01uRu000004SyY7IAK",
"productOptionQuantity": 2
},
{
"changeType": "UpdateQuantity",
"assetNumber": "SUB-086068",
"productOptionQuantityChange": 5
},
{
"changeType": "Cancel",
"assetNumber": "SUB-086069",
"cancellationDate": "2024-12-31"
}
]
}
]
}Upgrade, Downgrade & Swap
Move a subscription to a different product/edition with changeType: "Upgrade", "Downgrade", or "Swap", targeting the new product via targetPriceBookEntryId. All three share the same request shape — the changeType value is the only difference; the direction (is the target really higher/lower?) is validated by the engine.
For a standalone product, targetPriceBookEntryId + startDate is all you need:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Upgrade",
"assetNumber": "SUB-086067",
"targetPriceBookEntryId": "01uRu000004SyY8IAK",
"startDate": "2024-01-15"
}
]
}quantity is not allowed on Upgrade/Downgrade/Swap. UDS quantities are fully derived — continuing lines keep their source subscription's quantity, and new lines book at the target bundle's configured defaults. Sending quantity is rejected with PRODUCT_QUANTITY_NOT_ALLOWED. To grow or shrink at the same time, add a separate UpdateQuantity change on the same asset in the same request.
Bundles: Upgrade/Downgrade/Swap with UDS
When the source or target is a bundle, the change is resolved by Unified Data Structure (UDS) logic: the engine maps each source subscription (the bundle root and its child options) onto the target bundle's option slots. Two extra fields on the asset change control this:
Field | Type | Purpose |
|---|---|---|
addOns | Array | Declares the target bundle's configuration — which option slots are filled and with what. Each entry identifies its product one way (priceBookEntryId or productSku/productName) and names the slot with productOptionId. Recursive for nested bundles. Omit it to let the server build the target from the bundle's default configuration. |
transformations | Array | Pins that tell the engine how a specific source subscription maps onto the target when it is not obvious — see ambiguity and convergence below. |
If the target bundle has a required feature whose options aren't satisfiable by defaults, a bare U/D/S (no addOns) is rejected with FEATURE_OPTION_COUNT_OUT_OF_RANGE — supply addOns to choose the option(s):
POST /cpq/change-order
{
"options": {
"activateOrder": false,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Upgrade",
"assetNumber": "SUB-000014", // source bundle subscription (e.g. Nue Star Edition)
"targetPriceBookEntryId": "01uRu0000ESSNTL", // target bundle (e.g. Nue Essential Package)
"startDate": "2026-10-01",
"addOns": [
{
"productOptionId": "a0qRu000PRICEBLDR", // target slot: "Price Builder"
"priceBookEntryId": "01uRu000PRICEBLDR"
}
]
}
]
}When a bundle transforms, any source child option that has no matching slot in the target bundle is cancelled — the commit response reports it in messages as SOURCE_CANCELLED (e.g. Source SUB-000015 has no candidate on the target and is cancelled). This is expected; plan for child lines to drop when the target bundle is narrower than the source.
Preview → resolve → commit
For any non-trivial bundle change, preview first. POST /cpq/change-order:preview takes the same request body but persists nothing and returns per-source pricing — or, when a source can't be mapped unambiguously, an ambiguities[] block instead of pricing:
POST /cpq/change-order:preview // same body as the commit request- Preview tolerates ambiguity — it returns HTTP 200 with ambiguities[] (and a null priceSummary for that source).
- Commit does not — if you POST /cpq/change-order with an unresolved source, it is refused with HTTP 409 RESOLUTION_REQUIRED and the same ambiguities payload.
The loop is: preview → read the candidates → pin each unresolved source with a transformations entry → commit.
Resolving ambiguity (one source, many targets)
An ambiguity (a "tie") is when a single source subscription could map to more than one slot in the target bundle. The engine won't guess. A preview surfaces the choices:
// Response from POST /cpq/change-order:preview
{
"results": [
{
"sourceAssetNumber": "SUB-000019", // the source the rep must resolve
"assetNumber": "SUB-000018",
"priceSummary": null, // no price until the tie is resolved
"ambiguities": [
{
"sourceAssetNumber": "SUB-000019",
"sourceProductName": "QuickBooks Integration",
"candidates": [
{
"targetProductOptionId": "a0qRu000INTEGPREM",
"targetPriceBookEntryId": "01uRu000INTEGPREM",
"targetProductName": "Premium Integrations",
"changeType": "Upgrade",
"targetKind": "BUNDLE",
"listPrice": 30.0,
"recommended": true
},
{
"targetProductOptionId": "a0qRu000INTEGSTD",
"targetPriceBookEntryId": "01uRu000INTEGSTD",
"targetProductName": "Standard Integrations",
"changeType": "Swap",
"targetKind": "BUNDLE",
"listPrice": 9.9,
"recommended": false
}
]
}
]
}
]
}Pick a candidate and echo it back as a transformations pin keyed by the same sourceAssetNumber, then commit. A pin identifies its target exactly one way — either explicit (targetProductOptionId + targetPriceBookEntryId together) or by-values (uom / customPricingAttributes / billingTiming); sending both is rejected with TRANSFORMATION_IDENTITY_CONFLICT.
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Upgrade",
"assetNumber": "SUB-000018",
"targetPriceBookEntryId": "01uRu000TARGETBNDL",
"startDate": "2026-10-01",
"transformations": [
{
"sourceAssetNumber": "SUB-000019", // resolve the ambiguous source…
"targetProductOptionId": "a0qRu000INTEGPREM", // …onto the chosen slot
"targetPriceBookEntryId": "01uRu000INTEGPREM"
}
]
}
]
}Convergence (many sources, one target)
Convergence is the opposite of a tie: several source subscriptions legitimately map onto the same target slot and merge into one line. For example, a customer holds two separate integration add-ons — QuickBooks Integration and Nue on Salesforce — and the target bundle consolidates both under a single Nue Platform option. Both sources converge onto that one slot.
Unlike an ambiguity, convergence isn't a tie to break — each source maps to that shared slot deterministically. You express it by sending one pin per source, all naming the same target (converging pins from different sources are explicitly valid):
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Upgrade",
"assetNumber": "SUB-000018", // the source bundle root
"targetPriceBookEntryId": "01uRu000TARGETBNDL",
"startDate": "2026-10-01",
"transformations": [
{
"sourceAssetNumber": "SUB-000019", // QuickBooks Integration
"targetProductOptionId": "a0qRu000NUEPLATFORM",
"targetPriceBookEntryId": "01uRu000NUEPLATFORM"
},
{
"sourceAssetNumber": "SUB-000020", // Nue on Salesforce
"targetProductOptionId": "a0qRu000NUEPLATFORM", // same target slot → they converge
"targetPriceBookEntryId": "01uRu000NUEPLATFORM"
}
]
}
]
}On commit, the two source subscriptions are credited/updated and the target Nue Platform line carries the merged result — the engine converges them onto the single slot rather than creating two lines. As always, any source option that lands on no target slot is cancelled and reported via SOURCE_CANCELLED.
Cancel
Immediate Cancellation
Cancel a subscription immediately:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-086067",
"cancellationDate": "2024-01-01"
}
]
}End-of-Term Cancellation
Cancel at the end of the current term:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-086067",
"cancellationDate": "2024-12-31"
}
]
}New Product (Cross-Sell / Cancel + Replace)
There are two ways to add brand-new products as part of a change order:
- The top-level products array — the same shape and behavior as adding products on a new quote, with metrics rolling up into the change order header. Best for straightforward cross-sell and cancel-and-replace.
- The NewProduct change type inside assetChanges — for adding a product/bundle alongside other asset changes, including full bundle hierarchies with configured and dynamic add-ons.
When to use products[] vs NewProduct
Use… | When |
|---|---|
Top-level products[] | Straightforward cross-sell or cancel-and-replace; you want the new-quote product shape (resolve by productSku/productName, discounts, price tags) with header metric rollup. Recommended default. |
NewProduct in assetChanges | You need to add into a bundle's add-on slots (productOptionId), reference an explicit priceBookEntryId, or use NewProduct-specific fields alongside other asset changes. |
A NewProduct asset change must be accompanied by at least one other change (it cannot be the only change), and NewProduct is the only change type that does not require an assetNumber. Most builds also expect a non-empty assetChanges even when using top-level products[] (a products-only request may be rejected), but this is deployment-dependent — confirm it in your target environment.
Adding products via the products array (cross-sell)
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "UpdateQuantity",
"assetNumber": "SUB-086067",
"quantity": 25,
"startDate": "2024-01-01"
}
],
"products": [
{
"productSku": "SKU-ADDON-01",
"quantity": 5,
"subscriptionTerm": 12,
"subscriptionTermDimension": "Months"
}
]
}Each entry uses the new-product request shape and supports the fields accepted by the deployed API version — productSku/productName (or productOptionId to target a specific bundle option slot), quantity, startDate/endDate, subscriptionTerm, paymentTerm, coTermAsset (to co-terminate the new line with an existing subscription), and nested addOns. Verify discount, price-tag, and dynamic add-on behavior in your target environment — these have had API-layer limitations on the change-order products[] path (see the discounts note below).
Expansion (Renew + New Product)
Renew an existing subscription and add a brand-new product in the same change order. The assetChanges entry renews the existing line; the products entry adds the new line. Both roll up into the same change order header, so ARR, CMRR, TCV, and ACV reflect the renewal and the new product combined.
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Renew",
"assetNumber": "SUB-086067",
"renewalTerm": 12
}
],
"products": [
{
"productSku": "SKU-ADDON-01",
"quantity": 10,
"subscriptionTerm": 12,
"subscriptionTermDimension": "Months"
}
]
}Cancel and Replace (Swap Editions)
Cancel an existing product or bundle and replace it with a different one in a single change order — for example, replace Nue Gem Edition with Nue Rise Edition. The Cancel change removes the old subscription; the products entry adds the replacement. Header metrics (ARR, CMRR, TCV, ACV) net the cancellation against the replacement.
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-086067",
"cancellationDate": "2026-08-01"
}
],
"products": [
{
"productName": "Nue Rise Edition",
"quantity": 100,
"subscriptionTerm": 36,
"subscriptionTermDimension": "Months"
}
]
}When the item being replaced is a bundle, cancelling the original bundle cancels its child lines too, and the replacement bundle's children are added automatically — exactly as in a new quote.
Applying Discounts and Price Tags to New Products
Products added through products accept the same discounting inputs as a new quote. Provide exactly one of discount (percentage), discountAmount (fixed amount), or netSalesPrice (explicit unit price) per line, and/or attach priceTags (price and discount tags referenced by code or id).
Percentage discount:
"products": [
{
"productName": "Nue Rise Edition",
"quantity": 100,
"subscriptionTerm": 36,
"subscriptionTermDimension": "Months",
"discount": 15
}
]Fixed discount amount:
"products": [
{
"productSku": "SKU-ADDON-01",
"quantity": 10,
"discountAmount": 500
}
]Price / discount tags (same as a new quote):
"products": [
{
"productName": "Nue Rise Edition",
"quantity": 100,
"priceTags": [
{ "code": "PROMO-2026" }
]
}
]On the change-order products[] path, discount, discountAmount, netSalesPrice, and priceTags follow the new-quote product shape, but line-level discounts and price tags are conditionally supported and have had API-layer limitations (and preview defects) depending on the deployed version — verify them in your target environment before relying on them. When they do apply, the resulting line metrics roll up into the change order header alongside the change itself.
The NewProduct change type (bundles & add-ons)
Use the NewProduct change type inside assetChanges to add a standalone product or a full bundle hierarchy alongside other asset changes. The new product's term can be set explicitly (term) or co-termed with an existing subscription (coTermAsset).
Important: NewProduct changes must be accompanied by at least one other change in the same request (e.g., Cancel, UpdateQuantity, Renew). NewProduct cannot be the only change in a change order.
NewProduct Fields
Field | Type | Required | Description |
|---|---|---|---|
changeType | String | Yes | Must be "NewProduct" |
priceBookEntryId | String | Yes | Price book entry ID identifying the product to add |
startDate | Date | Yes | Effective start date for the new product |
quantity | Decimal | Yes | Quantity to add (must be positive) |
term | Decimal | One of term/coTermAsset | Subscription term in months. Mutually exclusive with coTermAsset |
coTermAsset | String | One of term/coTermAsset | Existing subscription number (e.g. SUB-000192) to co-term with. The new product's end date aligns to this subscription's end date |
netSalesPrice | Decimal | No | Override the calculated net sales price |
addOns | Array | No | Bundle add-ons to include with the product (see add-on structure below) |
autoRenew | Boolean | No | Whether the subscription should auto-renew |
defaultRenewalTerm | Decimal | No | Renewal term in months when auto-renew is enabled |
billingTiming | String | No | Billing timing, e.g. In Advance, In Arrears |
billingPeriod | String | No | Billing frequency, e.g. Month, Year |
billCycleDay | String | No | Day of the billing cycle |
billCycleStartMonth | String | No | Starting month for annual billing cycles |
priceTagIds | Array | No | Price tag IDs to apply discounts |
priceTagCodes | Array | No | Price tag codes (alternative to IDs) |
description | String | No | Custom description |
customFields | Object | No | Custom field values to populate on the order product |
Add-On Structure
Each add-on can contain nested addOns, supporting recursive bundle hierarchies.
Field | Type | Required | Description |
|---|---|---|---|
changeType | String | Yes | Must be "NewProduct" |
productOptionId | String | Yes (configured) | Product option ID for configured add-ons |
priceBookEntryId | String | Yes (dynamic) | Price book entry ID for dynamic add-ons |
productOptionQuantity | Decimal | Yes | Quantity of the add-on |
subscriptionTerm | Decimal | No | Term in months if different from parent |
coTermAsset | String | No | Co-term with an existing subscription |
subscriptionStartDate | Date | No | Start date if different from parent |
netSalesPrice | Decimal | No | Override net sales price for this add-on |
billingTiming | String | No | Billing timing override |
billingPeriod | String | No | Billing period override |
billCycleDay | String | No | Bill cycle day override |
billCycleStartMonth | String | No | Bill cycle start month override |
priceTagIds | Array | No | Price tag IDs |
priceTagCodes | Array | No | Price tag codes |
addOns | Array | No | Nested add-ons (recursive) |
customFields | Object | No | Custom field values |
Basic New Product
Cancel an existing subscription and add a new product with a 12-month term:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-000192",
"cancellationDate": "2025-02-15"
},
{
"changeType": "NewProduct",
"priceBookEntryId": "01uRu000004SyY8IAK",
"startDate": "2025-02-15",
"quantity": 3,
"term": 12
}
]
}New Product with Bundle Add-Ons
Add a new bundle product with configured and dynamic add-ons:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-000192",
"cancellationDate": "2025-02-15"
},
{
"changeType": "NewProduct",
"priceBookEntryId": "01uRu000004SyY8IAK",
"startDate": "2025-02-15",
"quantity": 3,
"term": 12,
"addOns": [
{
"changeType": "NewProduct",
"productOptionId": "a0jRu0000002bOmIAI",
"productOptionQuantity": 3
},
{
"changeType": "NewProduct",
"productOptionId": "a0jRu0000002bOnIAI",
"priceBookEntryId": "01uRu000004SyY7IAK",
"productOptionQuantity": 10
}
]
}
]
}New Product Co-Termed with Existing Subscription
Align the new product's end date to an existing subscription instead of specifying a term:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-000192",
"cancellationDate": "2025-02-15"
},
{
"changeType": "NewProduct",
"priceBookEntryId": "01uRu000004SyY8IAK",
"startDate": "2025-02-15",
"quantity": 5,
"coTermAsset": "SUB-000200"
}
]
}New Product with Price Override
Override the calculated price at both the parent and add-on level:
POST /cpq/change-order
{
"options": {
"activateOrder": false,
"proceedOption": "CreateQuote"
},
"assetChanges": [
{
"changeType": "Cancel",
"assetNumber": "SUB-000192",
"cancellationDate": "2025-02-15"
},
{
"changeType": "NewProduct",
"priceBookEntryId": "01uRu000004SyY8IAK",
"startDate": "2025-02-15",
"quantity": 3,
"term": 12,
"netSalesPrice": 450.00,
"addOns": [
{
"changeType": "NewProduct",
"productOptionId": "a0jRu0000002bOmIAI",
"productOptionQuantity": 10,
"netSalesPrice": 25.00
}
]
}
]
}Convert Free Trial
Convert Trial to Paid
Convert a free trial to a paid subscription:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "ConvertFreeTrial",
"assetNumber": "SUB-086067",
"startDate": "2024-01-15",
"term": 12,
"overrideTrialEnd": true
}
]
}Convert Trial to Evergreen
Convert a trial directly to an evergreen subscription:
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "ConvertFreeTrial",
"assetNumber": "SUB-086067",
"startDate": "2024-01-15",
"switchToEvergreen": true,
"coTermToBundleSubscription": false
}
]
}Update Fields on Subscriptions
Update editable fields on an existing subscription without changing its commercial terms — for example, updating the Description or a custom field. Use changeType: "UpdateField" with the target assetNumber and a fieldsToUpdate list of { fieldApiName, newValue } pairs. Values are always provided as strings and are coerced to the field's type on the Salesforce side. An optional startDate sets the effective date, and applyToChildren cascades the update to a bundle's child subscriptions. UpdateField is currently documented and tested as a standalone change type — send it on its own, not mixed with other change types (e.g. AdjustPrice, UpdateQuantity) in the same request.
UpdateField is deployment-dependent. It requires the corresponding backend and Salesforce metadata in the target environment (e.g. the change-type picklist value and any referenced fields); older REST deployments may not support it or may reject the request at validation before it reaches Apex. As of July 2026 some environments still have a registration gap — the change-type picklist value or the REST route for UpdateField may be missing even where the underlying Apex is present — so confirm support in your target environment before relying on it. Field API names must be org-qualified on a namespaced org (e.g. Ruby__BillingPeriod__c) — an unqualified name is ignored.
UpdateField Fields
Field | Type | Required | Description |
|---|---|---|---|
changeType | String | Yes | Must be "UpdateField" |
assetNumber | String | Yes | Subscription (asset) number to update |
startDate | Date | No | Effective date of the field update |
applyToChildren | Boolean | No | Cascade the update to the subscription's bundle children |
fieldsToUpdate | Array | Yes | List of fields to set on the subscription |
fieldsToUpdate[].fieldApiName | String | Yes | API name of the field to update (e.g. "Description") |
fieldsToUpdate[].newValue | String | Yes | New value to set, as a string (coerced to the field's type) |
Update One or More Fields
POST /cpq/change-order
{
"options": {
"activateOrder": true,
"proceedOption": "CreateOrder"
},
"assetChanges": [
{
"changeType": "UpdateField",
"assetNumber": "SUB-086067",
"startDate": "2026-08-01",
"fieldsToUpdate": [
{ "fieldApiName": "Description", "newValue": "Renewed per Q3 amendment" },
{ "fieldApiName": "PO_Number__c", "newValue": "PO-2026-0042" }
]
}
]
}