Inventory IMPEX Best Practices

Inventory Impex APIs enable you to manage imports and exports of inventory availability for Omnichannel Inventory (OCI). This document is meant to assist you in following some best practices while implementing your use of the OCI service, as well as provide some information about the service itself.

  • ALWAYS use the high performant import process. The legacy import process should no longer be used and will be deprecated. The new method has much higher performance and is the method of choice for performing imports. For more information regarding the high-performance changes, please refer to "Import File Layout - High Performance" in the Inventory Impex documentation.

  • Don’t perform an import of inventory data while location graph changes are in progress. Doing so can cause inventory inconsistencies between the group and the locations that it contains. If you’re running scheduled imports, please ensure that they aren’t running during location graph changes, or are paused until those changes are complete. This ensures that you have an accurate picture of inventory in the system across the locations and groups.

  • For updating small batches of data, we recommend using the batchInventoryUpdate API call, rather than performing a full import. There’s a limitation of 100 SKUs per batch, however, this is faster than loading the data via file.

  • If using the batchInventoryUpdate API call, and you find that you’re updating more than 1000 SKUs via multiple calls to the API, this is the time to switch back over and use the inventory file import process.

  • For best performance, use delta data imports for updating things that have changed rather than full data imports. Full data imports are more time intensive and shouldn’t be used unless necessary.

  • Be very careful in the scheduling of your data imports. Ensure that you’re allowing ample time between full data imports. Multiple customers have had difficult situations arise because they’ve scheduled their imports too closely together (some as close as one minute). Recovery from misusing the service in this way is difficult and can create problems for your go-live and implementation plans.

  • When processing large amounts of import data, you can split your data into multiple files and submit them separately. However, there are rate and concurrency limits within the OCI service and infrastructure. For this reason, it’s best to group inventory imports into fewer files rather than large numbers of smaller files.

  • Separate the top-level entries in your import file with line feeds, not with commas. Each entry in the file must be on a single line.
  • The mode is always assumed to be UPDATE.
  • Each inventory record entry request has a unique idempotence recordId. Best practice in this case is to use a UUID. The record ID protects against the import of duplicate data.
  • Each inventory record imported requires an effectiveDate entry.
  • Each inventory Future record imported requires a nonzero quantity and expectedDate.
  • The system only creates or updates entries that are included in the data. It doesn’t delete entries that aren't included. However, if an included entry has an empty value, it will be ignored and reported as an error.
  • To avoid overselling during the initial implementation period, we recommend defining safety stock levels.

When performing an import for item availability records, the following endpoints should be used in this order:

  1. Initiate the import with a call to the imports POST URI. https://{shortCode}.api.commercecloud.salesforce.com/inventory/impex/v1/organizations/{organizationId}/availability-records/imports

    Use the availability-records imports POST endpoint to initiate a load of the inventory availability data into Omnichannel Inventory. The details of the data being imported are included in the body of the POST request.

  2. Use the uploadLink from the response body of step 1 to upload the data stream/file to OCI using a POST call. https://{shortCode}.api.commercecloud.salesforce.com/inventory/impex/v1/organizations/{organizationId}/availability-records/imports/uploadlink/{uploadLinkId}

    The resulting response body from the initiate call contains a link to submit the actual import data stream/file. If the file being uploaded into the system is larger than 100 MB, the file must be compressed with gzip.

  3. After the file has been uploaded into OCI, you can use a GET call to the importStatusLink that was provided in the response body to the initiate call of step 1, to check on the status of the processing of your import data. https://{shortCode}.api.commercecloud.salesforce.com/inventory/impex/v1/organizations/{organizationId}/availability-records/imports/{importId}/status

    After the file has been successfully uploaded to the OCI system, the status of the import can be checked by making calls to the availability-records imports status endpoint. When using this endpoint in conjunction with the importId used in the file upload, you can see if the import process has completed yet.

  4. After the status field in the response body returned from step 3 is COMPLETED, the import of the availability record data into OCI is complete. The status response includes a fullResults href field that has a URI for retrieving the details about the completed import process. This information is retrieved with a GET call to the provided URI. https://{shortCode}.api.commercecloud.salesforce.com/inventory/impex/v1/organizations/{organizationId}/availability-records/imports/{importId}/file-content

    After the file has been successfully processed by OCI, the response body includes a status of COMPLETED. Other metrics regarding the import are also included, and can be found in the documentation for the status call.

    Also included in the COMPLETED status is the fullResults href. Using this URI, you can retrieve the detailed results of the completed import process.

Following these steps in this order, with the endpoints indicated, allows you to successfully import availability records into OCI.

This is the preferred import file layout:

The mode is assumed to be UPDATE since it's currently the only one supported.

{"recordId":"0a87539d-f3dd-47bc-91c7-9c752e39dbe0","onHand":10,"sku":"sku1","effectiveDate":"2020-04-08T14:05:22.790896-07:00","futures":[{"quantity":1,"expectedDate":"2020-04-18T14:05:22.781-07:00"}],"safetyStockCount":0,"locationId":"wickenburg"}

{"recordId":"3127e2ad-748b-459a-917c-78bef741602c","onHand":10,"sku":"sku1","effectiveDate":"2020-04-08T14:05:22.790896-07:00","futures":[{"quantity":1,"expectedDate":"2020-04-18T14:05:22.781-07:00"}],"safetyStockCount":0,"locationId":"prescott"}

{"recordId":"b709e7a8-7d4f-4458-be0c-a88f1e594f1d","onHand":10,"sku":"sku2","effectiveDate":"2020-04-08T14:05:22.790896-07:00","futures":[{"quantity":1,"expectedDate":"2020-04-18T14:05:22.781-07:00"}],"safetyStockCount":0,"locationId":"wickenburg"}

