Guide to Shopify Webhooks Features and Best Practices
Shopify through webhooks provides a way for events that occur on a store to be relayed to an app as a notification. The notification Shopify sends contains data related to the event that was triggered. The webhook feature reduces the additional cost that manually polling the Shopify API for new data can bring to your systems. In this article, we will explore Shopify webhooks and discuss best practices for working with Shopify webhooks.
Shopify Webhooks Features
Features | Notes |
---|---|
Webhook Configuration | Admin API and Shopify Admin |
Hashing Algorithm | SHA-256 |
Timeout | 5 seconds |
Retry Logic | Exponential, 19 times over 48 hours |
Alert Logic | Shopify sends an alert for each failure and right before it deletes any webhooks |
Manual Retry | Not Available |
Browsable Log | Not Available |
Here are some of the essentials features to know when dealing with Shopify webhooks. If you are interested in learning more, grab a cup of coffee. Let's get started.
What To Know Working with Shopify Webhooks
Setting Up And Configuring A Webhook
To receive a notification whenever an event is triggered, we must first subscribe to a webhook event. There are several Shopify webhook events we can subscribe to, some of the most commonly used events are the order create, billing attempt, and fulfillment events. A full list of all Shopify events can be found here.
There are two ways to create a webhook subscription, either through the admin API or the Shopify Admin. We will discuss how to set up and configure a webhook using both methods below.
Configuring a webhook using the REST admin API
To configure a webhook through the REST admin API, we have to make an HTTP POST request to the /admin/api/2021-01/webhooks.json endpoint. The request body should contain a topic property which is the event that would trigger the subscription and an address property which is the endpoint that will receive the notifications sent by Shopify whenever the webhook is triggered. An optional parameter that can be specified in the request body is the format. The format field determines the format of the notification payload. Shopify supports JSON or XML formats. The request body for creating a webhook subscription that is triggered when an order is created would look similar to this:
{
"webhook": {
"topic": "orders/create",
"address": "https://whatever.hostname.com/",
"format": "json"
}
}
It is important to note that all requests to the Shopify API must be authenticated either by using Basic Authentication or Open Authorization depending on the nature of the request.
Learn how to configure Shopify webhooks using the REST API in this in-depth tutorial.
Configuring a webhook using the Shopify Admin
Configuring a webhook using the Shopify admin is quite straightforward and can be done in three steps:
- Head over to the settings in the admin dashboard and click notifications
- Scroll down to the webhooks section and click the create webhook button
- Select the event type, the format, and URL which should receive the webhook notification.
Shopify webhook subscriptions are app-scoped, webhook subscriptions created using the Shopify Admin are associated solely to the shop in question and would not be returned from calls to the API.
Follow a step by step tutorial on how to create your Shopify webhooks with Shopify Admin dashboard here.
Webhooks HTTP Header
Shopify payloads typically contain an Id field and name field. The rest of the payload content varies depending on the type of event being triggered. Alongside the payload, another very important component of the Shopify webhook notifications are the HTTP headers. Webhooks are HTTP based and often come with headers that provide more information about the event that triggered the webhook notification. Shopify webhook notifications contain the following headers:
- X-Shopify-Topic — This header specifies the event/topic that triggered the webhook notification.
- X-Shopify-API-Version — As the name implies, this header is used to indicate which version of the Shopify Admin API was used to generate the payload.
- X-Shopify-Webhook-Id — This is the unique ID of the webhook. This header is important when debugging a failed webhook.
- X-Shopify-Shop-Domain — ****This header contains the store associated with the webhook notification. It is usually a Shopify domain. The value for this header might look similar to toby.myshopify.com
- X-Shopify-Hmac-Sha256 — The Hmac header is a base64 encoded string that is used to verify a webhook notification.
Webhooks Retry Logic
The webhook endpoint under ideal circumstances should send a response to the webhook provider. Shopify treats 2xx responses as successful notifications. Any response to a Shopify webhook with any other status code is considered to be a failure. Shopify has a retry mechanism in place for failed webhook notifications. If there is no response after waiting for five seconds, Shopify retries the connection 19 times over the next 48 hours. At the end of the retries, if there is still no response then Shopify deletes the webhook. The retry mechanism is a very important feature as it helps to keep track of webhook notifications and deal with possible missed notifications. In this section, we explored the basics of Shopify webhooks and the components of a Shopify webhook notification. In the next section, we will look at best practices for working with Shopify webhooks.
Best Practices for Working with Shopify Webhooks
Read the revised and extended article for Shopify webooks best practices here
Retrieve Lost Webhooks
Factoring in possible failures and how to handle them when designing a system is considered a good practice and would save you a lot of possible stress. What can you do if the app receiving your webhook notification goes offline past the duration of Shopify retries logic and your webhooks are deleted? To recover a webhook, you have to first re-register the webhook. Next, you have to import by querying the original resources and deduplicating all the notifications that were not delivered during the outage period and then add them to your webhook processing code. To avoid missing webhooks, Shopify recommends you set up a Reconciliation Job that will periodically query the Shopify API for data. The reconciliation job you set up should use the created and updated filter parameters to fetch data that was created since the last time the job was run.
Delayed Processing Of Webhooks
If an incoming webhook notification is not responded to within Shopify's five-second window, it times out. Responding to Shopify webhooks quickly is important, you should defer processing of the webhooks till a response is sent to the webhook provider. Queues can come in handy when deferring the processing of webhooks. The usage of queues decouples the webhook provider(Shopify) and the consumer(the server which receives the webhook notifications). This decoupling allows for the webhooks to be processed after the webhook notification sent from Shopify has been acknowledged. When working with a large volume of webhook notifications, Shopify recommends you use Amazon EventBridge or a similar solution, we personally would recommend using Hookdeck, to receive webhook notifications for your shop.
Incoming Webhooks Verification
Webhook notifications are sent to an endpoint you provide when creating the webhook subscription. It is important to note that Shopify only accepts SSL, non-Shopify domains as the webhook endpoint. The endpoint which receives the webhook notification should verify the payload. Shopify webhooks can be verified by comparing the value of the X-Shopify-Hmac-Sha256 header with a computed HMAC made up of a secret key and the content of your webhook payload. In generating a Hash-based message authentication code (HMAC), you are required to use your payload content along with the secret key that Shopify provides you when you create a webhook subscription. If the X-Shopify-Hmac-Sha256 header and your computed match, then you can be certain the notification was originally sent from Shopify.
Example of adding webhook verification for Shopify in Javascript
To get started with adding webhook verification to the server, run the following command:
$ npm install raw-body
The raw-body
module we just installed would help us parse our incoming request body in a way that can be used to generate the hash. The default body-parser module that comes with Express could also be used in place of the raw-body
package.
Next, Import the raw-body
& crypto
packages into the project by adding the following lines of code:
const getRawBody = require("raw-body");
const crypto = require("crypto");
The crypto module would be used to generate the hash from our request body and the secret key provided to us by Shopify.
Replace the app.post
request handler created above with this
app.post("/webhook", async (req, res) => {
//Extract X-Shopify-Hmac-Sha256 Header from the request
const hmacHeader = req.get("X-Shopify-Hmac-Sha256");
//Parse the request Body
const body = await getRawBody(req);
//Create a hash based on the parsed body
const hash = crypto
.createHmac("sha256", secret)
.update(body, "utf8", "hex")
.digest("base64");
// Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header
if (hash === hmacHeader) {
console.log("Notification Requested from Shopify received");
res.sendStatus(200);
} else {
console.log("There is something wrong with this webhook");
res.sendStatus(403);
}
});
In the code above, we extract the X-Shopify-Hmac-SHA256 HTTP header from the request, create a hash based on the Hmac-SHA256 algorithm from the request body then compare both hashes.
Lastly, go ahead and create a constant called secret
which would hold the value of the secret Shopify returned to you when created a new webhook connection. You would want to store that as an environmental variable to ensure it is safe.
Dealing with Idempotency
Receiving the same webhooks multiple times is a possible occurrence. While some type of work is by nature idempotent (ie. updating a product inventory) some other is not (ie. creating a new order). Using the X-Shopify-Webhook-Id, the unique ID of the webhook, is good way to validate if the webhook was already processed. One common approach is to store in a ACID compliant database the X-Shopify-Webhook-Id using a unique field along with a status such as processed
, failed
or pending
and update that status to reflect the state of your system. Generally speaking you will want to ignore webhooks already indexed with a status of processed
or pending
.
Conclusion
So far, we have discussed about Shopify webhooks and its best practices in depth. Webhooks are interesting and I hope this article helps you understand and get started on Shopify webhooks.