Webhooks Guide

Introduction

The Webhooks API allows your application to stay informed about events of interest on the Shipwire platform, in near real-time. By creating a Webhook, your application can subscribe to specific topics and tell us where to send notifications for each.

This is the list of topics you can subscribe to, and information on when the associated event gets “fired”.


Order topics

Topic Triggered When Payload Resource
order.created Sent when an order is created /api/v3/orders/:id
order.updated Sent when an order is updated /api/v3/orders/:id
order.canceled Sent when an order is canceled /api/v3/orders/:id
order.completed Sent when an order is completed /api/v3/orders/:id
order.completed.with-po-sku-source Sent when a shipping order associated with a purchase order is completed. Contains product information from purchase order. /api/v3/orders/:id
order.hold.added Sent when a hold is added to an order /api/v3/orders/:id/holds
order.hold.cleared Sent when a hold is cleared for an order /api/v3/orders/:id/holds
tracking.created Sent when tracking information is first received for an order /api/v3/orders/:id/trackings
tracking.delivered Sent when tracking information indicates the order has been delivered /api/v3/orders/:id/trackings
tracking.updated Sent when tracking information has been updated for an order /api/v3/orders/:id/trackings


Purchase Order topics

Topic Triggered When Payload Resource
purchaseorder.created Sent when a purchase order is created /api/v3/purchaseOrders/:id
purchaseorder.updated Sent when a purchase order is updated /api/v3/purchaseOrders/:id
purchaseorder.needs-approval Sent when a purchase order enters a state where it needs approval /api/v3/purchaseOrders/:id
purchaseorder.approved Sent when a purchase order is approved /api/v3/purchaseOrders/:id
purchaseorder.partially-completed Sent when a purchase order is partially completed /api/v3/purchaseOrders/:id
purchaseorder.completed Sent when a purchase order is completed /api/v3/purchaseOrders/:id
purchaseorder.canceled Sent when a purchase order is canceled /api/v3/purchaseOrders/:id


Stock topics

Topic Triggered When Payload Resource
stock.transition Sent when product stock moves from one state to another (e.g. an order moves 3 “in-stock” units to “reserved”) see below
stock.transition.good Sent when “good” product stock changes (e.g. when an order is placed or new inventory is received at a warehouse). They are a subset of stock.transition where either “toState” or “fromState” is good. see below
alert.low-stock Sent when a configured low-stock alert is triggered see below
alert Sent when a generic alert is triggered see below


Return topics

Topic Triggered When Payload Resource
return.created Sent when a return is created /api/v3/returns/:id
return.updated Sent when a return is updated /api/v3/returns/:id
return.canceled Sent when a return is canceled /api/v3/returns/:id
return.completed Sent when a return is completed /api/v3/returns/:id
return.hold.added Sent when a hold is added to a return /api/v3/returns/:id/holds
return.hold.cleared Sent when a hold is cleared for a return /api/v3/returns/:id/holds
return.tracking.created Sent when tracking information is first received for a return /api/v3/returns/:id/trackings
return.tracking.updated Sent when tracking information has been updated for a return /api/v3/returns/:id/trackings
return.tracking.delivered Sent when tracking information indicates a return has been received by the warehouse /api/v3/returns/:id/trackings


Receiving (ASN) topics

Topic Triggered When Payload Resource
receiving.created Sent when a receiving order is created /api/v3/receivings/:id
receiving.updated Sent when a receiving order is updated /api/v3/receivings/:id
receiving.canceled Sent when a receiving order is canceled /api/v3/receivings/:id
receiving.completed Sent when a receiving order is completed /api/v3/receivings/:id
receiving.hold.added Sent when a hold is added to a receiving order /api/v3/receivings/:id/holds
receiving.hold.cleared Sent when a hold is cleared for a receiving order /api/v3/receivings/:id/holds
receiving.tracking.created Sent when tracking information is first received for a receiving order /api/v3/receivings/:id/trackings
receiving.tracking.updated Sent when tracking information has been updated for a receiving order /api/v3/receivings/:id/trackings
receiving.tracking.delivered Sent when tracking information indicates a receiving order has been received by the warehouse /api/v3/receivings/:id/trackings


Product topics

Topic Triggered When Payload Resource
product.created Sent when a product order is created /api/v3/product/:id
product.updated Sent when a product is updated /api/v3/product/:id
product.retired Sent when a product is retired /api/v3/product/:id

POST bodies

Webhooks are POSTed to endpoints with a common envelope wrapping a topic-dependent resource. For most topics,
the resource, or “body”, is identical to the resource you would get from a related API endpoint. For example, on
a tracking.updated webhook, you might get:

{
"attempt": 1,
"body": {
"status": 200,
"message": "Successful",
"resourceLocation": "https://api.shipwire.com/trackings/638903765",
"resource": {
"id": 638903765,
"orderId": 186903450,
"orderExternalId": null,
"tracking": "294631149443923572",
"carrier": null,
"url": null,
"summary": "Shipment information sent to FedEx",
"summaryDate": "2015-05-05 03:17:10",
"trackedDate": "2015-05-05 03:17:10",
"deliveredDate": null
}
},
"timestamp": "2015-05-12T11:01:51-07:00",
"topic": "tracking.updated",
"uniqueEventID": "ba898330ad9b9dfd41de247c09bf7b96",
"webhookSubscriptionID": 222
}


