Sales Order PUT

URI

https://{DATACENTER}.brightpearlconnect.com/public-api/{ACCOUNT}
/order-service/sales-order/{id}/

Refer to our documentation on URI syntax for more information on how to construct URIs.

Description

This message enables you to update existing sales orders in Brightpearl.

To update a sales order, provide the sales order ID in the URI and the complete desired writable representation in the request body. PUT is replacement-style: writable fields not present have their values removed or take the indicated default value. Read-only fields are ignored if supplied.

Optimistic concurrency

The If-Match request header should be used to prevent overwriting a sales order that has changed since it was read. Set the header to the numeric version returned by the most recent sales-order GET response, without quotes; for example, If-Match: 5. If the supplied version does not match the current order version, the request fails with HTTP 412 Precondition Failed. Retrieve the latest sales order and re-apply your changes before retrying the update.

If the If-Match header is omitted, blank, or is not a valid non-negative integer, no version comparison is performed and the update proceeds without optimistic-concurrency protection. Be aware that this can lead to lost updates if another user or subsystem updates the order.

Restrictions for invoiced orders and processed rows

Invoiced orders: the request must contain the same number of rows and the ID of every existing row. Rows cannot be added, removed or replaced. The following row values must match the stored order: productId, name, sku, quantity, taxCode, tax, net, nominalCode, productPrice, sequence and clonedFromId. rowCost must also remain unchanged for stock-tracked products and bundle-parent rows. Normal order-row fields are not rewritten for an invoiced order, so fields not listed above must not be assumed to remain writable.

The following order values must also match the stored invoiced order: customer, billing, delivery address, channelId, staffOwnerId, projectId, leadSourceId, teamId, priceListId, priceModeCode, currency, placedOn, taxDate, paymentDueDate, seasonId, settlementDiscount and orderWeighting. These comparisons occur after omission defaults and address population have been applied, so omitting one of these values can still cause the request to fail if its resulting value differs from the stored order.

Reserved and fulfilled quantities: on an uninvoiced order, the quantity of an existing row cannot be reduced below the combined quantity already reserved, fulfilled through a goods-out note, or fulfilled through drop shipping. Omitting a row attempts to delete it; deletion is rejected if the row has goods-out or drop-ship fulfilment. A reservation on its own does not prevent row deletion.

Field summary: sales order

Field Description Data Type Required Values
customerId The ID of the customer you wish this order to be associated with. int Yes
customer.address The address of the customer for this order. Omit this object to use the customer's current default address from the contact record. Omitting it is the recommended way to refresh the order's customer address from contact data. address object (see later) No
billing.contactId The ID of the customer you wish to use as the billing contact for this order. int No
billing.address The billing address for this order. Omit this object to use the selected billing contact's or company's current billing address, or the customer's default address when no billing address is available. Omitting it is the recommended way to refresh the order's billing address from contact data. address object (see later) No
ref The customer reference you wish to assign to this order. string of up to 64 characters No
placedOn The date this order was placed on. If not specified, defaults to the current date and current time. string. ISO8601 format date-time. No
taxDate

The tax date of the order. If omitted, it uses the date given by placedOn; if placedOn is also omitted, it uses today's date.

To specify an exact calendar date, send a date such as 2027-06-04. You can also send a full ISO 8601 date-time with a time zone. Its date is determined in the account's local time zone: 2027-06-04T17:00:00.00-07:00 becomes 2027-06-05 for an account in Europe/London.

GET returns this field and invoice.taxDate as ISO 8601 date-times.

string. ISO 8601 date or date-time with time zone. No
statusId The ID of the status you wish to assign to this order. int Yes
warehouseId The ID of the warehouse which will be used by default for fulfilment. If omitted, the default warehouse for the effective channel is used. If that channel has no default warehouse, the account's default warehouse is used. int No
channelId The ID of the channel you wish to assign this order to. If omitted, the account's default sales channel is used. int No
staffOwnerId The ID of the staff member who owns this order. If you do not specify this value and the auto assign property is enabled in your sales settings, it will be set to the staff owner currently assigned to the customer contact. int No
projectId The ID of the project you wish to assign this order to. If omitted, no project is assigned. int No
leadSourceId The ID of the lead source you wish to assign this order to. If omitted, the customer's lead source is used. If the customer has no lead source, no lead source is assigned. int No
teamId The ID of the team you wish to assign this order to. If omitted, no team is assigned. int No
priceListId

The ID of the price list you wish to use for this order. If no price list ID is provided the customer's assigned price list is used.

