Guide to WooCommerce Webhooks Features and Best Practices
WooCommerce powers over 5 million online stores worldwide, making it the most popular ecommerce platform built on WordPress. At the heart of WooCommerce's integration capabilities lies its webhook system. It's a mechanism that enables real-time communication between your store and external applications.
This guide covers everything you need to know about WooCommerce webhooks: their features, how to configure them, best practices for production deployments, and the common pain points developers face along with solutions to address them.
What are WooCommerce webhooks?
A webhook is an event notification sent to a URL of your choice whenever specific events occur in your WooCommerce store. Instead of continuously polling the WooCommerce API to check for new orders, product updates, or customer changes, webhooks push this information to your application the moment it happens.
Webhooks were introduced in WooCommerce 2.2 and have since become essential for integrating WooCommerce with CRM systems, inventory management platforms, fulfilment services, analytics tools, and custom applications.
WooCommerce webhook features
| Feature | Details |
|---|---|
| Webhook configuration | WooCommerce admin dashboard or REST API |
| Hashing algorithm | SHA256 HMAC |
| Timeout | Varies based on server configuration |
| Retry logic | Automatic retries, webhook disabled after 5 failures |
| Alert logic | Not available |
| Manual retry | Not available |
| Browsable log | Available at WooCommerce > Status > Logs |
Supported event topics
WooCommerce provides 15 pre-defined webhook topics across four main resource types:
Order Events
order.created- Triggered when a new order is placedorder.updated- Triggered when any order data changesorder.deleted- Triggered when an order is trashedorder.restored- Triggered when an order is restored from trash
Product Events
product.created- Triggered when a new product is addedproduct.updated- Triggered when product data changesproduct.deleted- Triggered when a product is trashedproduct.restored- Triggered when a product is restored
Customer Events
customer.created- Triggered when a new customer registerscustomer.updated- Triggered when customer data changescustomer.deleted- Triggered when a customer is deleted
Coupon Events
coupon.created- Triggered when a new coupon is createdcoupon.updated- Triggered when coupon data changescoupon.deleted- Triggered when a coupon is trashedcoupon.restored- Triggered when a coupon is restored
Custom action topics
Beyond the pre-defined topics, WooCommerce supports custom action topics that map to any WordPress/WooCommerce action hook. For example, you can create a webhook with the topic action.woocommerce_add_to_cart that fires every time a product is added to the cart.
Custom topics pass the first hook argument to the payload, enabling webhooks for virtually any event in the WooCommerce ecosystem.
Webhook payload structure
WooCommerce webhook payloads are JSON-encoded and mirror the REST API response for the corresponding resource. An order webhook payload includes:
{
"id": 1234,
"parent_id": 0,
"status": "processing",
"currency": "USD",
"date_created": "2025-01-21T10:30:00",
"date_modified": "2025-01-21T10:30:00",
"discount_total": "0.00",
"shipping_total": "10.00",
"total": "99.99",
"total_tax": "8.50",
"customer_id": 5,
"billing": {
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
},
"shipping": { ... },
"line_items": [ ... ],
"meta_data": [ ... ]
}
This consistency with the REST API means you can reuse data models and parsing logic across both webhook handlers and direct API integrations.
Security with HMAC signatures
Every WooCommerce webhook request includes an HMAC-SHA256 signature in the X-WC-Webhook-Signature header. This signature is generated using a secret key you configure when creating the webhook, allowing your receiving endpoint to verify that requests genuinely originated from your WooCommerce store.
The signature is calculated over the JSON-encoded payload body, then base64-encoded before being included in the header.
Webhook request headers
WooCommerce includes several custom headers with each webhook delivery:
| Header | Description | Example |
|---|---|---|
X-WC-Webhook-Topic | The event topic | order.created |
X-WC-Webhook-Resource | The resource type | order |
X-WC-Webhook-Event | The event action | created |
X-WC-Webhook-Signature | HMAC-SHA256 signature | base64-encoded-hash |
X-WC-Webhook-ID | The webhook's internal ID | 15 |
X-WC-Webhook-Delivery-ID | Unique delivery identifier | 42 |
These headers enable your endpoint to route, filter, and validate incoming webhooks efficiently.
Webhook API versions
When configuring webhooks, you can choose from multiple API versions that determine the payload structure:
| Version | Description |
|---|---|
| WP REST API Integration v3 | Current recommended version. Provides the most complete data structure. |
| WP REST API Integration v2 | Legacy version with slightly different field names. |
| WP REST API Integration v1 | Oldest version, may lack newer fields. |
| Legacy API v3 | Pre-REST API format, deprecated but still available. |
Unless you have specific compatibility requirements with an existing integration, use WP REST API Integration v3 for all new webhooks.
Setting up WooCommerce webhooks
Via the admin dashboard
Navigate to WooCommerce > Settings > Advanced > Webhooks
Click Add webhook
Configure the webhook:
- Name: A descriptive identifier (e.g., "Order sync to fulfilment")
- Status: Set to "Active" to enable delivery
- Topic: Select from pre-defined topics or enter a custom action
- Delivery URL: Your HTTPS endpoint that will receive the webhook
- Secret: A secure key for HMAC signature verification
- API Version: Select "WP REST API Integration v3" unless you have specific requirements
Click Save webhook
When you first save a webhook with "Active" status, WooCommerce sends a ping request to verify your endpoint is reachable.
Via the REST API
You can also create and manage webhooks programmatically using the WooCommerce REST API:
curl -X POST https://yourstore.com/wp-json/wc/v3/webhooks \
-u consumer_key:consumer_secret \
-H "Content-Type: application/json" \
-d '{
"name": "Order created webhook",
"topic": "order.created",
"delivery_url": "https://your-endpoint.com/webhooks/woocommerce",
"secret": "your-webhook-secret"
}'
This approach is essential for apps that need to register webhooks during installation or manage them dynamically.
Best practices when working with WooCommerce webhooks
Verifying webhook signatures
Never process a webhook without first verifying its HMAC signature. Here's how to verify in different languages:
Node.js
const crypto = require('crypto');
const express = require('express');
const app = express();
// IMPORTANT: Capture raw body for signature verification
// WooCommerce signs the raw request body, not a re-serialized version
app.use(express.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString('utf8');
}
}));
function verifyWooCommerceSignature(rawBody, signature, secret) {
const hash = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(hash),
Buffer.from(signature)
);
}
// In your webhook handler
app.post('/webhooks/woocommerce', (req, res) => {
const signature = req.headers['x-wc-webhook-signature'];
// Use raw body, not JSON.stringify(req.body) - order/whitespace may differ
if (!verifyWooCommerceSignature(req.rawBody, signature, process.env.WC_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook...
res.status(200).send('OK');
});
Note: WooCommerce applies wp_specialchars_decode() to your secret key before generating the signature. If your secret contains HTML entities, this could cause verification failures. Use alphanumeric secrets to avoid issues.
Python
import hmac
import hashlib
import base64
def verify_woocommerce_signature(payload, signature, secret):
computed = base64.b64encode(
hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).digest()
).decode('utf-8')
return hmac.compare_digest(computed, signature)
Decouple webhook processing to avoid timeouts
WooCommerce expects a 2xx response (200-204), 301, or 302 status code. If your endpoint doesn't respond within a reasonable timeframe or returns an error status, WooCommerce will consider the delivery failed.
So you'll need to process webhooks asynchronously: acknowledge receipt immediately, then queue the payload for processing.
Idempotency to avoid duplicates impacting data integrity
Webhooks may be delivered more than once. Your endpoint must handle duplicate deliveries gracefully without creating duplicate records or performing actions twice.
Use the X-WC-Webhook-Delivery-ID header or resource-specific identifiers (like order_id) to track processed events.
Use HTTPS endpoints
Always use HTTPS for your webhook delivery URLs. This encrypts data in transit and protects sensitive customer information like names, addresses, and order details.
Handle the full payload size
Order webhooks can be large, especially for orders with many line items, metadata, or custom fields. Ensure your web server and application can handle payloads of several kilobytes to a few megabytes.
Log webhook deliveries for debugging
Maintain detailed logs of webhook deliveries for debugging integration issues. WooCommerce logs webhook deliveries at WooCommerce > Status > Logs, but you should also log on the receiving side.
Customize webhook payloads
Use the woocommerce_webhook_payload filter to add custom fields or remove unnecessary data:
Adding custom fields:
add_filter('woocommerce_webhook_payload', function($payload, $resource, $resource_id, $id) {
if ($resource === 'order') {
$order = wc_get_order($resource_id);
$payload['custom_field'] = $order->get_meta('_custom_field');
}
return $payload;
}, 10, 4);
Removing sensitive or unnecessary data to reduce payload size:
add_filter('woocommerce_webhook_payload', function($payload, $resource, $resource_id, $id) {
if ($resource === 'order') {
// Remove sensitive billing details if not needed
unset($payload['billing']['phone']);
// Remove verbose metadata to reduce payload size
unset($payload['meta_data']);
// Keep only essential line item data
if (isset($payload['line_items'])) {
$payload['line_items'] = array_map(function($item) {
return [
'id' => $item['id'],
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'total' => $item['total']
];
}, $payload['line_items']);
}
}
return $payload;
}, 10, 4);
WooCommerce webhook limitations and pain points
Delayed webhook delivery
The Problem: WooCommerce delivers webhooks asynchronously using Action Scheduler, which is backed by WordPress cron. This means webhooks may be delayed by anywhere from a few seconds to minutes, depending on site traffic and cron configuration.
Why It Happens: WordPress cron is "pseudo-cron." It only runs when someone visits your site. On low-traffic stores, this can cause significant delays.
Workarounds:
- Configure a server-side cron job to trigger WordPress cron every minute
- Disable async delivery with
add_filter('woocommerce_webhook_deliver_async', '__return_false')(warning: this can slow down checkout for customers)
How Hookdeck Can Help: By placing Hookdeck between WooCommerce and your application, you can implement more sophisticated retry logic and ensure reliable delivery even when WooCommerce's built-in delivery mechanism experiences delays. Hookdeck's persistent queuing ensures no webhooks are lost during traffic spikes.
Automatic webhook disabling after failures
The Problem: WooCommerce automatically disables webhooks after 5 consecutive delivery failures. This can happen due to temporary network issues, endpoint downtime during deployments, or transient errors, leaving your integration silently broken.
The Impact: Without monitoring, you may not notice a disabled webhook until data synchronization issues surface days or weeks later.
Workarounds:
- Increase the retry limit using the
woocommerce_max_webhook_delivery_failuresfilter:
// Increase max failures to 10
add_filter('woocommerce_max_webhook_delivery_failures', function() {
return 10;
});
- Implement monitoring to alert you when webhooks are disabled
- Regularly check webhook status in the admin dashboard
How Hookdeck Can Help: Hookdeck provides automatic retries with configurable backoff strategies, ensuring temporary failures don't result in lost webhooks. With Hookdeck's monitoring and alerting, you'll know immediately if delivery issues occur.
No built-in duplicate prevention
The Problem: WooCommerce doesn't deduplicate webhooks. Events like order.updated can fire multiple times for a single logical change, and network retries can result in duplicate deliveries.
Common Scenarios:
- An order status change triggers multiple
order.updatedwebhooks - A failed delivery that was actually received results in a retry
- Plugin conflicts cause webhooks to fire unexpectedly
How Hookdeck Can Help: Hookdeck offers configurable deduplication based on payload content or custom identifiers, preventing duplicate processing without requiring changes to your webhook handler code.
Limited retry configuration
The Problem: WooCommerce's built-in retry mechanism is basic. It retries failed webhooks but doesn't offer configurable backoff strategies, retry intervals, or dead letter queues for webhooks that repeatedly fail.
How Hookdeck Can Help: Hookdeck provides sophisticated retry policies with exponential backoff, fixed delays, or custom schedules. Failed webhooks are preserved in a dead letter queue for manual inspection and replay.
Action Scheduler queue backlogs
The Problem: High-volume stores can accumulate massive backlogs of pending woocommerce_deliver_webhook_async actions, preventing timely webhook delivery and consuming database resources.
Workarounds:
- Increase Action Scheduler's concurrent batch limit
- Run Action Scheduler more frequently via server cron
- Clean up unnecessary pending actions
How Hookdeck Can Help: By offloading webhook processing to Hookdeck, you reduce the load on WooCommerce's Action Scheduler. Hookdeck handles queuing, retries, and delivery at scale, allowing your store's internal systems to focus on serving customers.
No native webhook monitoring
The Problem: WooCommerce provides basic delivery logs but lacks dashboards, alerting, or analytics for webhook health. You won't know about delivery issues until they cause visible problems.
How Hookdeck Can Help: Hookdeck's dashboard provides real-time visibility into webhook traffic, success rates, latency, and errors. Set up alerts to notify you of delivery failures before they impact your business.
Payload size and timeouts
The Problem: Large orders with many line items can produce webhook payloads that timeout during delivery or overwhelm receiving endpoints.
Mitigation:
- Use the
woocommerce_webhook_payloadfilter to remove unnecessary data - Consider fetching full details via the REST API after receiving a minimal webhook
How Hookdeck Can Help: Hookdeck handles large payloads efficiently and provides extended delivery timeouts (60+ seconds) compared to WooCommerce's default, giving your endpoint more time to process complex requests.
Testing WooCommerce webhooks
Use request inspection tools
Services like Hookdeck's Console let you inspect webhook payloads without building an endpoint:
- Create a temporary URL
- Configure a WooCommerce webhook with this URL
- Trigger the event (create an order, update a product)
- Inspect the payload and headers
Test in staging
Always test webhook integrations in a staging environment that mirrors production. Create test orders, update products, and verify your endpoint handles all scenarios correctly.
Verify signature validation
Intentionally send requests with invalid signatures to confirm your endpoint rejects them. Test with missing signatures, tampered payloads, and incorrect secrets.
Conclusion
WooCommerce webhooks provide a powerful foundation for real-time integrations, enabling your store to communicate with external systems as events happen. However, the platform's reliance on WordPress cron, limited retry capabilities, and lack of built-in monitoring mean production deployments require careful planning.
By implementing proper signature verification, idempotent processing, comprehensive logging, and reconciliation jobs, you can build robust integrations that handle WooCommerce's quirks gracefully.
For smaller stores with straightforward integration needs, the built-in webhook system combined with proper error handling and reconciliation jobs may be sufficient. For high-volume stores or mission-critical integrations where webhook reliability directly impacts business operations, webhook infrastructure like Hookdeck can address many of WooCommerce's limitations, providing you with reliable delivery, automatic retries with configurable backoff, deduplication, and real-time monitoring without requiring changes to WooCommerce itself.