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
| Property | Type | Description |
|---|---|---|
topic | string | The webhook topic (e.g., APP_UNINSTALLED) |
shop | string | The shop domain |
session | Session | undefined | The offline session for the shop (if available) |
payload | Record<string, any> | The parsed webhook payload |
apiVersion | string | The API version of the webhook |
Registering Webhooks
There are two approaches to registering webhooks:
Via shopify.app.toml (recommended)
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.