int No
priceModeCode The price mode to use for the order. If not specified, it defaults to the mode of the effective price list: 'INC' when the price list is configured as gross, otherwise 'EXC'. string No 'INC' or 'EXC'
currency.code The currency you wish to set this order to be. If omitted, the customer's currency is used. If the customer's currency cannot be found, the account's base currency is used. string. 3 chars. No
currency.fixedExchangeRate If true, the provided currency.exchangeRate will be used when the order is invoiced. If currency.exchangeRate is omitted, the configured exchange rate for the selected currency is used. If false, any provided currency.exchangeRate is ignored and the configured exchange rate for the selected currency is used. If both fields are omitted, currency.fixedExchangeRate defaults to true. Supplying currency.exchangeRate while omitting currency.fixedExchangeRate is invalid; set currency.fixedExchangeRate explicitly to true to use a supplied exchange rate. boolean No
currency.exchangeRate The exchange rate to use when calculating base currency amounts from the order currency. This field is optional and is used only when currency.fixedExchangeRate is true. If it is omitted, the configured exchange rate for the selected currency is used. When currency.fixedExchangeRate is false, any value supplied in this field is ignored and the configured exchange rate is used. Supplying this field while omitting currency.fixedExchangeRate causes a validation error. string. A number with up to six decimal places. No
delivery.date The date you wish to have the order delivered on. string. ISO8601 format date-time. No
delivery.address The delivery address for the order. Omit this object to use the customer's current delivery address, or the customer's default address when no delivery address is available. Omitting it is the recommended way to refresh the order's delivery address from contact data. address object (see later) No
delivery.shippingMethodId The ID of the shipping method to be used to fulfil this order. int No
rows An array of sales order rows associated with this order. Omitting this field is treated the same as supplying an empty array and attempts to delete every existing row. array of up to 250 row objects (see below) No
paymentDueDate

The payment due date of the order. If omitted, it is calculated from today's date and the customer's credit terms.

To specify an exact calendar date, send a date such as 2027-06-04. You can also send a full ISO 8601 date-time with a time zone. Its date is determined in the account's local time zone: 2027-06-04T17:00:00.00-07:00 becomes 2027-06-05 for an account in Europe/London.

GET returns this field and invoice.dueDate as ISO 8601 date-times. Only the top-level paymentDueDate field is used when writing an order.

string. ISO 8601 date or date-time with time zone. No
seasonId The ID of the season to assign to this order. If omitted, no season is assigned. int No
settlementDiscount.percentage The settlement discount percentage available for early payment. If the settlementDiscount object is omitted, any existing settlement discount is cleared. string. A number up to 2dp. When settlementDiscount is supplied.
settlementDiscount.days The number of days in which payment must be made to qualify for the settlement discount. If omitted from a supplied settlementDiscount object, it defaults to 0. int No
orderWeighting The weighting of the order as a percentage. If not provided, defaults to 100. int. An integer between 0 and 100 inclusive. No

Field summary: sales order rows

When updating sales order rows, you can add new rows, update existing rows (by providing the row ID), or delete rows (by omitting them from the rows array). You must include the ID of every existing row that you want to retain. If the rows field is omitted, it is normalised to an empty array and the request attempts to delete all existing rows. The request will fail if any affected row cannot be deleted, for example because the order is invoiced or the row has goods-out or drop-ship fulfilment.

Field Description Data Type Required Values
id The ID of the order row to update. int When updating existing rows.
productId The ID of the product you wish to add to the order. int If name not supplied.
name The name of the product or description of the row. If not supplied, defaults to the name of the product associated with the productId (if provided). string If productId not supplied
sku The SKU to store on the row. If omitted for a product row, the product's SKU is used. string of up to 32 characters No
externalRef An external reference for a new row. For an existing row, the stored external reference is retained and any supplied value is ignored. string No
quantity The quantity of the item on this row. string Yes Must be an integer for stock-tracked product or bundle rows. Can be up to 4dp otherwise.
taxCode The tax code for this row. string Yes
tax The tax value of this row. string Yes Max precision of 4dp.
taxCalculator The tax calculator to store for this row. A supplied value is used for new and existing rows. If omitted, the value is determined from the supplied tax amount and tax code. string No brightpearl, manual, or an external calculator name.
net The net value of this row. string Yes Max precision of 4dp.
nominalCode The nominal code for this row. If you do not provide a nominal code, then the following rules will be used to determine which nominal code will be applied to the row.
  • The nominal code associated to the customer will be used
  • If the customer does not have a nominal code, the nominal code associated with the product will be used
  • If the product does not have a nominal code or it is not a Brightpearl product the default sales nominal code is used
