Receive Fulfillment Order Receipts
Playbook can process fulfillment order receipts from one ore many warehouses via a webhook endpoint.
When the Webhook - Process Shipments
Playbook Step is installed on a business
it provides a synchronous endpoint that can be used to post order fulfillment request acknowledgements (fulfillment order receipts) back to ChannelApe. It handles much more
than our
order update endpoint.
It will match up to pending fulfillments and update fulfillment status. It will
also handle making inventory adjustments on the warehouse and virtual sales
channel locations when needed for complete, short, and backorder shipments.
Configuration
Install the Webhook - Process Shipments
Playbook Step on a business using the
app.
-
Login to appx.channelape.io
-
Install playbook step on business by going to this URL:
https://appx.channelape.io/{{businessId}}/embed/playbook/steps/a970417b-bb48-47cc-b3ea-58ca5d9edbc1/install
- Replace businessId with the ID of the business you are using
-
Ensure Synchronous is enabled
-
Ensure Authenticated Callback is enabled
- This flag will use the provided authorization headers on the callbacks endpoint
-
Set version to
STAGING
if this is a Staging Business otherwise usePRODUCTION
-
Under
Order Filtering
you may optionally select 1 or many channels if you know that you will only be processing fulfillment order receipts for those channels. All other fields can be left alone since they are not used. -
Enter a valid value for your Virtual Sales Channel Location ID where inventory adjustments could be made based on fulfillment order receipt activity.
-
Fill in the Warehouse Location Map with all of the locations you expect to receive fulfillment order receipts for.
-
Select Install
-
Refresh the Playbook ➝ Steps Configuration page and select the new step you just installed
-
Select
Copy Callback URL
- This URL is your endpoint to post fulfillment order receipts too
- You will need to pass in a valid authorization headers along with the payload
-
Optionally select 1 or many channels on the Steps Configuration page
- Will help with additional filtering when receiving a payload. This is usually only needed if you wish to match a Purchase Order Number or Warehouse Order ID that is duplicated across channels. In this case you would select the single channel you are interested in receiving ship confirmations for.
Example
Given the following values:
- Callback URL set to https://callbacks.channelape.com/v1/suppliers/f81c87c7-2692-4088-a396-05872695ff93/callbacks
- API Account for business with private key of e375b408-3c06-41ee-9df2-00402c7472b1
- WAREHOUSE_LOCATION_MAP set to:
[
{
"warehouse": "RJT1",
"company": "United Parcel Service",
"locationId": "942",
"name": "UPS East Coast",
"id": "UPSF",
"zipCode": "40165"
},
{
"warehouse": "RJT2",
"company": "United Parcel Service",
"locationId": "874",
"name": "UPS West Coast",
"id": "UPSW",
"zipCode": "91752"
}
]
- Order with purchaseOrderNumber of
CAT333
has 1PENDING
fulfillment with the following line items:- SKU
sku-1
, Quantity 2 - SKU
sku-2
, Quantity 3
- SKU
Request sent:
POST https://callbacks.channelape.com/v1/suppliers/f81c87c7-2692-4088-a396-05872695ff93/callbacks
Headers sent:
Authorization: Bearer YOUR_KEY
apikey: ANON_KEY
Content-Type: application/json
Payload sent:
{
"orderUpdate": {
"fulfillments": [
{
"purchaseOrderNumber": "CAT333", // optional
"additionalFields": [
{
"name": "warehouse_order_id",
"value": "CAT333-3"
}
],
"locationId": "874",
"shippingCompany": "UPS",
"shippingMethod": "UPS GROUND",
"trackingNumber": "54545412165451",
"status": "OPEN",
"lineItems": [
{
"sku": "sku_1",
"quantity": 2
},
{
"sku": "sku_2",
"quantity": 3
}
]
}
]
}
}
Will return a 200 Response Status
And a Response Body of:
{
"updateResult": {
"inventoryAdjustmentCount": 2,
"inventorySetCount": 0,
"orderActivityCount": 0,
"orderUpdateCount": 1
}
}
And Order with purchaseOrderNumber of CAT333
has 1 OPEN
fulfillment with the
following line items: SKU sku-1
, Quantity 2 SKU sku-2
, Quantity 3
And the following inventory adjustments are made:
- SKU
sku-1
, Location ID 874AVAILABLE_TO_SELL
Quantity -2COMMITTED
Quantity -2
- SKU
sku-2
, Location ID 874AVAILABLE_TO_SELL
Quantity -3COMMITTED
Quantity -3
Workflow
Set the required authorization headers
Setup a flow in integration to map fulfillment order receipts to the expected JSON format. Send the JSON in the request body of a POST call to the URL you received in setup. The request body should look something like:
{
"orderUpdate": {
// id, channelOrderId, purchaseOrderNumber, or warehouse_order_id are required to match
"id": "order_id",
"channelOrderId": "channel_order_id",
"purchaseOrderNumber": "purchase_order_number",
"fulfillments": [
{
"id": "fulfillment_id", // optional
"additionalFields": [
{
"name": "warehouse_order_id", // optional as long as id, channelOrderId, or purchaseOrderNumber are included
"value": "warehouse_1"
},
{
"name": "some_extra_data", // optional
"value": "some_extra_data_value"
},
{
"name": "more_extra_data", // optional
"value": "more_extra_data"
}
],
"supplierId": "supplierIdValue", // optional
"shippingCompany": "UPS", // optional
"shippingMethod": "UPS GROUND", // optional
"trackingNumber": "54545412165451",
"trackingUrls": [
// optional
"https://example.tracking/445687484",
"https://other.tracking/4456874895355"
],
"shippedAt": "2022-08-10T19:46:05.014Z", // optional - defaults to current time
"locationId": "location_1", // maps to locationId within WAREHOUSE_LOCATION_MAP
"status": "OPEN", //Can be OPEN|SUCCESS|CANCELED|PENDING|NEW|BACKORDER by is typically OPEN for fulfillment order receipts coming back from the warehouse
"lineItems": [
{
//Supply either id or sku both are acceptable but generally stick with sku
"id": "linItem_id",
"sku": "sku_1",
"title": "sku_1 title", // optional
"additionalFields": [
// optional
{
"name": "some_extra_data",
"value": "some_extra_data_value"
}
],
"grams": 450, // optional
"price": 1500, // optional
"shippingPrice": 500, // optional
"shippingTax": 25, // optional
"quantity": 9,
"shippingMethod": "UPS GROUND", // optional
"vendor": "channel ape" // optional
}
]
}
]
}
}
The order id
, channelOrderId
, purchaseOrderNumber
, or
warehouse_order_id
(found within fulfillment[x].additionalFields
) are
required to identify the order.
For fulfillments.lineItems
an identifier to id
or sku
has to be defined
and the quantity.
locationId
should be mapped to a value within the WAREHOUSE_LOCATION_MAP
. It
will not attempt to change a fulfillment that's locationId
is not in the
current context.
Anything else on fulfillments
and its descendants can be defined and it'll be
merged in. Note that anything else on the parent order
object won't be merged
in and ignored. Don't attempt to do an order state transition or update the
order lineItems
.
If no result comes back a 204
Response Status is returned with an empty
Response Body. If something is wrong with the input a 400
Response Status is
returned with an error trying to explain what was wrong. If everything worked it
will return a 200
Response Status with the following Response Body:
{
"updateResult": {
"inventoryAdjustmentCount": 1,
"inventorySetCount": 2,
"orderActivityCount": 3,
"orderUpdateCount": 4
}
}
The numbers will reflect the amount of results updated.
Partial Fulfillments
Orders are typically processed with a single fulfillment order receipt message. If items are under-fulfilled or absent from this message, we assume they are backordered. However, certain integrations necessitate ChannelApe to receive multiple ship confirmation messages for a single order fulfillment request. If this applies, enable the "partial fulfillments" feature to indicate that multiple messages per order fulfillment are permissible.
If you enable partial fulfillments there are less protections for exceptional shipping cases like short shipments.
Config
To enable partial fulfillments just add the following to the body of the request:
{
"fulfillmentOptions": {
"partialShipments": true
},
...[rest of the request]
}
Example Workflow
Given the example order:
{
"id": "orderId_1",
"status": "IN_PROGRESS",
"purchaseOrderNumber": "purchase_1",
"fulfillments": [
{
"additionalFields": [
{
"name": "warehouse_order_id",
"value": "warehouse_1"
}
],
"trackingNumber": "3903289892389238932",
"locationId": "locationId_1",
"shippingCompany": "UPS",
"shippingMethod": "Ground",
"status": "PENDING",
"lineItems": [
{
"sku": "sku_1",
"quantity": 2
},
{
"sku": "sku_2",
"quantity": 3
}
]
}
]
}
Sending the following partial fulfillment:
{
"fulfillmentOptions": {
"partialShipments": true
},
"orderUpdate": {
"purchaseOrderNumber": "purchase_1",
"fulfillments": [
{
"additionalFields": [
{
"name": "warehouse_order_id",
"value": "warehouse_1"
}
],
"status": "OPEN",
"locationId": "locationId_1",
"lineItems": [
{
"quantity": 2,
"sku": "sku_1"
}
]
}
]
}
}
Will return the following result:
{
"updateResult": {
"inventoryAdjustmentCount": 2,
"inventorySetCount": 0,
"orderActivityCount": 0,
"orderUpdateCount": 1
}
}
The state of the order after this shipment has happened will be as follows:
{
"id": "orderId_1",
"status": "IN_PROGRESS",
"purchaseOrderNumber": "purchase_1",
"fulfillments": [
{
"additionalFields": [
{
"name": "warehouse_order_id",
"value": "warehouse_1"
}
],
"trackingNumber": "3903289892389238932",
"locationId": "locationId_1",
"shippingCompany": "UPS",
"shippingMethod": "Ground",
"status": "OPEN",
"lineItems": [
{
"sku": "sku_1",
"quantity": 2
}
]
},
{
"additionalFields": [
{
"name": "warehouse_order_id",
"value": "warehouse_1"
}
],
"trackingNumber": "3903289892389238932",
"locationId": "locationId_1",
"shippingCompany": "UPS",
"shippingMethod": "Ground",
"status": "PENDING",
"lineItems": [
{
"sku": "sku_2",
"quantity": 3
}
]
}
]
}
A second process shipment call can then be made to fulfill the remaining fulfillment.