Partially update a sales order
PATCH /accounting/sales-orders/{id}
Group: Accounting › Sales Orders · Operation ID: updateSalesOrder
Writes only the fields the request body carries. Everything omitted is left exactly as it was. Product lines are edited through three parallel arrays: createProducts adds new lines, updateProducts rewrites existing ones (matched by salesOrderProductLineId, omitted fields left unchanged), and deleteProducts removes them by id; createSubscriptions adds new subscriptions with their own lines. A new Commit with Overage line requires allowance (its Allowance & Overage terms, defaulting from the product catalog); other line types reject it, and updateProducts cannot change an existing line's allowance. While an order is pending approval the whole header and its lines are editable. Once approved only a small set of header fields may change, and updateProducts entries may carry nothing but salesOrderProductLineId and tags; any other field or product operation fails with APPROVED_FIELD_LOCKED. An unknown id fails with NOT_FOUND; a product line id that does not belong to the order fails with INVALID_LINE. Runs in a transaction, so a failure leaves the order untouched.
Open in the interactive reference
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Api-Version |
header | "v1" |
yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version v1. |
Idempotency-Key |
header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with 400 IDEMPOTENCY_NOT_SUPPORTED rather than silently ignored. |
id |
path | string | yes | UUID of the sales order to update. The selector, never itself changed. |
Request body
Required, application/json: updateSalesOrderInput.
| Field | Type | Required | Description |
|---|---|---|---|
billToAddressId |
string | no | UUID of the Address invoices for this order go to. An address that does not exist fails with INVALID_ADDRESS. Stays editable after the order is approved. |
createProducts |
array of object | no | Product lines to add to the order. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
createProducts[].allowance |
LineAllowance | no | The Allowance & Overage terms of a Commit with Overage line. Required for that line type and rejected on any other; omitted fields default from the product catalog. |
createProducts[].allowance.allowance |
string (decimal) | yes | Committed quantity per allowance term, in the catalog Allowance's unit of measure. |
createProducts[].allowance.allowanceTerm |
string | no | The period the allowance covers: monthly, biMonthly, quarterly, semiAnnually, annually or orderTerm. Defaults to the product catalog's Allowance term. |
createProducts[].allowance.maximumRolloverLimit |
string (decimal) | no | Cap on the quantity that may roll over. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
createProducts[].allowance.overage |
LineOverage | no | Discount and invoicing overrides for the line's overage. Omit it to take everything from the catalog Overage and its price-book entry. |
createProducts[].allowance.overage.discountAmount |
string (decimal) | no | Discount per unit off the overage price, in the order currency. Takes precedence over discountPercentage when both are sent; omit both to price the overage exactly as its price-book entry has it. |
createProducts[].allowance.overage.discountPercentage |
string (decimal) | no | Discount per unit off the overage price on a 0–100 scale, so 10 means ten percent. Used only when discountAmount is not given. |
createProducts[].allowance.overage.invoiceTiming |
string | no | Whether the overage invoices advance (at the start of its period) or arrears (after it). Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
createProducts[].allowance.overage.lineInvoiceDay |
string | no | Which day of the period the overage invoices on: anniversary, first, last or specific. Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
createProducts[].allowance.rolloverDuration |
string (decimal) | no | How many allowance terms unused allowance rolls over for. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
createProducts[].discountAmount |
string (decimal) | no | Discount per unit off listPrice, in the order currency. Used when no unitPrice is given, and then drives the other two. |
createProducts[].discountPercentage |
string (decimal) | no | Discount per unit off listPrice on a 0–100 scale, so 10 means ten percent. Stored as a 0–1 fraction. The lowest precedence of the three — used only when neither unitPrice nor discountAmount is given. |
createProducts[].externalIdentifiers |
array of object | no | Identifiers this line carries in other systems, upserted by fieldName. |
createProducts[].externalIdentifiers[].externalUrl |
string | no | Deep link to the record in the owning system, shown beside the id. |
createProducts[].externalIdentifiers[].fieldName |
string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
createProducts[].externalIdentifiers[].value |
string | yes | The identifier as the owning system spells it. |
createProducts[].invoiceFrequency |
string | no | How often the line invoices — an InvoiceFrequency member: monthly, biMonthly, quarterly, semiAnnually, annually, once, custom, template, milestone, allAtOnce or termEnd. Defaults to the product line's own setting. |
createProducts[].invoiceTiming |
string | no | Whether the line invoices advance (at the start of the service period) or arrears (after it). Defaults from the entity's active invoice policy for this line type. |
createProducts[].lineInvoiceDay |
string | no | Which day of the period the line invoices on: anniversary, first, last or specific. Defaults from the entity's active invoice policy. |
createProducts[].listPrice |
string (decimal) | no | Undiscounted price per unit, in the order currency. Defaults to the list price the order's price book holds for this product line, and is the figure the other pricing fields are reconciled against. |
createProducts[].priceModel |
string | no | How the line is priced: flat, perUnit, tiered, volume, stairstep or rateCard. Defaults to the product line's own model. tiered, volume and stairstep take their prices from tiers and rateCard from rateCardEntries, so for those the line-level discount fields are not reconciled. thresholdBased is rejected: it needs a base line plus a metered usage child, which this endpoint does not build. |
createProducts[].pricingTerm |
string | no | The period the price is quoted for: monthly, biMonthly, quarterly, semiAnnually, annually or once. Defaults to the product line's own term. |
createProducts[].productLineId |
string | yes | UUID of the ProductLine being added. It settles the product, the line type and the billing rules every omitted field defaults from. |
createProducts[].quantity |
string (decimal) | no | Number of units ordered. Required unless the line takes its quantity from somewhere else: usage-metered line types (PayAsYouGo, Usage, Billable*) meter it from usage records and tier-priced models (tiered, volume, stairstep, thresholdBased) derive it from the tier structure, so for those it defaults to zero. Anywhere else, omitting it fails the request. |
createProducts[].rateCardEntries |
array of object | no | Per-unit rates for a rateCard-priced line. Supplying any entry moves pricing onto the rate card, so the line-level discount fields stop being reconciled. |
createProducts[].rateCardEntries[].billableComponentId |
string | no | UUID of the BillableComponent this row meters. Optional — a row may price a unit of measure without naming a component — but a UUID that does not resolve rejects the whole request. |
createProducts[].rateCardEntries[].conversion |
string (decimal) | no | Factor converting the billable unit into the reporting unit — 1000 when usage is metered per call but reported per thousand. |
createProducts[].rateCardEntries[].description |
string | no | Free-text label for the row, carried through to the stored rate-card entry. |
createProducts[].rateCardEntries[].listPrice |
string (decimal) | no | Undiscounted rate per unitOfMeasure, in the order currency. Honoured only while the product line's enforceListPrice is false; otherwise the product line's own list price wins. |
createProducts[].rateCardEntries[].unitOfMeasure |
string | yes | The unit this row is priced in. |
createProducts[].rateCardEntries[].unitPrice |
string (decimal) | yes | Negotiated rate per unitOfMeasure, in the order currency. |
createProducts[].serviceEndDate |
string (date) | no | Last day of the line's service period. The order's own service end is the latest across all its lines, and is left open on an evergreen order. |
createProducts[].serviceStartDate |
string (date) | no | First day of the line's service period. The order's own service start is the earliest across all its lines. |
createProducts[].subscriptionId |
string | no | UUID of a subscription already on this order to hang the line under. Omit it to add the line to the order itself as a one-time line. A subscription that is not on the order fails with INVALID_SUBSCRIPTION. |
createProducts[].tags |
array of object | no | Value-accounting tags to allocate this line by. Requires value-allocation tagging to be enabled for sales orders. |
createProducts[].tags[].dimension |
string | yes | Name of the value-accounting dimension, as configured for the tenant. Resolved by name, not by id. |
createProducts[].tags[].value |
string | yes | Name of the value within that dimension. The pair must resolve to an existing tag and the dimension must be enabled for sales orders; an unknown pair rejects the whole request. |
createProducts[].tiers |
array of object | no | The pricing tiers of a tiered, volume or stairstep line. The ranges must be contiguous, and the last tier is left open-ended by omitting its toQuantity. |
createProducts[].tiers[].fromQuantity |
string (decimal) | yes | Lower bound of the tier's quantity range, inclusive. |
createProducts[].tiers[].listPrice |
string (decimal) | no | Undiscounted price for this tier, in the order currency. Defaults to unitPrice, i.e. no discount on the tier. |
createProducts[].tiers[].priceModel |
string | no | How unitPrice applies within the tier: perUnit (the default) charges it for every unit in the range, flat charges it once for the whole tier. |
createProducts[].tiers[].toQuantity |
string (decimal) | no | Upper bound of the tier's quantity range. Omit it on the last tier to leave the range open-ended. |
createProducts[].tiers[].unitPrice |
string (decimal) | yes | Negotiated price for this tier, in the order currency. Charged per unit in the range when priceModel is perUnit, or once for the whole tier when it is flat. |
createProducts[].totalPrice |
string (decimal) | no | Total for the line, in the order currency. Persisted exactly as supplied rather than recomputed — omit it and the line's own pricing drives the total. |
createProducts[].unitOfMeasure |
string | no | The unit the line is priced and metered in. Defaults to the product line's own unit. |
createProducts[].unitPrice |
string (decimal) | no | Negotiated price per unit, in the order currency. The highest precedence of the three pricing inputs — supply it and discountAmount and discountPercentage are recomputed from it (discountAmount = listPrice − unitPrice). |
createSubscriptions |
array of object | no | New subscriptions to add to the order, each bringing its own product lines. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
createSubscriptions[].autoRenew |
boolean | no | Whether the subscription renews itself at the end of its term. |
createSubscriptions[].products |
array of ProductLine | yes | The recurring product lines of the new subscription. Every one must be a subscription-based product line, or the request fails with INVALID_SUBSCRIPTION_PRODUCT. |
createSubscriptions[].products[].allowance |
LineAllowance | no | The Allowance & Overage terms of a Commit with Overage line. Required for that line type and rejected on any other; omitted fields default from the product catalog. |
createSubscriptions[].products[].allowance.allowance |
string (decimal) | yes | Committed quantity per allowance term, in the catalog Allowance's unit of measure. |
createSubscriptions[].products[].allowance.allowanceTerm |
string | no | The period the allowance covers: monthly, biMonthly, quarterly, semiAnnually, annually or orderTerm. Defaults to the product catalog's Allowance term. |
createSubscriptions[].products[].allowance.maximumRolloverLimit |
string (decimal) | no | Cap on the quantity that may roll over. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
createSubscriptions[].products[].allowance.overage |
LineOverage | no | Discount and invoicing overrides for the line's overage. Omit it to take everything from the catalog Overage and its price-book entry. |
createSubscriptions[].products[].allowance.overage.discountAmount |
string (decimal) | no | Discount per unit off the overage price, in the order currency. Takes precedence over discountPercentage when both are sent; omit both to price the overage exactly as its price-book entry has it. |
createSubscriptions[].products[].allowance.overage.discountPercentage |
string (decimal) | no | Discount per unit off the overage price on a 0–100 scale, so 10 means ten percent. Used only when discountAmount is not given. |
createSubscriptions[].products[].allowance.overage.invoiceTiming |
string | no | Whether the overage invoices advance (at the start of its period) or arrears (after it). Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
createSubscriptions[].products[].allowance.overage.lineInvoiceDay |
string | no | Which day of the period the overage invoices on: anniversary, first, last or specific. Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
createSubscriptions[].products[].allowance.rolloverDuration |
string (decimal) | no | How many allowance terms unused allowance rolls over for. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
createSubscriptions[].products[].discountAmount |
string (decimal) | no | Discount per unit off listPrice, in the order currency. Used when no unitPrice is given, and then drives the other two. |
createSubscriptions[].products[].discountPercentage |
string (decimal) | no | Discount per unit off listPrice on a 0–100 scale, so 10 means ten percent. Stored as a 0–1 fraction. The lowest precedence of the three — used only when neither unitPrice nor discountAmount is given. |
createSubscriptions[].products[].externalIdentifiers |
array of ExternalIdentifier | no | Identifiers this line carries in other systems, upserted by fieldName. |
createSubscriptions[].products[].externalIdentifiers[].externalUrl |
string | no | Deep link to the record in the owning system, shown beside the id. |
createSubscriptions[].products[].externalIdentifiers[].fieldName |
string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
createSubscriptions[].products[].externalIdentifiers[].value |
string | yes | The identifier as the owning system spells it. |
createSubscriptions[].products[].invoiceFrequency |
string | no | How often the line invoices — an InvoiceFrequency member: monthly, biMonthly, quarterly, semiAnnually, annually, once, custom, template, milestone, allAtOnce or termEnd. Defaults to the product line's own setting. |
createSubscriptions[].products[].invoiceFrequencyTemplateId |
string | no | UUID of the invoice frequency template. |
createSubscriptions[].products[].invoiceTiming |
string | no | Whether the line invoices advance (at the start of the service period) or arrears (after it). Defaults from the entity's active invoice policy for this line type. |
createSubscriptions[].products[].lineInvoiceDay |
string | no | Which day of the period the line invoices on: anniversary, first, last or specific. Defaults from the entity's active invoice policy. |
createSubscriptions[].products[].listPrice |
string (decimal) | no | Undiscounted price per unit, in the order currency. Defaults to the list price the order's price book holds for this product line, and is the figure the other pricing fields are reconciled against. |
createSubscriptions[].products[].priceModel |
string | no | How the line is priced: flat, perUnit, tiered, volume, stairstep or rateCard. Defaults to the product line's own model. tiered, volume and stairstep take their prices from tiers and rateCard from rateCardEntries, so for those the line-level discount fields are not reconciled. thresholdBased is rejected: it needs a base line plus a metered usage child, which this endpoint does not build. |
createSubscriptions[].products[].pricingTerm |
string | no | The period the price is quoted for: monthly, biMonthly, quarterly, semiAnnually, annually or once. Defaults to the product line's own term. |
createSubscriptions[].products[].productLineId |
string | yes | UUID of the ProductLine being ordered. It settles the product, the line type and the billing rules every omitted field defaults from. It must exist, and it must not be threshold-based or prepaid: those need a base line plus a metered usage child under one product, which this endpoint does not build. |
createSubscriptions[].products[].quantity |
string (decimal) | no | Number of units ordered. Required unless the line takes its quantity from somewhere else: usage-metered line types (PayAsYouGo, Usage, Billable*) meter it from usage records and tier-priced models (tiered, volume, stairstep, thresholdBased) derive it from the tier structure, so for those it defaults to zero. Anywhere else, omitting it fails the request. |
createSubscriptions[].products[].rateCardEntries |
array of RateCardEntry | no | Per-unit rates for a rateCard-priced line. Supplying any entry moves pricing onto the rate card, so the line-level discount fields stop being reconciled. |
createSubscriptions[].products[].rateCardEntries[].billableComponentId |
string | no | UUID of the BillableComponent this row meters. Optional — a row may price a unit of measure without naming a component — but a UUID that does not resolve rejects the whole request. |
createSubscriptions[].products[].rateCardEntries[].conversion |
string (decimal) | no | Factor converting the billable unit into the reporting unit — 1000 when usage is metered per call but reported per thousand. |
createSubscriptions[].products[].rateCardEntries[].description |
string | no | Free-text label for the row, carried through to the stored rate-card entry. |
createSubscriptions[].products[].rateCardEntries[].listPrice |
string (decimal) | no | Undiscounted rate per unitOfMeasure, in the order currency. Honoured only while the product line's enforceListPrice is false; otherwise the product line's own list price wins. |
createSubscriptions[].products[].rateCardEntries[].unitOfMeasure |
string | yes | The unit this row is priced in. |
createSubscriptions[].products[].rateCardEntries[].unitPrice |
string (decimal) | yes | Negotiated rate per unitOfMeasure, in the order currency. |
createSubscriptions[].products[].serviceEndDate |
string (date) | no | Last day of the line's service period. The order's own service end is the latest across all its lines, and is left open on an evergreen order. |
createSubscriptions[].products[].serviceStartDate |
string (date) | no | First day of the line's service period. The order's own service start is the earliest across all its lines. |
createSubscriptions[].products[].tags |
array of LineTag | no | Value-accounting tags to allocate this line by. Requires value-allocation tagging to be enabled for sales orders. |
createSubscriptions[].products[].tags[].dimension |
string | yes | Name of the value-accounting dimension, as configured for the tenant. Resolved by name, not by id. |
createSubscriptions[].products[].tags[].value |
string | yes | Name of the value within that dimension. The pair must resolve to an existing tag and the dimension must be enabled for sales orders; an unknown pair rejects the whole request. |
createSubscriptions[].products[].tiers |
array of LineTier | no | The pricing tiers of a tiered, volume or stairstep line. The ranges must be contiguous, and the last tier is left open-ended by omitting its toQuantity. |
createSubscriptions[].products[].tiers[].fromQuantity |
string (decimal) | yes | Lower bound of the tier's quantity range, inclusive. |
createSubscriptions[].products[].tiers[].listPrice |
string (decimal) | no | Undiscounted price for this tier, in the order currency. Defaults to unitPrice, i.e. no discount on the tier. |
createSubscriptions[].products[].tiers[].priceModel |
string | no | How unitPrice applies within the tier: perUnit (the default) charges it for every unit in the range, flat charges it once for the whole tier. |
createSubscriptions[].products[].tiers[].toQuantity |
string (decimal) | no | Upper bound of the tier's quantity range. Omit it on the last tier to leave the range open-ended. |
createSubscriptions[].products[].tiers[].unitPrice |
string (decimal) | yes | Negotiated price for this tier, in the order currency. Charged per unit in the range when priceModel is perUnit, or once for the whole tier when it is flat. |
createSubscriptions[].products[].totalPrice |
string (decimal) | no | Total for the line, in the order currency. Persisted exactly as supplied rather than recomputed — omit it and the line's own pricing drives the total. |
createSubscriptions[].products[].unitOfMeasure |
string | no | The unit the line is priced and metered in. Defaults to the product line's own unit. |
createSubscriptions[].products[].unitPrice |
string (decimal) | no | Negotiated price per unit, in the order currency. The highest precedence of the three pricing inputs — supply it and discountAmount and discountPercentage are recomputed from it (discountAmount = listPrice − unitPrice). |
createSubscriptions[].serviceEndDate |
string (date) | no | Last day the subscription is in service. Omit it on an evergreen subscription. |
createSubscriptions[].serviceStartDate |
string (date) | yes | First day the subscription is in service. |
createSubscriptions[].term |
string (decimal) | no | Intended length of a termed subscription in months. What actually bounds the subscription is serviceStartDate and serviceEndDate, so supply the end date to fix when it ends. |
createSubscriptions[].termType |
string | yes | termed for a subscription of fixed length, evergreen for an open-ended one that keeps renewing. |
deleteProducts |
array of string | no | UUIDs of product lines to remove from the order. Each must belong to it, or the request fails with INVALID_LINE. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
effectiveDate |
string (date) | no | Day the order becomes commercially effective. Stays editable after the order is approved. |
excludeFromTaxEngine |
boolean | no | Whether the order is kept out of the tax engine, so no tax is calculated for it. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
oneTimeDiscountAllocation |
string | no | How the one-time discount is spread across the order's invoices: firstInvoice puts all of it on the first, alignToInvoiceFrequency spreads it over the invoicing schedule, custom leaves the allocation to be set by hand. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
oneTimeDiscountAmount |
string (decimal) | no | A one-off discount on the order as a whole, in the order currency. Independent of the per-line discount fields. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
opportunityName |
string | no | Name of the opportunity the order came out of. Stays editable after the order is approved. |
paymentTermCode |
string | no | The order's payment term, named by its code. Stays editable after the order is approved. |
purchaseOrder |
string | no | The customer's own purchase-order number for this order. Stays editable after the order is approved. |
serviceEndDate |
string (date) | no | Last day of the order's service period. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
serviceStartDate |
string (date) | no | First day of the order's service period. Locked once the order is approved: sending it then fails with APPROVED_FIELD_LOCKED. |
signDate |
string (date) | no | Day the order was signed. Stays editable after the order is approved. |
soldToAddressId |
string | no | UUID of the Address of the contracting party. An address that does not exist fails with INVALID_ADDRESS. Stays editable after the order is approved. |
updateProducts |
array of object | no | Changes to product lines already on the order, each matched by its salesOrderProductLineId. Fields left out of an entry keep their current values. Once the order is approved an entry may carry only salesOrderProductLineId and tags; any other field then fails with APPROVED_FIELD_LOCKED. |
updateProducts[].discountAmount |
string (decimal) | no | Discount per unit off listPrice, in the order currency. This endpoint has no unitPrice field, so the discount is what drives it: unitPrice = listPrice − discountAmount. Takes precedence over discountPercentage. |
updateProducts[].discountPercentage |
string (decimal) | no | Discount per unit off listPrice on a 0–100 scale, so 10 means ten percent. Stored as a 0–1 fraction. Used only when discountAmount is absent. |
updateProducts[].externalIdentifiers |
array of object | no | Identifiers to set on the line, upserted by fieldName. Names not listed are left as they are — there is no way to remove one. |
updateProducts[].externalIdentifiers[].externalUrl |
string | no | Deep link to the record in the owning system, shown beside the id. |
updateProducts[].externalIdentifiers[].fieldName |
string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
updateProducts[].externalIdentifiers[].value |
string | yes | The identifier as the owning system spells it. |
updateProducts[].invoiceFrequency |
string | no | New invoicing frequency for the line — an InvoiceFrequency member: monthly, biMonthly, quarterly, semiAnnually, annually, once, custom, template, milestone, allAtOnce or termEnd. Omit it to leave the frequency as it stands. |
updateProducts[].invoiceTiming |
string | no | Whether the line invoices advance (at the start of the service period) or arrears (after it). Omit it to leave the timing as it stands. |
updateProducts[].lineInvoiceDay |
string | no | Which day of the period the line invoices on: anniversary, first, last or specific. Omit it to leave the day as it stands. |
updateProducts[].listPrice |
string (decimal) | no | New undiscounted price per unit, in the order currency. Changing it on its own still re-reconciles the line: the existing discount amount is held fixed and unitPrice and discountPercentage are re-derived against the new list price. |
updateProducts[].quantity |
string (decimal) | no | New number of units for the line. Omit it to leave the quantity as it stands. |
updateProducts[].rateCardEntries |
array of object | no | Replaces the line's rate-card rows: a row carrying an id is updated, one without is added, and any existing row whose id is not in the list is deleted. Omit the field entirely to leave the line's rows untouched. |
updateProducts[].rateCardEntries[].billableComponentId |
string | no | UUID of the BillableComponent this row meters. Optional — a row may price a unit of measure without naming a component — but a UUID that does not resolve rejects the whole request. |
updateProducts[].rateCardEntries[].conversion |
string (decimal) | no | Factor converting the billable unit into the reporting unit — 1000 when usage is metered per call but reported per thousand. |
updateProducts[].rateCardEntries[].description |
string | no | Free-text label for the row, carried through to the stored rate-card entry. |
updateProducts[].rateCardEntries[].id |
string | no | UUID of a rate-card row already on this line, to change it in place. Leave it out to add a new row. An id that is not on the line fails the request and nothing is applied. |
updateProducts[].rateCardEntries[].listPrice |
string (decimal) | no | Undiscounted rate per unitOfMeasure, in the order currency. Honoured only while the product line's enforceListPrice is false; otherwise the product line's own list price wins. |
updateProducts[].rateCardEntries[].unitOfMeasure |
string | yes | The unit this row is priced in. |
updateProducts[].rateCardEntries[].unitPrice |
string (decimal) | yes | Negotiated rate per unitOfMeasure, in the order currency. |
updateProducts[].salesOrderProductLineId |
string | yes | UUID of the order's product line to change. It must belong to this order, or the request fails with INVALID_LINE. |
updateProducts[].serviceEndDate |
string (date) | no | New last day of the line's service period. |
updateProducts[].serviceStartDate |
string (date) | no | New first day of the line's service period. |
updateProducts[].tags |
array of object | no | Replaces the line's value-accounting tags wholesale. Send [] to clear them; omit the field to leave them untouched. Lines that share one product (ramp deals) share their tags, so all of them must be sent the same set. Stays editable after the order is approved. |
updateProducts[].tags[].dimension |
string | yes | Name of the value-accounting dimension, as configured for the tenant. Resolved by name, not by id. |
updateProducts[].tags[].value |
string | yes | Name of the value within that dimension. The pair must resolve to an existing tag and the dimension must be enabled for sales orders; an unknown pair rejects the whole request. |
updateProducts[].tiers |
array of object | no | Replaces the line's pricing tiers wholesale. Omit the field to leave them untouched. |
updateProducts[].tiers[].fromQuantity |
string (decimal) | yes | Lower bound of the tier's quantity range, inclusive. |
updateProducts[].tiers[].listPrice |
string (decimal) | no | Undiscounted price for this tier, in the order currency. Defaults to unitPrice, i.e. no discount on the tier. |
updateProducts[].tiers[].priceModel |
string | no | How unitPrice applies within the tier: perUnit (the default) charges it for every unit in the range, flat charges it once for the whole tier. |
updateProducts[].tiers[].toQuantity |
string (decimal) | no | Upper bound of the tier's quantity range. Omit it on the last tier to leave the range open-ended. |
updateProducts[].tiers[].unitPrice |
string (decimal) | yes | Negotiated price for this tier, in the order currency. Charged per unit in the range when priceModel is perUnit, or once for the whole tier when it is flat. |
updateProducts[].totalPrice |
string (decimal) | no | New total for the line, in the order currency. Persisted exactly as supplied rather than recomputed. |
Example:
{
"billToAddressId": "5b8c31f6-04ad-472e-9f81-63a0d7e295cb",
"createProducts": [
{
"allowance": {
"allowance": "1000",
"allowanceTerm": "monthly",
"maximumRolloverLimit": "500",
"overage": {
"discountAmount": "1.50",
"discountPercentage": "10",
"invoiceTiming": "arrears",
"lineInvoiceDay": "anniversary"
},
"rolloverDuration": "2"
},
"discountAmount": "120.00",
"discountPercentage": "10",
"externalIdentifiers": [
{
"externalUrl": "https://netsuite.example.com/app/accounting/transactions/salesord.nl?id=10042",
"fieldName": "netsuiteInternalId",
"value": "SO-10042"
}
],
"invoiceFrequency": "monthly",
"invoiceTiming": "advance",
"lineInvoiceDay": "anniversary",
"listPrice": "1200.00",
"priceModel": "perUnit",
"pricingTerm": "annually",
"productLineId": "6a1f2c94-3d58-4b17-9e02-7c8ab4d5f610",
"quantity": "10",
"rateCardEntries": [
{
"billableComponentId": "9f1c2d34-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"conversion": "1000",
"description": "API calls beyond the included volume",
"listPrice": "0.005",
"unitOfMeasure": "apiCall",
"unitPrice": "0.004"
}
],
"serviceEndDate": "2026-12-31",
"serviceStartDate": "2026-01-01",
"subscriptionId": "c8402e17-95bf-4a63-8d20-71ec3f6a0b48",
"tags": [
{
"dimension": "Department",
"value": "Engineering"
}
],
"tiers": [
{
"fromQuantity": "0",
"listPrice": "15.00",
"priceModel": "perUnit",
"toQuantity": "100",
"unitPrice": "12.50"
}
],
"totalPrice": "12960.00",
"unitOfMeasure": "seat",
"unitPrice": "1080.00"
}
],
"createSubscriptions": [
{
"autoRenew": true,
"products": [
{
"allowance": {
"allowance": "1000",
"allowanceTerm": "monthly",
"maximumRolloverLimit": "500",
"overage": {
"discountAmount": null,
"discountPercentage": null,
"invoiceTiming": null,
"lineInvoiceDay": null
},
"rolloverDuration": "2"
},
"discountAmount": "120.00",
"discountPercentage": "10",
"externalIdentifiers": [
{
"externalUrl": null,
"fieldName": null,
"value": null
}
],
"invoiceFrequency": "monthly",
"invoiceFrequencyTemplateId": "9f1c2d34-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"invoiceTiming": "advance",
"lineInvoiceDay": "anniversary",
"listPrice": "1200.00",
"priceModel": "perUnit",
"pricingTerm": "annually",
"productLineId": "6a1f2c94-3d58-4b17-9e02-7c8ab4d5f610",
"quantity": "10",
"rateCardEntries": [
{
"billableComponentId": null,
"conversion": null,
"description": null,
"listPrice": null,
"unitOfMeasure": null,
"unitPrice": null
}
],
"serviceEndDate": "2026-12-31",
"serviceStartDate": "2026-01-01",
"tags": [
{
"dimension": null,
"value": null
}
],
"tiers": [
{
"fromQuantity": null,
"listPrice": null,
"priceModel": null,
"toQuantity": null,
"unitPrice": null
}
],
"totalPrice": "12960.00",
"unitOfMeasure": "seat",
"unitPrice": "1080.00"
}
],
"serviceEndDate": "2026-12-31",
"serviceStartDate": "2026-01-01",
"term": "12",
"termType": "termed"
}
],
"deleteProducts": [
"<deleteProducts>"
],
"effectiveDate": "2026-02-01",
"excludeFromTaxEngine": true,
"oneTimeDiscountAllocation": "firstInvoice",
"oneTimeDiscountAmount": "500.00",
"opportunityName": "Contoso — 2026 renewal",
"paymentTermCode": "NET30",
"purchaseOrder": "PO-2026-0042",
"serviceEndDate": "2026-12-31",
"serviceStartDate": "2026-01-01",
"signDate": "2026-01-10",
"soldToAddressId": "5b8c31f6-04ad-472e-9f81-63a0d7e295cb",
"updateProducts": [
{
"discountAmount": "120.00",
"discountPercentage": "10",
"externalIdentifiers": [
{
"externalUrl": "https://netsuite.example.com/app/accounting/transactions/salesord.nl?id=10042",
"fieldName": "netsuiteInternalId",
"value": "SO-10042"
}
],
"invoiceFrequency": "monthly",
"invoiceTiming": "advance",
"lineInvoiceDay": "anniversary",
"listPrice": "1200.00",
"quantity": "10",
"rateCardEntries": [
{
"billableComponentId": "9f1c2d34-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"conversion": "1000",
"description": "API calls beyond the included volume",
"id": "3f7b9a52-0c1d-48e6-b934-a5d720e8146f",
"listPrice": "0.005",
"unitOfMeasure": "apiCall",
"unitPrice": "0.004"
}
],
"salesOrderProductLineId": "a05e7d13-8f62-4b90-bc47-1e63d0592a8f",
"serviceEndDate": "2026-12-31",
"serviceStartDate": "2026-01-01",
"tags": [
{
"dimension": "Department",
"value": "Engineering"
}
],
"tiers": [
{
"fromQuantity": "0",
"listPrice": "15.00",
"priceModel": "perUnit",
"toQuantity": "100",
"unitPrice": "12.50"
}
],
"totalPrice": "12960.00"
}
]
}
Response
200 Success
Under data (updateSalesOrderOutput):
| Field | Type | Required | Description |
|---|---|---|---|
approvalStatus |
string | yes | The order's approval status after the update: draft, pendingApproval, revise or approved. |
id |
string | yes | UUID of the sales order that was updated. |
Response headers
Idempotency-Replayed— Present andtrueonly when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent asfalse.
Errors
| Code | Status |
|---|---|
APPROVED_FIELD_LOCKED |
400 |
INVALID_ADDRESS |
400 |
INVALID_LINE |
400 |
INVALID_SUBSCRIPTION |
400 |
INVALID_SUBSCRIPTION_PRODUCT |
400 |
VALIDATION_FAILED |
400 |
NOT_FOUND |
404 |
IDEMPOTENCY_KEY_CONFLICT |
409 |
IDEMPOTENCY_PAYLOAD_MISMATCH |
422 |
Statuses
400— Invalid request or business failure. Codes: APPROVED_FIELD_LOCKED, INVALID_ADDRESS, INVALID_LINE, INVALID_SUBSCRIPTION, INVALID_SUBSCRIPTION_PRODUCT, VALIDATION_FAILED401— Missing or invalid access token403— Not authorized404— Not found. Codes: NOT_FOUND409— The Idempotency-Key is still being processed by another request.422— The Idempotency-Key was already used with a different request payload.429— Too many requests500— Internal server error