string No
productPrice The net list price of the product at the time of sale. If omitted or blank, it is obtained from the order's effective price list for a product row, or defaults to 0.00 when no product price can be found. string No Max precision of 4dp.
clonedFromId The ID of the source order row from which this row was cloned. This is an internal provenance field. If omitted, no source row is recorded. int No
sequence Used to define a sorting order (ascending) for rows. int No

Field summary: addresses

Where the type 'address object' appears in the table above, the following fields may be used. Fields marked as required are only required if the address object is supplied (which may or may not be required).

Address objects are not merged with the address already stored on the sales order. An address object is treated as supplied only when addressLine1 is present. If addressLine1 is omitted, the supplied address object is discarded, including any other address fields it contains, and the complete address is populated from contact data. For example, supplying only countryIsoCode for delivery.address will not update only the country; the delivery address will instead be populated from the customer's stored delivery address, or from the customer's default address if no stored delivery address is available.

The same rule applies to customer.address and billing.address. An incomplete customer address is populated from the customer's default address. An incomplete billing address is populated from the selected billing contact or company where available, otherwise from the customer's default address.

Field Description Data Type Required
addressFullName The full name of the person associated with this address. string of up to 64 characters
companyName The name of the company associated with the address. string of up to 64 characters
addressLine1 The first line of the address (e.g. street). This field must be present for the supplied address object to be used instead of an address populated from contact data. string of up to 256 characters When using the supplied address object
addressLine2 The second line of the address (e.g. suburb) string of up to 256 characters
addressLine3 The third line of the address (e.g. city) string of up to 64 characters
addressLine4 The fourth line of the address (e.g. state / province) string of up to 64 characters
postalCode The postal/zip code of the address string string of up to 32 characters
countryIsoCode The code for the country in the address string. ISO 3166-1 alpha-2 or 3166-1 alpha-3 format codes. Yes
telephone Telephone number associated with the address string of up to 32 characters
mobileTelephone Mobile/cell number associated with the address string of up to 24 characters
fax Fax number associated with the address string of up to 32 characters
email Email address associated with the postal address string of up to 96 characters

Bundle rows

A bundle is a collection of products sold together as a single unit. When a bundle product is added to a sales order, it creates a single order row for that bundle product. On GET requests, this bundle row is shown in an expanded view with child rows representing the bundle's component products as they were at the point of creation. One level of nesting is supported (a bundle whose components are themselves bundles).

Updating a bundle row

  • Retain the bundle: include the parent row with its existing id. Omitting the parent deletes the entire bundle, including all child rows.
  • Composition is frozen at creation: component products, names and SKUs are carried over exactly as stored on the order. They cannot be changed via a PUT. If you want to regenerate the child rows from the product catalogue, you must re-create the parent row by omitting the row ID.
  • Child tax codes are inherited from the parent: each existing child row receives the tax code supplied for its incoming parent row. For nested bundles this applies recursively, so every descendant receives the top-level parent's tax code.
  • Any child rows included in the request are ignored. Child rows cannot be edited independently. They are reconstructed from the existing order data, with quantities and tax codes handled as described here; the product catalogue is not consulted on PUT.
  • Quantities are scaled automatically: changing the parent row quantity causes all child quantities to be scaled by the same ratio (e.g. doubling the parent quantity doubles every child quantity).

Adding a new bundle row

To add a new bundle row, provide the bundle parent without an ID. As with POST, the service expands the bundle and creates its component products as child sales order rows.

Removing a bundle row

To remove an existing bundle, omit its parent row from the rows array. The parent and all of its component rows are deleted, subject to the deletion restrictions described above.

Field summary: row costs

When productId is omitted or is 1000, either rowCost.averageCost or rowCost.unshippedCost must be provided. If both are provided, rowCost.averageCost will be used.

Field Description Data Type Required Values
rowCost.averageCost The average cost for this row. string When rowCost.unshippedCost is omitted and productId is omitted or is 1000. A number up to 4dp.
rowCost.unshippedCost The unshipped cost for this row. string When rowCost.averageCost is omitted and productId is omitted or is 1000. A number up to 4dp.

Field summary: offer rows

The goal of these attributes was to model discounts as free-text rows for the POS system. These attributes are mainly cosmetic and do not affect the order total. The supplied negative row net and tax values represent the discount amount.

This feature is not complete and should be used with caution.

Field Description Data Type Required Values
offerType The type of offer represented by this row string If offer row DISCOUNT, PROMOTION
offerReductionType The type of value reduction applied by this offer row, i.e. X% off vs $X off string If offer row PERCENTAGE, FIXED_PRICE
offerReductionMagnitude

