Shipping Management Integration

Last updated 01/31/2021

This guide will explain the necessary steps to correctly implement a successful shipping solution into Zentail.

The primary tasks of the integration are to:

  • Discover new Sales Orders that are ready to be processed.
  • Send Zentail the Shipping information (e.g. tracking).

This guide assumes you are familiar with registering an application and Authentication.

Here is a basic outline of what the integration needs to do:

  1. Find your warehouses
  2. Request order data from Zentail
  3. Send shipment notifications to Zentail

Find your warehouses

When a Zentail Customer installs your Application, they will also associate Warehouses in Zentail with Warehouse Unique Identifiers from your Shipping Management Software. This can be confirmed with an API call to view all of the Warehouses associated with your Integration:

shell
curl -X GET \ -H "Accept: application/json" \ -H "Authorization: <bearer token>" \ "https://api.zentail.com/v1/warehouses"

Example Response:

json
{ "results": [ { "warehouseId": 3, // Zentail ID for the Warehouse "warehouseUniqueId": "Warehouse 0001", // Your unique id for the warehouse "name": "Main Warehouse", // Customer's name for the Warehouse in Zentail "canUpdateInventory": true // deprecated } ] }

Request Order Data

The next step will be to request order data from Zentail.

The integration will poll the API to discover new Sales Orders. This requires making a GET /v1/salesOrder request with the recommended query parameters:

parametervaluedescription
statusPENDING,CANCELED,PARTIALLY_CANCELLEDPENDING orders should be fulfilled, CANCELED orders should not be fulfilled if they have not yet shipped.
lastUpdatedTsISO 8601 timestampThe time since the integration last received an order. If provided, that timestamp will provide a marker and only orders that have had some kind of modification after that timestamp will be included in the response.

The endpoint will automatically filter for Sales Orders that are being routed to the Warehouses that the integration is associated with.

shell
curl -X GET -H "Accept: application/json" \ -H "Authorization: <bearer token>" \ "https://api.zentail.com/v1/salesOrder?status=PENDING,CANCELED,PARTIALLY_CANCELLED&lastUpdatedTs=2021-01-22T16:15:46.740Z"

Example Response:

json
{ "results": [ { "orderNumber": "string", "status": "PENDING", "channel": "string", "channelLabel": "string", "channelOrderId": "string", "channelOrderReferenceNumber": "string", "customer_notes": "string", "marketplaceId": "string", "customerName": "string", "customerEmail": "string", "orderTs": "2021-01-22T18:57:34.312Z", "lastUpdatedTs": "2021-01-22T18:57:34.312Z", "requestedServiceLevel": "string", "standardServiceLevel": "Standard", "shipBy": "2021-01-22T18:57:34.312Z", "accounting": { "payment": 0, "channelTax": 0, "resellerCommission": 0, "shippingCost": 0, "itemCost": 0, "totalCost": 0, "revenue": 0, "profit": 0, "margin": 0, "itemPrice": 0, "shippingPrice": 0, "refund": 0 }, "shippingAddress": { "name": "string", "company": "string", "addressLine1": "string", "addressLine2": "string", "city": "string", "state": "string", "postalCode": "string", "phone": "string", "email": "string", "countryCode": "string", "type": "string" }, "billingAddress": { "name": "string", "company": "string", "addressLine1": "string", "addressLine2": "string", "city": "string", "state": "string", "postalCode": "string", "phone": "string", "email": "string", "countryCode": "string", "type": "string" }, "packages": [ { "packageId": 0, "carrier": "string", "serviceLevel": "string", "cost": 0, "tracking": "string", "shippedTs": "string", "rma": "string", "labelUrl": "string", "packageWeight": 0, "packageWeightUom": "string", "packageHeight": 0, "packageWidth": 0, "packageLength": 0, "products": [ { "sku": "string", "lineItemId": "string", "quantity": 0 } ] } ], "products": [ { "lineItemId": "string", "status": "PENDING", "requestedTitle": "string", "requestedSku": "string", "SKU": "string", "mpn": "string", "standard_product_id": "string", "title": "string", "quantity": 0, "routing_info": [ { "warehouseId": 0, "warehouseUniqueId": "string", "quantity": 0, "assembledQuantity": 0, "kitComponents": [ { "SKU": "string", "componentQuantity": 0 } ] } ], "cancelQuantity": "string", "shippedQuantity": "string", "unitPrice": 0, "cost": 0, "totalWeight": 0, "refund": "string", "refundReason": "string", "giftWrapMessage": "string", "giftWrapLevel": "string", "earliestShipBy": "2021-01-22T18:57:34.312Z", "latestShipBy": "2021-01-22T18:57:34.312Z", "earliestDeliverBy": "2021-01-22T18:57:34.312Z", "latestDeliverBy": "2021-01-22T18:57:34.312Z", "requestedServiceLevel": "string" } ], "returnOrders": [], "pagination": { "hasNext": true, "nextToken": "string" } }

At the root of the order, there is some important information:

FieldRecommended Use
orderNumberThis is the Zentail order number, use this as the unique identifier for this order for this particular account
statusAn indicator of the overall status of the order, PENDING_PAYMENT orders generally should not be shipped until they reach PENDING. For a full list of statuses see the docs
shippingAddressThis is the address that the items in this order should be shipped to, there will only ever be one shipping address per order.
packagesThis represents any packages already created for this order, each package may also contain items that we know are contained in the package.

Line Items

If you now examine the products field of the order response, there is more important information:

FieldRecommended Use
lineItemIdAn identifier unique to this order which represents this specific line item on the order.
statusSimilar to status above, this is a more granular indication of the status of this individual line item.
requestedSkuThis is the SKU requested by the sales channel, this may be a SKU that doesnt exist in Zentail. It may also not be the SKU that Zentail has decided should be used to fulfill this order. For the sku that should be used for filling the line item, see the SKU field.
SKUThis is the SKU that was matched in Zentail and should be fulfilled. It is possible that this field is NULL if the line item does not correspond to a known product in Zentail. In that case, requestedSku should be used as a fallback.
quantityThis is the quantity that was requested to fulfill this line item. If a warehouse ID filter is provided, it will be only the quantity routed to that warehouse.
cancelQuantityThis is the quantity that has been cancelled, this should be deducted from the quantity field above when determining how many items still need to be shipped.
shippedQuantityThis is the quantity that has already been shipped. Note: This quantity may reflected shipped orders from other warehouses so its not necessarily reliable if the warehouseId filter is used.


Kits / Multi-packs

Multi-packs or Kits may require additional consideration to account for inventory changes from sales. A customer may have a "Kit SKU" that is not present in the Warehouse, but derives its inventory from one or more SKUs that are in the Warehouse.

As an example, Warehouse1 stocks SKU TESTSKU1. The customer sells this SKU directly, as well as a multi-pack version with sku TESTSKU1-2PK. An order is placed like the following (note some fields are omitted for brevity):

json
{ "orderNumber": "1000005", "status": "PENDING_PAYMENT", "orderTs": "2021-01-22T09:39:57-05:00", "lastUpdatedTs": "2021-01-22T09:39:57-05:00", "products": [ { "lineItemId": "0", "status": "PENDING_PAYMENT", "requestedSku": "TESTSKU1-2PK", "SKU": "TESTSKU1-2PK", "quantity": 2, "routing_info": [ { "warehouseId": 3, "quantity": 2, "assembledQuantity": 0, "kitComponents": [ { "componentQuantity": 2, "SKU": "TESTSKU1" } ], "warehouseUniqueId": "Warehouse1" } ] } ] }

To determine the correct quantity to fulfill, routing_info fields must be utilized. In this example, an order was placed for 2 of TESTSKU1-2PK as indicated by the quantity. The routing_info["quantity"] indicates that both of the requested orders for TESTSKU1-2PK were routed to the warehouse Warehouse1. The routing_info["kitComponents"][0]["componentQuantity"] indicates that each TESTSKU1-2PK is composed of 2 of TESTSKU1. Thus the total amount of inventory to fulfill is 4 of TESTSKU1, or routing_info["quantity"] * routing_info["kitComponets"][0]["componentQuantity"].

Also make note of the routing_info["assembledQuantity"] field. If your Warehouse tracks Kits, and provides Zentail with the assembled quantity via the update inventory requests, this field will include the amount of already assembled units that should be fulfilled. As an example, consider the following (note, again some fields are omitted for brevity):

json
{ "orderNumber": "1000005", "status": "PENDING_PAYMENT", "orderTs": "2021-01-22T09:39:57-05:00", "lastUpdatedTs": "2021-01-22T09:39:57-05:00", "products": [ { "lineItemId": "0", "status": "PENDING_PAYMENT", "requestedSku": "TESTSKU1-2PK", "SKU": "TESTSKU1-2PK", "quantity": 2, "routing_info": [ { "warehouseId": 3, "quantity": 2, "assembledQuantity": 1, "kitComponents": [ { "componentQuantity": 2, "SKU": "TESTSKU1" } ], "warehouseUniqueId": "Warehouse1" } ] } ] }

In this case there is already an assembled TESTSKU1-2PK. So one unit of that assembled SKU should be fulfilled, and the remaining quantity should be fulfilled as 2 units of the component SKU, TESTSKU1

Sending Shipment Notifications

The final step in most shipping service integrations is to notify Zentail when items are shipped. An important thing to note here is once we receieve the tracking information, future updates are not guaranteed to take effect on the sales channels. Most sales channels do not allow us to modify tracking once we have sent it to them.

Once the user ships an order in your system, you can notify Zentail using the POST /salesOrder/shipments endpoint.

The request to that endpoint involves up to 50 packages at a time (they do not need to all belong to the same order) with the following required fields.

Required Fields

The following fields are always required to create packages. Please note that creating a package alone is not enough to mark an order line item shipped in our system (and therefore trigger us to provide the shipment info to the relevant channels). See the next section on creating the main shipment for an order.

FieldRecommended Use
orderNumberThis is the order number from the above GET /salesOrder call, this is the order for which this package belongs.
fulfillmentPackageIdThis is a unique identifier which represents the package in your system.
warehouseUniqueIdThe warehouse unique ID this package is shipping from, should correspond to the warehouse Unique ID for which your integration is responsible
carrierThe shipping carrier that is delivering this package. Though it is not enforced, we strongly recommend sticking to simple carrier names like USPS, UPS, FedEx etc...
trackingThe tracking number provided by the carrier, this is required by most, if not all, sales channels.
service_levelThe shipping service level used for this package, something like "Ground", "Priority" or "First Class" for example.


Those are the only required values to create a new package on an order. As mentioned above, this is not enough to indicate to Zentail that an order has been shipped. In order to do so, you must provide the products entry on the package. This entry should contain a mapping of the SKU and quantity that is packaged in this package.

Zentail only considers an order fully shipped when all of the products on the order are accounted for. In the meantime it may reside in a Partially Shipped status.

An example request to mark a line item shipped may look like this:

shell
curl -X POST -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: <bearer token>" \ "https://api.zentail.com/v1//salesOrder/shipments" -d '{ "packages": [ { "orderNumber": 10000004, "fulfillmentPackageId": "ShipCo #123456", "warehouseUniqueId": "WAREHOUSE_UNIQUE_ID", "carrier": "USPS", "tracking": "12345667890002394230", "service_level": "First Class", "products": [{ "sku": "testSku1", "quantity": 5 }] }, { "orderNumber": 10000004, "fulfillmentPackageId": "ShipCo #123457", "warehouseUniqueId": "WAREHOUSE_UNIQUE_ID", "carrier": "USPS", "tracking": "12345667890002394231", "service_level": "Priority", "products": [ { "sku": "testSku2", "quantity": 1 }, { "sku": "testSku3", "quantity": 4 } ] } ] }'


Additional Packages

You can use the same endpoint to indicate additional packages on an order. Keep in mind though that only packages associated with products will be communicated to the channels (and the products can only be included once per-unit).

Conclusion

Hopefully this document has proved helpful in designing and understanding how to integrate with the Zentail API as a shipping service provider. If you have any questions, please consult the documentation or email support@zentail.com.

Was this section helpful?
Yes No