The object above, rooted at “body”, is the same object that you would get from the order-tracking API. The exceptions to this pattern are “stock.transition”, “alert”, and “alert.low-stock”, which contain data unique to those webhook topics, and are described below:


“stock.transition” webhook body

{
"orderId": 1244,
"productId": 528,
"warehouseId": 1038,
"fromState": "pending",
"toState": "good",
"delta": 12,
"toStateStockAfterTranstion": 24,
"fromStateStockAfterTranstion":64
}


It is important to note that the toStateStockAfterTransition and fromStateStockAfterTransition fields are meant to be references, but since webhooks may send in a different order than the actual events occurred, the latest webhook you receive may not necessarily contain the most up-to-date stock values. For correct and up-to-date information, use the Stock endpoint.


“alert” and “alert.low-stock” webhook body

{
"id": "2",
"type": "backordered",
"name": "A backordered alert",
"triggeredFor": {
"warehouseId": 1038,
"productId": 528
},
"triggerCondition": {
"type": "backordered",
"value": 1104,
"threshold": 1
}
}

Webhook versions

Currently, the Webhooks API has only the “v1” version. In the future, additional Webhook API versions will be added as needed to support new capabilities and topics to subscribe to.

If you want your integration with Shipwire to be tied to a specific version of the Webhook API which has a particular payload structure and content version, you can specify the API version as a prefix to the topic.


- "v1.order.created"
- "v1.order.updated"
- "v1.order.canceled"
- "v1.order.completed"
- "v1.order.hold.added"
- "v1.order.hold.cleared"
- "v1.tracking.created"
- "v1.tracking.updated"
- "v1.tracking.delivered"
- "v1.stock.transition"
- "v1.stock.transition.good"
- "v1.alert.low-stock"
- "v1.alert"


Subscribing to topics without a prefixed version number means that your integration will use the latest API payload
version at the time of subscription. For example, if you register now for the topic order.created, your subscription
will be tied to v1. After v2 is made available, your existing subscriptions will remain with v1 and that distinction will
not change. However, if you now add new subscriptions without specifying the API version, those new subscriptions will
be tied to v2. By specifying a version, you can ensure that each of your topic subscriptions are associated with the
version of the Webhook API that you want.

Guarantees and caveats

Webhooks are currently delivered on a best-effort basis. Shipwire will typically deliver a webhook message within minutes of an event occurring and usually much sooner. The message will be sent once and only once while Shipwire and your application’s servers both remain connected and available.

Requirements for webhook endpoints

Webhook recipients are expected to conform to the following:

  • URLs must be for HTTPS hosts with valid and verifiable SSL certificates
  • Servers must reply to POST requests with a 200-level response within 5 seconds
  • Servers must reply to HEAD requests with a 200-level response within 5 seconds (HEAD requests are occasionally used to validate the endpoint, e.g. at subscription time)

When a failure occurs on a POST request to the consumer, the message will be retried. Note that the rate and number of retries is not constant.

Secrets

So that applications can validate the authenticity of events, Shipwire can optionally sign events with a shared secret. Secrets are managed through the Secrets resource. Secret keys are 64 bytes, encoded as 128-digit hexadecimal strings. You may have multiple shared secrets active at a given time, in which case Shipwire will sign events with all active secrets (see example below).

Verification

Webhooks should be verified by the consumer to confirm that information is authentic. Accounts which have at least one active API Secret will have webhook events with a signature for each valid secret as an HTTP header. Example:

 

X-Shipwire-Signature: abc123;secret-id=2
X-Shipwire-Signature: bcd345;secret-id=5

 

The hash value is the HMAC-SHA256 of the unaltered POST request body. Having multiple secrets allows you to rotate your keys without missing messages.

Signature verification code samples

Here is a sample of how you could verify your webhook secrets in PHP

 

if ($key === FALSE) {
echo "invalid key";
return;
}

$request = file_get_contents("php://input");
if (strlen($request) == 0) {
$request = file_get_contents("php://stdin");
}

echo strlen($request) . " bytes. ";
echo hash_hmac("sha256", $request, $key);

 

Here is a sample of how you could verify your webhook secrets in GO

 

package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"flag"
"fmt"
"io"
"log"
"os"
)

func main() {
flag.Parse()
if flag.NArg() < 1 {
log.Fatal("need key")
}

// decode the key from hex to binary ([]byte)
key, err := hex.DecodeString(flag.Arg(0))
if err != nil {
log.Fatal(err)
}

// compute hash
h := hmac.New(sha256.New, key)

l, err := io.Copy(h, os.Stdin)
if err != nil {
log.Fatal(err)
}

fmt.Printf("%d bytes: %x\n", l, h.Sum(nil))
}

API reference

View the webhooks API reference here

Forgot your password?