The customer-facing magnitude of the reduction due to this offer. For example, 15 for a 15% off offer, or 10.00 for a $10.00 off coupon. This value is for display purposes only and is not to be used for calculations. An accurate representation of the discount amount is provided via the row's net and tax properties. It may appear that for FIXED_PRICE type discounts this property is redundant; however, consider the following cases:

  • Depending on the tax configuration of the channel, the fixed value reduction magnitude could be equal to the row's net value or the sum of the row's net and tax values.
  • In the case where a coupon exceeds the value of the applicable items, the gross value of the line would be less than the reduction magnitude of the coupon, i.e. a $10 coupon for a $9 item has a reduction magnitude of $10 but the gross offer line value would be -$9.
string If offer row A decimal with unrestricted precision
offerAssociatedOrderRowExternalRefs A list of external references of non-offer rows on this order to which this offer is related. This can include references supplied for new rows in this PUT request. For existing rows, use the stored external reference. On sales order GET, this property is replaced by offerAssociatedOrderRowIds and is therefore write-only. array of strings
offerAssociatedOrderRowIds A list of IDs of existing non-offer rows on this order to which this offer is related. Rows created by this request do not have IDs until they are persisted. array of integers No

Example

A request to update a sales order. This request may be sent with If-Match: 5, where 5 is the version obtained from the most recent sales-order GET response. The row IDs identify the existing rows to retain and update; omitting an ID creates a replacement row.

Request URI

/sales-order/42/

Request body

{
	"customerId": 6,
	"customer": {
		"address": {
			"addressFullName": "customer first name customer last name",
			"companyName": "company name",
			"addressLine1": "customer address line 1",
			"addressLine2": "customer address line 2",
			"addressLine3": "customer address line 3",
			"addressLine4": "customer address line 4",
			"postalCode": "customer postal code",
			"countryIsoCode": "GB",
			"telephone": "01234 567890",
			"mobileTelephone": "01234 567890",
			"email": "customer@email.com"
		}
	},
	"billing": {
		"contactId": 5,
		"address": {
			"addressFullName": "customer first name customer last name",
			"companyName": "company name",
			"addressLine1": "billing line 1",
			"addressLine2": "billing line 2",
			"addressLine3": "billing line 3",
			"addressLine4": "billing line 4",
			"postalCode": "billing postal code",
			"countryIsoCode": "GB",
			"telephone": "01234 567890",
			"mobileTelephone": "01234 567890",
			"email": "customer@email.com"
		}
	},
	"ref": "customer reference",
	"placedOn": "2024-10-12T09:25:35.000+01:00",
	"taxDate": "2024-10-12T00:00:00.000+01:00",
	"statusId": 2,
	"warehouseId": 2,
	"channelId": 1,
	"staffOwnerId": 4,
	"projectId": 1,
	"leadSourceId": 1,
	"priceListId": 2,
	"teamId": 1,
	"priceModeCode": "INC",
	"currency": {
		"code": "GBP",
		"exchangeRate": "2.000000",
		"fixedExchangeRate": true
	},
	"delivery": {
		"address": {
			"addressFullName": "customer first name customer last name",
			"companyName": "company name",
			"addressLine1": "delivery line 1",
			"addressLine2": "delivery line 2",
			"addressLine3": "delivery line 3",
			"addressLine4": "delivery line 4",
			"postalCode": "delivery postal code",
			"countryIsoCode": "GB",
			"telephone": "01234 567890",
			"mobileTelephone": "01234 567890",
			"email": "customer@email.com"
		},
		"shippingMethodId": 1,
		"date": "2025-01-01T00:00:00.000Z"
	},
	"rows": [
		{
			"id": 247,
			"productId": 1002,
			"name": "Nerf guns",
			"quantity": "20",
			"taxCode": "T20",
			"net": "200",
			"tax": "40",
			"nominalCode": "4000",
			"productPrice": "10.0000"
		},
		{
			"id": 248,
			"productId": 1003,
			"name": "Nerf darts",
			"quantity": "1000",
			"taxCode": "T20",
			"net": "100",
			"tax": "20",
			"nominalCode": "4000",
			"productPrice": "0.1000"
		}
	],
	"paymentDueDate": "2025-01-01T00:00:00.000Z",
	"orderWeighting": 100
}

Response

