Shopify Webhook API Tutorial: How To Delete Data on Uninstall

Shopify Webhook API Tutorial: How To Delete Data on Uninstall

When you develop a Shopify app, you are required to store merchants’ data such as access tokens and shop URLs. These two data are one of the most important data for using the API.

The takeaway with this is that- you are responsible for deleting their data as soon as they uninstall the app.

So, how can you do that?

It’s simple. Just use webhooks.

Video Tutorial

If you prefer watching video tutorials, feel free to watch the video version of this article:

What is Webhook?

Webhook is a method of sending notifications about particular events that have happened in a shop.

For example, when a merchant uninstalls an app, Shopify will send a notification to the developer that a merchant has uninstalled the app. This notification will have a set of data that the developer can use to determine which store was the one who uninstalled the app.

Webhooks are also available for the following events/topics:

  • Cart
  • Collection
  • Checkout
  • Collection Publication
  • Customer
  • Customer Saved Search
  • Draft Order
  • Fulfillment
  • Fulfillment Event
  • Inventory Item
  • Inventory Level
  • Location
  • Order
  • Order Transaction
  • Product
  • Product Listing
  • Refund
  • Shop
  • Shop Alternate Locale
  • Tender Transaction
  • Theme

If you want to learn more about each of the events listed above, you may visit Shopify webhook documentation.

For this tutorial, we’ll be focusing on how to implement webhook API for the event/topic: app/uninstalled.

We’ll assume once again that you have already set up your Shopify app and connected it with the database because we’re going to delete the stored data as soon as we uninstall the app.

Read more: How to connect Shopify app to MySQL database

We’ll also assume that you are using the same script that we’re using for this tutorial. So if you’re not familiar with everything we used in this tutorial, we suggest you follow the tutorial where we teach you how to create a Shopify app from scratch.

We’re going to be editing generate_token.php or token_generator.php for this tutorial. So make sure you have that file ready.

Implementing Webhooks

Before you can make a post request to webhooks API, you will need to create a payload that contains the topic and the target URL that will load once the event is fired.

Of course, for this tutorial, we’ll be using PHP to implement webhook API although using a different programming language will also have the same process.

Now, let’s open our token generator script.

For the moment, we have the following inside our code.

// Get our helper functions

// Set variables for our request
$api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //Replace with your API KEY
$shared_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //Replace with your Shared secret key
$params = $_GET; // Retrieve all request parameters
$hmac = $_GET['hmac']; // Retrieve HMAC request parameter

$shop_url = $params['shop'];

$params = array_diff_key($params, array('hmac' => '')); // Remove hmac from params
ksort($params); // Sort params lexographically

$computed_hmac = hash_hmac('sha256', http_build_query($params), $shared_secret);

