Webhooks

There is a built method to listen for webhooks sent by Shopify back to your application. These are validated based on a secret you received when setting them up.

Registering Webhooks

The easiest way to register all webhooks is to run the built-in Artisan command. It will query your Shopify store, skip any that are already registered, and create any that are missing.

php artisan shopify:webhooks:register

In multi-store mode you can target a specific store:

php artisan shopify:webhooks:register --store=uk

If --store is omitted in multi-store mode, the command iterates all configured stores.

The command outputs a table showing the topic, callback URL, and whether each webhook was already registered, newly registered, or failed.

The command requires your Shopify credentials to be configured and your application's APP_URL to be set correctly, so that the callback URLs it generates are publicly accessible.

Via the Shopify Admin

You can also register webhooks manually:

  1. Head to your Shopify admin
  2. Click Settings > Notifications
  3. Scroll down to Webhooks and click Create Webhook
  4. Enter the URL as shown in the sections below.
  5. Leave the type as JSON and on the latest API.
  6. If this is your first webhook, you will be provided a secret which you should add to your .env as SHOPIFY_WEBHOOK_SECRET

If you are trying to test any webhooks locally you'll need to use a service like Ngrok to forward your localhost to a secure endpoint.

Webhook Status

The Shopify Control Panel dashboard includes a Webhook Status card that queries your Shopify store and shows which webhooks are registered, whether their callback URLs match what the addon expects, and which topics are missing.

For any missing topics, run php artisan shopify:webhooks:register to create them automatically.

Webhook Endpoints

The following endpoints are registered by the addon. All webhook URLs follow the pattern:

https://YOURSITE/!/shopify/webhook/{resource}/{action}
TopicURL
Collection Create/!/shopify/webhook/collection/create
Collection Update/!/shopify/webhook/collection/update
Collection Delete/!/shopify/webhook/collection/delete
Product Create/!/shopify/webhook/product/create
Product Update/!/shopify/webhook/product/update
Product Delete/!/shopify/webhook/product/delete
Customer Create/!/shopify/webhook/customer/create
Customer Update/!/shopify/webhook/customer/update
Customer Delete/!/shopify/webhook/customer/delete
Order Created/!/shopify/webhook/order

Shopify will send the product update webhook on creation too, so a separate product/create webhook is optional for most setups.

The Order Created webhook scans all line_items and re-fetches the product data for each.

Multi-Store

In multi-store mode, the same webhook URLs are used for all stores. Configure a separate set of webhooks in each Shopify admin pointing to the same Statamic endpoints.

Shopify always sends an X-Shopify-Shop-Domain header with every webhook request. The addon uses this header to:

  1. Identify which store the webhook came from (matched against each store's url in config).
  2. Verify the HMAC signature using that store's webhook_secret.
  3. Pass the resolved store handle to any import jobs dispatched.
Webhook requests from domains not listed in `multi_store.stores` are rejected with a `403` response.

Each store's webhook secret must be set individually. See Env Values for the per-store env var pattern (SHOPIFY_STORE_{HANDLE}_WEBHOOK_SECRET).

Events

Each webhook listener also fires an event you can use to hook into with your own logic based on the payload received.

The available events are:

StatamicRadPack\Shopify\Events\CollectionCreate
StatamicRadPack\Shopify\Events\CollectionDelete
StatamicRadPack\Shopify\Events\CollectionUpdate
StatamicRadPack\Shopify\Events\CustomerCreate
StatamicRadPack\Shopify\Events\CustomerDelete
StatamicRadPack\Shopify\Events\CustomerUpdate
StatamicRadPack\Shopify\Events\ProductCreate
StatamicRadPack\Shopify\Events\ProductDelete
StatamicRadPack\Shopify\Events\ProductUpdate
StatamicRadPack\Shopify\Events\OrderCreate

Each event has one property $data with the payload data decoded to a stdClass.

For import job failures, see ProductImportFailed documented in Importing Data.

Edit this page on GitHub Updated at Mon, Mar 16, 2026