{
	"response": {
		"id": 42,
		"version": 6,
		"customer": {
			"id": 6,
			"address": {
				"addressFullName": "customer first name customer last name",
				"companyName": "company name",
				"addressLine1": "customer address line 1",
				"addressLine2": "customer address line 2",
				"addressLine3": "customer address line 3",
				"addressLine4": "customer address line 4",
				"postalCode": "customer postal code",
				"countryIsoCode": "GB",
				"telephone": "01234 567890",
				"mobileTelephone": "01234 567890",
				"fax": "",
				"email": "customer@email.com"
			}
		},
		"billing": {
			"contactId": 5,
			"address": {
				"addressFullName": "customer first name customer last name",
				"companyName": "company name",
				"addressLine1": "billing line 1",
				"addressLine2": "billing line 2",
				"addressLine3": "billing line 3",
				"addressLine4": "billing line 4",
				"postalCode": "billing postal code",
				"countryIsoCode": "GB",
				"telephone": "01234 567890",
				"mobileTelephone": "01234 567890",
				"fax": "",
				"email": "customer@email.com"
			}
		},
		"ref": "customer reference",
		"externalRef": "hsdf74-2js7-jdsfi38-73jsd8",
		"placedOn": "2024-10-12T09:25:35.000+01:00",
		"taxDate": "2024-10-12T00:00:00.000+01:00",
		"parentId": 1,
		"statusId": 2,
		"warehouseId": 2,
		"channelId": 1,
		"staffOwnerId": 4,
		"projectId": 1,
		"leadSourceId": 1,
		"teamId": 1,
		"priceListId": 2,
		"priceModeCode": "INC",
		"currency": {
			"code": "GBP",
			"exchangeRate": "2.000000",
			"fixedExchangeRate": true
		},
		"delivery": {
			"date": "2025-01-01T00:00:00.000Z",
			"address": {
				"addressFullName": "customer first name customer last name",
				"companyName": "company name",
				"addressLine1": "delivery line 1",
				"addressLine2": "delivery line 2",
				"addressLine3": "delivery line 3",
				"addressLine4": "delivery line 4",
				"postalCode": "delivery postal code",
				"countryIsoCode": "GB",
				"telephone": "01234 567890",
				"mobileTelephone": "01234 567890",
				"fax": "",
				"email": "customer@email.com"
			},
			"shippingMethodId": 1
		},
		"rows": [
			{
				"id": 247,
				"productId": 1002,
				"name": "Nerf guns",
				"sku": "1111-111-1111",
				"quantity": "20.0000",
				"taxCode": "T20",
				"tax": "40.00",
				"net": "200.0000",
				"nominalCode": "4000",
				"externalRef": "7hgfjksdf-234sfd-w34-sdf-w234",
				"productPrice": "10.0000",
				"discountPercentage": "0.00",
				"sequence": 10,
				"bundleChild": false,
				"bundleParent": false,
				"parentRowId": 0,
				"taxClassId": 7,
				"taxCalculator": "brightpearl",
				"clonedFromId": 0,
				"installedIntegrationInstanceId": 4
			},
			{
				"id": 248,
				"productId": 1003,
				"name": "Nerf darts",
				"sku": "1111-111-1112",
				"quantity": "1000.0000",
				"taxCode": "T20",
				"tax": "20.00",
				"net": "100.0000",
				"nominalCode": "4000",
				"externalRef": "723423-234-234dsdwe3-fsfefe4f-dfse3",
				"productPrice": "0.1000",
				"discountPercentage": "0.00",
				"sequence": 20,
				"bundleChild": false,
				"bundleParent": false,
				"parentRowId": 0,
				"taxClassId": 7,
				"taxCalculator": "brightpearl",
				"clonedFromId": 0,
				"installedIntegrationInstanceId": 4
			}
		],
		"total": {
			"net": "300.00",
			"tax": "60.00",
			"gross": "360.00",
			"baseNet": "150.00",
			"baseTax": "30.00",
			"baseGross": "180.00"
		},
		"paymentDueDate": "2025-01-01T00:00:00.000Z",
		"orderPaymentStatus": "UNPAID",
		"allocationStatusCode": "ANA",
		"stockStatusCode": "SON",
		"shippingStatusCode": "SNS",
		"installedIntegrationInstanceId": 4,
		"createdBy": 4,
		"createdOn": "2024-12-02T11:48:07.000Z",
		"updatedOn": "2024-12-09T11:39:05.000Z",
		"invoice": {
			"invoiceReference": "",
			"taxDate": "2024-10-12T00:00:00.000+01:00",
			"dueDate": "2025-01-01T00:00:00.000Z"
		},
		"orderWeighting": 100,
		"costPriceListId": 2,
		"isCanceled": false,
		"customerId": 6
	}
}