Sales Orders
Sales Orders API Reference This page provides a comprehensive reference for all parameters, endpoints, and behaviors of the Sales Orders API. Use this guide to understand request/response structures, validation rules, and integration best practices.
Purpose: The Sales Orders API manages all aspects of sales order data, including creation, updates, and termination. Each order contains a set of pre-configured products to be delivered to, and paid for by, a customer associated with the order.
Base URL:
/v1/sales-orders
Authentication:
All APIs require a Client ID and Client Secret.
Refer to the global authentication documentation for details.
System Parameters:
System parameters for this API are available in Online Help → System parameters and values → System parameters → Logistics → Sales (SO)
Supported Methods
| HTTP Method | Endpoint | Description | Limits & Notes |
|---|---|---|---|
| POST | /v1/batch/sales-orders | Creates many sales orders in bulk. | Asynchronous operation. Accepts a JSON file containing an array of sales orders following the POST /v1/sales-orders DTO format. Returns 201 Created immediately with a URL to track the batch operation. Hard limits depend on the optional skipWorkflow parameter value: 12 detail lines / 37,000 DB rows (skipWorkflow=false) or 1,000 detail lines / 1,550,000 DB rows (skipWorkflow=true). Automatic sales order number assignment is allowed if the order uses a number type with a number series that has an automatic assignment method. |
Swagger / Schema
Version v1
Method Details
Sample Request
Header details:
- content-type: multipart/form-data;
Body details (only one row of the form-data):
- name: leave it empty
- value: attach the JSON file with the content[
{
"customerId": "1000",
"transactionType": "SO",
"responsible": "FRED",
"salesPerson": "SYSEN",
"orderLines": [
{
"productCode": "1000",
"price": 50.0,
"quantity": 1,
"glAnalysis": [
{
"accounting": {
"dim1": {
"dimValue": "120"
},
"dim2": {
"dimValue": "1000"
}
}
}
]
}
]
},
{
"customerId": "1015",
"transactionType": "SO",
"responsible": "FRED",
"salesPerson": "SYSEN",
"orderLines": [
{
"productCode": "NESP02",
"price": 0.90,
"quantity": 50,
"glAnalysis": [
{
"accounting": {
"dim1": {
"dimValue": "120"
},
"dim2": {
"dimValue": "1000"
}
}
}
]
}
]
}
][
{
"accountable": "Fredrik Grinøy",
"customerId": "1015",
"isBackToBackOrderUsed": false,
"isBilledAtProject": false,
"currencyCode": "GBP",
"isPartOfConsolidatedInvoice": false,
"externalReference": "",
"externalOrderId": "",
"followUp": "2026-04-29",
"notes": "",
"orderDate": "2026-04-29",
"orderNumber": 21990055,
"orderType": "VS",
"period": 202601,
"responsible": "SYSEN",
"salesPerson": "SYSEN",
"orderStatus": "N",
"transactionType": "SO",
"defaultGLAnalysis": {
"dim1": {
"attributeId": "C1",
"attributeIdDescription": "Cost centre",
"dimValue": "",
"dimValueDescription": ""
},
"dim2": {
"attributeId": "B0",
"attributeIdDescription": "Project code",
"dimValue": "",
"dimValueDescription": ""
},
"dim3": {},
"dim4": {},
"dim5": {},
"dim6": {},
"dim7": {}
},
"deliveryInformation": {
"thirdPartyDeliveryId": "1015",
"thirdPartyDeliveryAddressId": 449,
"thirdPartyTypeDeliveryAddress": "R",
"thirdPartyDeliveryAddress": "Alfheim 7\r\n1370 ASKER\r\nNorway",
"deliveryMethodDescription": "Delivery by car",
"deliveryTermsDescription": "Free on Board",
"deliveryComment": "",
"deliveryDate": "2026-04-29",
"deliveryMethod": "CAR",
"deliveryTerms": "FOB"
},
"invoicing": {
"customerAddressId": 449,
"invoiceRecipientId": "1015",
"headerText": "",
"footerText": "",
"paymentMethod": "CH",
"paymentTerms": "30",
"text1": "",
"text2": "3",
"text3": "",
"text4": ""
},
"orderLines": [
{
"distributionKey": 0,
"productCode": "NESP02",
"productDescription": "Dharkan",
"isBackToBackOrderUsedOnLine": false,
"backToBackOrderNumber": 0,
"isCustomerAddressUsedOnBackToBackOrder": false,
"currencyAmount": 45,
"lineDiscountPercent": 0,
"lineDeliveryDate": "2026-04-29",
"status": "N",
"backToBackOrderPriority": "2",
"price": 0.9,
"productText": "",
"quantity": 50,
"accountingTemplate": "",
"unit": "UN",
"glAnalysis": [
{
"accounting": {
"dim1": {
"attributeId": "C1",
"attributeIdDescription": "Cost centre",
"dimValue": "120",
"dimValueDescription": "Purchase & Stock Control"
},
"dim2": {
"attributeId": "B0",
"attributeIdDescription": "Project code",
"dimValue": "1000",
"dimValueDescription": "Project 1000"
},
"dim3": {},
"dim4": {
"attributeId": "BF",
"attributeIdDescription": "Work order code",
"dimValue": "",
"dimValueDescription": ""
},
"dim5": {
"attributeId": "B1",
"attributeIdDescription": "Activity code",
"dimValue": "",
"dimValueDescription": ""
},
"dim6": {},
"dim7": {}
},
"account": "3013",
"amount": 45,
"percentageSplit": 100,
"taxCode": "3N",
"taxSystem": "EU"
}
]
}
]
}
]Important Notes
The Batch API does not receive a JSON object in the request body, but instead, it expects a JSON file (GZIP-compressed files containing the JSON are also accepted, for cases where the file size exceeds the batch framework’s upload limit). Please refer to the “POST Structure Details” tab above to understand how to include the file in the request, and to the “Simple/Basic Example” tab above for the minimal expected file content, or to the “Complete Example” tab above for a full example with all available fields.
The call is asynchronous. The endpoint returns a 201 Created response immediately, containing a URL to track the batch operation (this is found in the response header, under the ’location’ property). Use the Batch Operations API endpoints to follow up:
GET /v1/batch-operations/{batchRequestId}/status— Check if the process is pending, in progress, completed, or failed.GET /v1/batch-operations/{batchRequestId}/results— Download the results file with details on any failed orders. Use the optionaltype=successparameter to retrieve a summary of successfully created orders instead.POST /v1/batch-operations/{batchRequestId}/cancel— Cancel a running process.
Valid orders are saved per page (100 sales orders at a time) directly into the Sales Orders tables as they are processed.
Invalid orders are not saved to the system. Error details for these orders are available via the results endpoint. To retry, correct the errors in the failed entries and resubmit them in a new Sales Order Batch request.
Multi-client capability: All sales orders in a batch are created in the same company. The company is determined by the optional companyId URL query parameter, supplied in the request URL as ?companyId={companyId} (for example: POST /v1/sales-orders/batch?companyId=123). If companyId is omitted in the query string, the authenticated user’s default company is used for all orders. If companyId is provided in the query string, all orders are created in that company, provided the authenticated user has access to it. Any companyId field present in individual order DTOs is ignored. Only the URL query parameter and the authenticated user’s default company are considered.
Limits & Notes
There are two types of hard limits for the batch endpoint of the Sales Orders API:
- On the number of detail lines per sales order.
- On the total number of database items to be stored. Database items are the sum of order header, detail, and GL rows generated in total. This can be estimated using the following formula:
- Database_Rows = SUM(1 + SalesOrderDetails * 3)
- Example: The total number of database rows for a batch of 2 orders (one with 4 and the other with 5 details) is (1+4*3) + (1+5*3) = 13+16 = 29 items
Note: The formula above is an approximation based on average order complexity. The actual number of database rows may vary depending on the specific configuration of each order.
These limits vary depending on the value of the skipWorkflow flag.
With skipWorkflow=false (or by default when the parameter is not provided):
- Maximum order detail lines: 12 detail lines per sales order.
- Maximum database items: 37,000 items per call.
- Using the formula above, if all orders have 12 details, you can create up to 1,000 sales orders per call.
- Workflow tasks are generated for each detail line of successfully created sales orders.
With skipWorkflow=true:
- Maximum order detail lines: 1,000 detail lines per sales order.
- Maximum database items: 1,550,000 items per call.
- Using the formula above, if all orders have 1,000 details, you can create up to 516 sales orders per call. If all orders have 10 details, you can create up to 50,000 total orders.
- No workflow tasks are generated. Orders can be printed, picked, and invoiced normally.
General:
- The file must be a valid JSON array, where each element follows the POST
/v1/sales-ordersrequest body structure. - Automatic sales order number assignment is allowed if the order uses a number type with a number series that has an automatic assignment method.
- Ensure sufficient posting cycles are available for the volume of orders being created.
Validation Rules for POST Batch Method
Hard limits or content validation errors are not captured by the asynchronous call, but rather when calling the batch-operations endpoint to get results.
POST_001 - User is not authorised
| Element | Details |
|---|---|
| Scenario ID | POST_001 |
| Scenario Name | User is not authorised |
| HTTP Code | 401 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| BUT | User does NOT have permissions |
| THEN | API is not reached; 401 Unauthorized response is returned |
| Example Error Message | "Unauthorized." |
POST_002 - File not sent with the request
| Element | Details |
|---|---|
| Scenario ID | POST_002 |
| Scenario Name | File not sent with the request |
| HTTP Code | 400 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| BUT | User has not attached the JSON file into the call |
| THEN | API is not reached; 400 Bad Request response is returned |
| Example Error Message | "File not present in MIME multipart content." |
POST_003 - Hard limit exceeded: detail lines per order
| Element | Details |
|---|---|
| Scenario ID | POST_003 |
| Scenario Name | Hard limit exceeded: detail lines per order |
| HTTP Code | 201 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| BUT | One or more sales orders in the file contain more detail lines than the configured hard limit |
| THEN | 201 Created is returned immediately; once the process finishes, the GET /v1/batch-operations/{batchRequestId}/status endpoint returns a processorResult of SuccessWithValidationErrors; the GET /v1/batch-operations/{batchRequestId}/results endpoint returns the failing sales orders with a per-order error message |
| Example Error Message Without SkipWorkflow | "The number of detail lines per order exceeds the maximum allowed limit of 12." |
| Example Error Message With SkipWorkflow | "The number of detail lines per order exceeds the maximum allowed limit of 1000." |
POST_004 - Hard limit exceeded: database items per call
| Element | Details |
|---|---|
| Scenario ID | POST_004 |
| Scenario Name | Hard limit exceeded: database items per call |
| HTTP Code | 201 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| BUT | The overall number of database items to be stored for the batch exceeds the configured hard limit |
| THEN | 201 Created is returned immediately; once the process finishes, the GET /v1/batch-operations/{batchRequestId}/status endpoint returns a processorResult of SuccessWithValidationErrors; the GET /v1/batch-operations/{batchRequestId}/results endpoint returns the failing sales orders with a per-order error message |
| Example Error Message Without SkipWorkflow | "The number of items to be saved will exceed the maximum allowed limit of 37000." |
| Example Error Message With SkipWorkflow | "The number of items to be saved will exceed the maximum allowed limit of 1550000." |
POST_005 - Happy Path: all orders are valid
| Element | Details |
|---|---|
| Scenario ID | POST_005 |
| Scenario Name | Happy Path: all orders are valid |
| HTTP Code | 201 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| THEN | 201 Created is returned immediately with a URL to track the batch operation; once the process finishes, the GET /v1/batch-operations/{batchRequestId}/status endpoint returns a processorResult of Success; the GET /v1/batch-operations/{batchRequestId}/results endpoint returns an empty array by default; using the optional type=success parameter on the results endpoint returns an array of summarized DTOs for the orders that were stored |
POST_006 - Partial success: some orders are invalid
| Element | Details |
|---|---|
| Scenario ID | POST_006 |
| Scenario Name | Partial success: some orders are invalid |
| HTTP Code | 201 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| BUT | One or more orders in the file contain validation errors (e.g., invalid customer, missing required field, invalid product) |
| THEN | 201 Created is returned immediately; once the process finishes, the GET /v1/batch-operations/{batchRequestId}/status endpoint returns a processorResult of SuccessWithValidationErrors, along with a validationErrorCount indicating how many orders failed and a processedItems value with the total number of orders processed; the GET /v1/batch-operations/{batchRequestId}/results endpoint returns only the failing orders with their specific per-order errors; using the optional type=success parameter on the results endpoint returns only the successfully created orders as summarized DTOs |
POST_007 - Duplicate order number
| Element | Details |
|---|---|
| Scenario ID | POST_007 |
| Scenario Name | Duplicate order number |
| HTTP Code | 201 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API |
| BUT | One or more orders use a transaction type that requires manual order number assignment, and the specified order number already exists in the system |
| THEN | 201 Created is returned immediately; once the process finishes, the GET /v1/batch-operations/{batchRequestId}/status endpoint returns a processorResult of SuccessWithValidationErrors; the GET /v1/batch-operations/{batchRequestId}/results endpoint returns the failing sales orders with a per-order error message |
| Example Error Message | "Order number in use !" |
POST_008 - User does not have access to the specified company
| Element | Details |
|---|---|
| Scenario ID | POST_008 |
| Scenario Name | User does not have access to the specified company |
| HTTP Code | 403 |
| GIVEN | A batch of new sales orders needs to be created |
| WHEN | Calling the POST batch method of the API with a companyId URL parameter |
| BUT | The authenticated user does not have access to the specified company |
| THEN | The request is rejected before the batch operation is processed, and a 403 Forbidden response is returned immediately |
| Example Error Message | "User is not authorized" |
Best Practices
- Use the single-order endpoint for individual orders: If only one sales order needs to be created, use the synchronous
POST /v1/sales-ordersendpoint instead. Reserve the batch endpoint for scenarios where multiple sales orders need to be created at once. - Handle partial failures by checking results: When the
GET /v1/batch-operations/{batchRequestId}/statusendpoint reports failures, callGET /v1/batch-operations/{batchRequestId}/resultsto retrieve the list of sales orders that were not saved. Fix the reported errors and resubmit those orders in a new batch API call. - Use the same
transactionTypefor all orders in a batch when possible: A known issue in the API can cause abnormal behavior in order number assignments when orders with differenttransactionTypevalues are mixed within the same batch request. Where possible, group orders by transaction type and submit them in separate batch calls.