home / skills / toilahuongg / shopify-agents-kit / shopify-webhooks
This skill guides you through configuring, verifying, and handling Shopify webhooks to keep data in sync securely.
npx playbooks add skill toilahuongg/shopify-agents-kit --skill shopify-webhooksReview the files below or copy the command above to add this skill to your agents.
---
name: shopify-webhooks
description: Guide for handling Shopify Webhooks, including configuration, verification, and processing. Use this skill when the user needs to set up webhook subscriptions, verify authentic requests, or handle event payloads.
---
# Shopify Webhooks Skill
Webhooks are the preferred way to stay in sync with Shopify data. They allow your app to receive real-time notifications when events occur in a shop (e.g., `orders/create`, `app/uninstalled`).
## 1. Verification (CRITICAL)
**ALL** webhook requests must be verified to ensure they came from Shopify.
### HMAC Verification
Shopify includes an `X-Shopify-Hmac-Sha256` header in every webhook request. This is a base64-encoded HMAC-SHA256 digest of the request body, using your **Client Secret** (API Secret Key) as the signing key.
> [!IMPORTANT]
> Always use the **raw request body** (Buffer) for verification. Parsed JSON bodies may have subtle differences that cause verification to fail.
#### Node.js Example (Generic)
```javascript
const crypto = require('crypto');
function verifyWebhook(rawBody, hmacHeader, apiSecret) {
const digest = crypto
.createHmac('sha256', apiSecret)
.update(rawBody, 'utf8')
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(digest),
Buffer.from(hmacHeader)
);
}
```
#### Remix / Shopify App Template (Recommended)
If using `@shopify/shopify-app-remix`, verification is handled automatically by the `authenticate.webhook` helper.
```typescript
/* app/routes/webhooks.tsx */
import { authenticate } from "../shopify.server";
export const action = async ({ request }) => {
const { topic, shop, session, admin, payload } = await authenticate.webhook(request);
if (!admin) {
// The webhook request was not valid.
return new Response();
}
switch (topic) {
case "APP_UNINSTALLED":
if (session) {
await db.session.deleteMany({ where: { shop } });
}
break;
case "ORDERS_CREATE":
console.log(`Order created: ${payload.id}`);
break;
}
return new Response();
};
```
## 2. Registration
### App-specific Webhooks (Recommended)
These are configured in `shopify.app.toml`. They are automatically registered when the app is deployed and are easier to manage. Best for topics that apply to the app in general (e.g., `app/uninstalled`).
```toml
[webhooks]
api_version = "2025-10"
[[webhooks.subscriptions]]
topics = [ "app/uninstalled", "orders/create" ]
uri = "/webhooks"
```
### Shop-specific Webhooks (Advanced)
Use `webhookSubscriptionCreate` via the Admin API. Best for:
- Per-shop customization.
- Topics not supported by config.
- Dynamic runtime registration.
```graphql
mutation webhookSubscriptionCreate($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) {
userErrors {
field
message
}
webhookSubscription {
id
}
}
}
```
## 3. Mandatory Compliance (GDPR)
Public apps **MUST** implement specific webhooks to comply with privacy laws. These are configured in the **Partner Dashboard** (App > Configuration > Privacy compliance), NOT in `shopify.app.toml` or via API.
- `customers/data_request`: Request to export customer data.
- `customers/redact`: Request to delete customer data.
- `shop/redact`: Request to delete shop data (48 hours after uninstall).
> [!WARNING]
> Failure to handle these can result in app removal.
## 4. Best Practices
- **Idempotency**: Webhooks can be delivered multiple times. Ensure your processing logic is idempotent (e.g., check if an order has already been processed before taking action).
- **Response Time**: Respond with a `200 OK` **immediately** (within 5 seconds). Perform long-running tasks asynchronously (e.g., using a background queue).
- **App Uninstalled**: ALWAYS handle `app/uninstalled` to clean up shop data and cancel subscriptions. This webhook acts as the "offboarding" signal.
- **App Uninstalled**: ALWAYS handle `app/uninstalled` to clean up shop data and cancel subscriptions. This webhook acts as the "offboarding" signal.
## Common Topics
- `APP_UNINSTALLED`: App removed. Cleanup required.
- `ORDERS_CREATE`: New order placed.
- `ORDERS_PAID`: Order payment status changed to paid.
- `PRODUCTS_UPDATE`: Product details changed.
This skill guides you through configuring, verifying, and processing Shopify webhooks so your app stays in sync with store events. It covers secure HMAC verification, registration options (app-configured and shop-specific), mandatory GDPR webhooks, and practical processing guidelines. Use it to implement reliable, compliant webhook handling for real-time Shopify notifications.
The skill explains how to verify incoming webhook requests using the X-Shopify-Hmac-Sha256 header and your API secret, emphasizing use of the raw request body for HMAC computation. It shows automatic verification options for common frameworks and how to register subscriptions either via a deployment config (recommended) or the Admin API for per-shop cases. It also describes required GDPR webhooks and how to respond quickly while delegating long work to background jobs.
What header and algorithm are used to verify Shopify webhooks?
Shopify sends X-Shopify-Hmac-Sha256 which is a base64 HMAC-SHA256 digest of the raw request body signed with your API secret.
Should I parse the request body before verification?
No. Always use the raw request body (bytes/Buffer) for HMAC verification; parsing can alter formatting and break the signature check.