Shopify Integration
Receive and route Shopify webhooks for orders, products, customers, and more.
Setup
1. Create a Source in Hookbase
bash
curl -X POST https://api.hookbase.app/api/sources \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Shopify Store",
"slug": "shopify",
"provider": "shopify",
"verificationConfig": {
"type": "shopify",
"secret": "your-shopify-api-secret"
}
}'Save your webhook URL:
https://api.hookbase.app/ingest/{orgSlug}/shopify2. Configure Shopify Webhooks
Via Shopify Admin
- Go to Settings → Notifications → Webhooks
- Click Create webhook
- Select the event (e.g., Order creation)
- Set the URL to your Hookbase webhook URL
- Select JSON format
- Click Save
Via Shopify API
bash
curl -X POST https://your-store.myshopify.com/admin/api/2024-01/webhooks.json \
-H "X-Shopify-Access-Token: YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"topic": "orders/create",
"address": "https://api.hookbase.app/ingest/{orgSlug}/shopify",
"format": "json"
}
}'3. Create Destinations and Routes
bash
# Create a destination
curl -X POST https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Order Service", "url": "https://myapp.com/webhooks/shopify"}'
# Create a route
curl -X POST https://api.hookbase.app/api/routes \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Shopify Orders", "sourceId": "src_...", "destinationIds": ["dst_..."]}'Signature Verification
Shopify signs webhooks with HMAC-SHA256 using your API secret. The signature is sent in the X-Shopify-Hmac-Sha256 header as a base64-encoded string.
Hookbase automatically verifies this signature when you configure:
json
{
"verificationConfig": {
"type": "shopify",
"secret": "your-shopify-api-secret"
}
}TIP
The secret is your Shopify app's API secret key, found in your app's settings under API credentials.
Common Events
Order Created
Topic: orders/create
json
{
"id": 820982911946154508,
"name": "#1001",
"email": "[email protected]",
"total_price": "59.99",
"currency": "USD",
"financial_status": "paid",
"fulfillment_status": null,
"line_items": [
{
"id": 866550311766439020,
"title": "T-Shirt",
"quantity": 1,
"price": "59.99",
"sku": "TS-001"
}
],
"shipping_address": {
"city": "New York",
"country": "US"
},
"created_at": "2024-01-15T10:30:00-05:00"
}Order Paid
Topic: orders/paid
Triggered when payment is captured for an order.
Product Updated
Topic: products/update
json
{
"id": 632910392,
"title": "Fancy T-Shirt",
"vendor": "My Store",
"product_type": "Clothing",
"variants": [
{
"id": 39072856,
"title": "Small",
"price": "59.99",
"inventory_quantity": 25
}
],
"updated_at": "2024-01-15T10:30:00-05:00"
}Customer Created
Topic: customers/create
json
{
"id": 207119551,
"email": "[email protected]",
"first_name": "John",
"last_name": "Doe",
"orders_count": 0,
"total_spent": "0.00",
"created_at": "2024-01-15T10:30:00-05:00"
}Shopify Webhook Topics
| Category | Topics |
|---|---|
| Orders | orders/create, orders/updated, orders/paid, orders/cancelled, orders/fulfilled, orders/partially_fulfilled |
| Products | products/create, products/update, products/delete |
| Customers | customers/create, customers/update, customers/delete |
| Carts | carts/create, carts/update |
| Checkouts | checkouts/create, checkouts/update, checkouts/delete |
| Refunds | refunds/create |
| Inventory | inventory_levels/connect, inventory_levels/update, inventory_levels/disconnect |
| Fulfillments | fulfillments/create, fulfillments/update |
| App | app/uninstalled |
Transform Examples
Slack Notification for Orders
javascript
function transform(payload) {
const items = (payload.line_items || [])
.map(i => `• ${i.quantity}x ${i.title} ($${i.price})`)
.join('\n');
return {
blocks: [
{
type: "header",
text: {
type: "plain_text",
text: `New Order ${payload.name}`
}
},
{
type: "section",
fields: [
{ type: "mrkdwn", text: `*Customer:*\n${payload.email}` },
{ type: "mrkdwn", text: `*Total:*\n$${payload.total_price} ${payload.currency}` }
]
},
{
type: "section",
text: {
type: "mrkdwn",
text: `*Items:*\n${items}`
}
}
]
};
}Normalize for Internal API
javascript
function transform(payload) {
return {
orderId: String(payload.id),
orderNumber: payload.name,
customer: {
email: payload.email,
name: `${payload.customer?.first_name || ''} ${payload.customer?.last_name || ''}`.trim()
},
total: parseFloat(payload.total_price),
currency: payload.currency,
items: (payload.line_items || []).map(item => ({
sku: item.sku,
name: item.title,
quantity: item.quantity,
price: parseFloat(item.price)
})),
status: payload.financial_status,
createdAt: payload.created_at
};
}Filter Examples
High-Value Orders Only
json
{
"name": "High-Value Orders",
"conditions": [
{
"field": "payload.total_price",
"operator": "gt",
"value": "100"
}
]
}Specific Product Types
json
{
"name": "Clothing Products",
"conditions": [
{
"field": "payload.product_type",
"operator": "equals",
"value": "Clothing"
}
]
}Headers
Shopify sends these headers with webhooks:
| Header | Description |
|---|---|
X-Shopify-Topic | Webhook topic (e.g., orders/create) |
X-Shopify-Hmac-Sha256 | HMAC-SHA256 signature (base64) |
X-Shopify-Shop-Domain | Store domain |
X-Shopify-Webhook-Id | Unique webhook delivery ID |
X-Shopify-API-Version | API version |
Troubleshooting
Webhook Not Triggering
- Check Settings → Notifications → Webhooks for delivery status
- Verify the URL is accessible from the internet
- Ensure the webhook topic is correct
Signature Verification Failed
- Use the API secret key (not the API key or access token)
- Ensure the secret matches your Shopify app credentials
- Check for encoding issues (the signature is base64)
Duplicate Events
Enable deduplication using the X-Shopify-Webhook-Id header:
bash
curl -X PATCH .../sources/src_shopify \
-d '{"dedupEnabled": true, "dedupStrategy": "provider_id"}'Best Practices
- Verify signatures: Always configure signature verification for security
- Use deduplication: Shopify may retry webhooks — enable dedup to prevent duplicates
- Subscribe selectively: Only subscribe to events you need
- Handle idempotently: Design your handlers to safely process the same event twice
- Monitor
app/uninstalled: Handle app uninstallation gracefully