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:
-
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.
-
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.
-
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.
-
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.
Location Header:
Field | Description | Required Field |
---|---|---|
location | The physical location where inventory is housed. | TRUE |
mode | The type of system operation to perform. Currently, only UPDATE is supported | TRUE |
Location Inventory:
Field | Description | Required Field |
---|---|---|
recordId | Unique identifier of inventory record. | TRUE |
sku | Unique item identifier. | TRUE |
onHand | The current quantity on hand. | FALSE |
effectiveDate | The effective date of the onHand (ISO format). | FALSE , see the additional description below for more detail. |
futures | A 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. |
safetyStockCount | The safety stock count. | FALSE |
Future Quantity:
Field | Description | Required Field |
---|---|---|
quantity | The quantity that will be arriving in the future. The value must be greater than 0. | TRUE |
expectedDate | The 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.
The result file starts with a status line record.
{"status": "COMPLETED_WITHOUT_ERRORS"}
Result Status | Description |
---|---|
COMPLETED_WITHOUT_ERRORS | Import completed without any errors. |
COMPLETED_WITH_PARTIAL_FAILURES | Some records failed to load. |
FAILED | Failed 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"}
Field | Description | Required Field |
---|---|---|
recordId | Unique identifier of inventory record sent in import file. | TRUE |
locationId | Location identifier. | TRUE |
sku | Unique item identifier of an item. | TRUE |
message | Message 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.
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.