// Use hmac data to check that the response is from Shopify or not
if (hash_equals($hmac, $computed_hmac)) {

	// Set variables for our request
	$query = array(
		"client_id" => $api_key, // Your API key
		"client_secret" => $shared_secret, // Your app credentials (secret key)
		"code" => $params['code'] // Grab the access key from the URL

	// Generate access token URL
	$access_token_url = "https://" . $params['shop'] . "/admin/oauth/access_token";

	// Configure curl client and execute request
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_URL, $access_token_url);
	curl_setopt($ch, CURLOPT_POST, count($query));
	curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($query));
	$result = curl_exec($ch);

	// Store the access token
	$result = json_decode($result, true);
	$access_token = $result['access_token'];

	// Show the access token (don't do this in production!)
	//echo $access_token;

	$sql = "INSERT INTO example_table (store_url, access_token, install_date) VALUE ('" . $shop_url . "', '" . $access_token . "', NOW())";

	if( mysqli_query( $conn, $sql ) ) {
		header('Location: https://' . $shop_url . '/admin/apps');
	} else {
		echo "Error installation: " . mysqli_error( $conn );

} else {
	// Someone is trying to be shady!
	die('This request is NOT from Shopify!');
Code language: PHP (php)

As you can see in the code above, we have already implemented MySQL to save the merchant’s data.

However, we’re not just going to save the merchant’s data but we’re also going to register webhook on app uninstall.

Also, make sure you reference your functions.php file since we’re going to do some API calls.

Now let’s talk about the payload. Let’s assume we have the following:

$array = array(
	'webhook' => array(
		'topic' => 'app/uninstalled', 
		'format' => 'json'
Code language: PHP (php)

Copy the code above just before the header() function.

Since we’re going to delete the merchant’s data when the app/uninstalled event is fired, that means we’re going to create a PHP file that uses MySQL functions.

In your project folder, create a new folder and name it webhooks.

Open the webhooks folder and inside, create a new file and name it delete.php.

Now, let’s update our payload.

$array = array(
	'webhook' => array(
		'topic' => 'app/uninstalled', 
		'address' => '' . $shop_url,
		'format' => 'json'
Code language: PHP (php)

Next, let’s get the subdomain of the store because we need that for the API function.

Copy the following code just underneath the code above.

$parsedUrl = parse_url('https://' . $shop_url );
$host = explode('.', $parsedUrl['host']);
$subdomain = $host[0];

$shop = $subdomain;
Code language: PHP (php)

Next, let’s create the POST request for webhook API.

Again, copy the following code just underneath the code above.

$webhook = shopify_call($access_token, $shop, "/admin/api/2020-07/webhooks.json", $array, 'POST');
$webhook = json_decode($webhook['response'], JSON_PRETTY_PRINT);Code language: PHP (php)

Save your file and let’s open our store.

This time, we’re not going to open our app but uninstall the app.

Deleting or Uninstalling Shopify app

Keep in mind though that the webhook is not yet registered which means we have to reinstall our app to register it.

Installing unlisted private shopify app

Install your app.

Checking Applied Webhooks

If you are wondering how you can check the list of webhooks applied to your store, you have to do it by sending a GET request instead of just browsing the webhooks.json file.

Let’s open our index.php file and update our code to the following.


$requests = $_GET;
$hmac = $_GET['hmac'];
$serializeArray = serialize($requests);
$requests = array_diff_key($requests, array( 'hmac' => '' ));

$sql = "SELECT * FROM example_table WHERE store_url='" . $requests['shop'] . "' LIMIT 1";
$result = mysqli_query( $conn, $sql );
$row = mysqli_fetch_assoc($result);

$token = $row['access_token'];

$url = parse_url( 'https://' . $row['store_url'] );
$host = explode('.', $url['host'] );
$shop  = $host[0];

$webhook = shopify_call($token, $shop, "/admin/api/2019-10/webhooks.json", array(), 'GET');
$webhook = json_decode($webhook['response'], JSON_PRETTY_PRINT);

echo print_r( $webhook );

<!DOCTYPE html>
	<title>Shopify Example App</title>

Code language: PHP (php)

Save your file and open your app.

You should see the following JSON response.

Displaying list of webhooks in Shopify API PHP

There you have it! You have successfully applied a webhook for the event app/uninstalled!

Now, the next thing we need to do is to actually create the delete.php file and use MySQL functions to delete the store data from the database.

So let’s open our delete.php file and add the following code:


define('SHOPIFY_APP_SECRET', 'xxxxxxxxxxxxxx'); // Replace with your SECRET KEY

function verify_webhook($data, $hmac_header)
  $calculated_hmac = base64_encode(hash_hmac('sha256', $data, SHOPIFY_APP_SECRET, true));
  return hash_equals($hmac_header, $calculated_hmac);

$res = '';
$hmac_header = $_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256'];
$topic_header = $_SERVER['HTTP_X_SHOPIFY_TOPIC'];
$data = file_get_contents('php://input');
$decoded_data = json_decode($data, true);

$verified = verify_webhook($data, $hmac_header);

if( $verified == true ) {
  if( $topic_header == 'app/uninstalled' || $topic_header == 'shop/update') {
    if( $topic_header == 'app/uninstalled' ) {

      $sql = "DELETE FROM example_table WHERE store_url='".$shop_header."' LIMIT 1";
      $result = mysqli_query($conn, $sql);

      $response->shop_domain = $decoded_data['shop_domain'];

      $res = $decoded_data['shop_domain'] . ' is successfully deleted from the database';
    } else {
      $res = $data;
} else {
  $res = 'The request is not from Shopify';

error_log('Response: '. $res); //check error.log to see the result


Code language: PHP (php)

Save the file and open your store.

As soon as you uninstall your app. Proceed to your webhooks folder and you should see a file called error_log. This file was generated by the delete.php file.

Open error_log file with your code editor. You should see the following if the process is successful.

Getting response from Webhook API in Shopify PHP

If you see the same response then that means your store data is also deleted from the database.

Why add the webhook in the token generator? Instead of the index?

The reason why we have to register the webhook in token generator is because, merchants can always change their minds and uninstall the app without opening your app.


Webhooks in Shopify is very important, in fact, Shopify is encouraging you to implement this before you plan to publish your app to the app store.

If you have questions, don’t hesitate to comment down below. Let us know what you think by sending us your reaction!


  1. I see, but sometimes cache keeps old files and I want to remove them,
    using DELETE – it wont delete , I reied to pass id of asset but maybe I made it wrong, pls can you help how to pass params in this case ?
    shopify_call($token, $shop, “/admin/api/2019-07/script_tags.json”, array(), ‘DELETE’);

  2. How many times does the file delete.php run? because I did everything but there comes a time when after deleting the application runs up to 4 times the file delete.php

    Is it normal for him to do that?

    P.S. English is not my native language

  3. How secure is the call of delete.php? The app is within an iframe but what if an hacker calls the url outside the iframe only with the shop paramter? Will the entry in the database deleted?

  4. I have a problem with the calculated hmac, I delete from the application panel, and the hmac header and the calculated one are different, I have used the same code but I still get the error.

Leave a Reply