{"recordId":"b34c99ea-e659-40f7-bcb4-2a3fdc4a80b0","onHand":10,"sku":"sku2","effectiveDate":"2020-04-08T14:05:22.790896-07:00","futures":[{"quantity":1,"expectedDate":"2020-04-18T14:05:22.781-07:00"}],"safetyStockCount":0,"locationId":"prescott"}

{"recordId":"4bf7b40b-c965-485c-afa0-cc56a8ea70eb","onHand":10,"sku":"sku3","effectiveDate":"2020-04-08T14:05:22.790896-07:00","futures":[{"quantity":1,"expectedDate":"2020-04-18T14:05:22.781-07:00"}],"safetyStockCount":0,"locationId":"wickenburg"}

{"recordId":"539d41b7-5f05-4908-831b-3dd0461794fc","onHand":10,"sku":"sku3","effectiveDate":"2020-04-08T14:05:22.790896-07:00","futures":[{"quantity":1,"expectedDate":"2020-04-18T14:05:22.781-07:00"}],"safetyStockCount":0,"locationId":"prescott"}

There are no other requirements for indicating that you’re using the High-Performance Layout. The system reads the file and automatically determines how to process it.

For information regarding the field contents, please see the Import Field Definitions section below.

Import Field Definitions

Location Header:

FieldDescriptionRequired Field
locationThe physical location where inventory is housed.TRUE
modeThe type of system operation to perform. Currently, only UPDATE is supportedTRUE

Location Inventory:

FieldDescriptionRequired Field
recordIdUnique identifier of inventory record.TRUE
skuUnique item identifier.TRUE
onHandThe current quantity on hand.FALSE
effectiveDateThe effective date of the onHand (ISO format).FALSE, see the additional description below for more detail.
futuresA collection of future quantity information for the item. (See Future Quantity, below.) Note: the Inventory Availability API references futures in a collection called, futureStock. These are the same thing.FALSE, If specified see the required fields below.
safetyStockCountThe safety stock count.FALSE

Future Quantity:

FieldDescriptionRequired Field
quantityThe quantity that will be arriving in the future. The value must be greater than 0.TRUE
expectedDateThe date the future quantity is expected to arrive (ISO format).TRUE

Availability Effective Date:

The effectiveDate field represents the time the onHand value was accurate at the physical location. It’s used in conjunction with the reservation fulfillment field, fulfillmentTime to keep onHand in sync with the physical location. For example, if effectiveDate is specified on a location record, the system will optionally decrement the value of onHand when a Reservation Fulfillment is made. See the Inventory Reservation API for more information on how Reservation Fulfillments work.

Example:

Import happens for SKU: abc at location: 123, and the value of onHand accounts for a Reservation Fulfillment that hasn't yet processed in OCI.

The JSON is shown formatted here, however in the actual import file it must all be on a single line.

Reservation Fulfillment Request happens after the import for SKU: abc at location: 123

The system compares the values of effectiveDate and fulfillmentTime. Since fulfillmentTime is less than effectiveDate it does not decrement the value of onHand. If the reservation fulfillment happened first, onHand would be decremented (assuming the previous value of effectiveDate was less than fulfillmentTime). Then when the system performed the inventory import, the value of onHand would replace the previous value.

Location Inventory Import Result File Layout

The result file starts with a status line record.

{"status": "COMPLETED_WITHOUT_ERRORS"}

Result StatusDescription
COMPLETED_WITHOUT_ERRORSImport completed without any errors.
COMPLETED_WITH_PARTIAL_FAILURESSome records failed to load.
FAILEDFailed to load any records.

Each line following the status line represents an error record.

{"recordId": "2a3e769c-c981-4d96-ba6a-e0b821a5bbbb","locationId":"location1","sku": "sku1", "message": "Unknown"}

FieldDescriptionRequired Field
recordIdUnique identifier of inventory record sent in import file.TRUE
locationIdLocation identifier.TRUE
skuUnique item identifier of an item.TRUE
messageMessage containing details for failure.TRUE

All endpoints have a theoretical limit of approximately 50 requests per second for the same sku/locationGroup combination. For this reason, things like flash sales struggle to perform optimally. When making large numbers of requests for an individual SKU/locationGroup, where possible, it’s recommended that you combine or batch your requests into one for availability, reservation, transfer, fulfill, and so on. That aids in keeping performance optimized across your implementation.

The following error codes apply to all API endpoints. See the API documentation for a complete list of the error codes and response bodies that can be expected on each endpoint.

  • 401 - The OATH bearer token isn’t valid for the tenant or is no longer valid. The response body has the details.
  • 403 - The tenant isn’t provisioned in the system.
  • 5XX - There was a server error. The response body has the details.
  • Unknown - If an Unknown error type is returned, it indicates that an unknown internal error occurred.

Currently, there are a number of places where a consumer can submit an ExternalRefId with their request. This is to allow the consumer to submit user-generated data along with their request as a link to other external systems data. For example, if I'm pulling data from an order that has an order ID, I can submit that order ID with my reservation request as the externalRefId. Then, if there’s a problem with the reservation, there’s an easy way to connect it with the order in the external system that generated the error or problem. This is NOT to be confused with externalRefId used in Salesforce Core, or any other services. The usage is exclusive to OCI and isn’t used anywhere else.

To facilitate tracking of operations in OCI, customers must populate a header named correlation-id, which is used by the CDN to send the request to OCI. This header helps trace requests as they move throughout our service.

Some services use a header named x-correlation-id. If this header is used with OCI, it’s overwritten by the CDN with a new value and the initial purpose of the header is defeated. Use the correlation-id instead for a consistent result.