Guides

Webhooks

Handle and register Shopify webhooks with HMAC verification.

Handling Webhooks

Use useShopifyWebhook() to validate and process incoming webhooks. HMAC verification is handled automatically:

server/api/webhooks.ts
export default defineEventHandler(async (event) => {
  const { topic, shop, payload } = await useShopifyWebhook(event)

  switch (topic) {
    case 'APP_UNINSTALLED':
      // Clean up shop data
      break
    case 'PRODUCTS_CREATE':
      // Handle new product
      break
  }

  return { success: true }
})

WebhookContext return type

PropertyTypeDescription
topicstringThe webhook topic (e.g., APP_UNINSTALLED)
shopstringThe shop domain
sessionSession | undefinedThe offline session for the shop (if available)
payloadRecord<string, any>The parsed webhook payload
apiVersionstringThe API version of the webhook

Registering Webhooks

There are two approaches to registering webhooks:

For most apps, define your webhooks in shopify.app.toml. Shopify handles registration automatically via managed install:

shopify.app.toml
[webhooks]

  [[webhooks.subscriptions]]
  topics = ["app/uninstalled"]
  uri = "/api/webhooks"

  [[webhooks.subscriptions]]
  topics = ["products/create", "products/update"]
  uri = "/api/webhooks"

Programmatically (per-shop)

Use registerShopifyWebhooks() when you need dynamic, per-shop webhook registration:

server/plugins/shopify.ts
import { configureShopify, registerShopifyWebhooks } from '#shopify/server'

export default defineNitroPlugin(() => {
  configureShopify({
    hooks: {
      afterAuth: async ({ session }) => {
        await registerShopifyWebhooks(session)
      }
    }
  })
})
For most apps, registering webhooks via shopify.app.toml is sufficient. Use registerShopifyWebhooks() only when you need dynamic, per-shop webhook registration.

Webhook Security

useShopifyWebhook() automatically handles HMAC verification using your apiSecretKey. If the HMAC is invalid, the request is rejected before your handler code runs.

Do not use any middleware that consumes the raw request body before useShopifyWebhook() — the raw body is needed for HMAC verification.
© 2026 KiriminAja. Polaris and Shopify are trademarks of Shopify Inc.

KiriminAja not affiliated with Shopify.