Author picture Gareth Wilson

Guide to WooCommerce Webhooks Features and Best Practices

Published


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

FeatureDetails
Webhook configurationWooCommerce admin dashboard or REST API
Hashing algorithmSHA256 HMAC
TimeoutVaries based on server configuration
Retry logicAutomatic retries, webhook disabled after 5 failures
Alert logicNot available
Manual retryNot available
Browsable logAvailable 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 placed
  • order.updated - Triggered when any order data changes
  • order.deleted - Triggered when an order is trashed
  • order.restored - Triggered when an order is restored from trash

Product Events

  • product.created - Triggered when a new product is added
  • product.updated - Triggered when product data changes
  • product.deleted - Triggered when a product is trashed
  • product.restored - Triggered when a product is restored

Customer Events

  • customer.created - Triggered when a new customer registers
  • customer.updated - Triggered when customer data changes
  • customer.deleted - Triggered when a customer is deleted

Coupon Events

  • coupon.created - Triggered when a new coupon is created
  • coupon.updated - Triggered when coupon data changes
  • coupon.deleted - Triggered when a coupon is trashed
  • coupon.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:

HeaderDescriptionExample
X-WC-Webhook-TopicThe event topicorder.created
X-WC-Webhook-ResourceThe resource typeorder
X-WC-Webhook-EventThe event actioncreated
X-WC-Webhook-SignatureHMAC-SHA256 signaturebase64-encoded-hash
X-WC-Webhook-IDThe webhook's internal ID15
X-WC-Webhook-Delivery-IDUnique delivery identifier42

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:

VersionDescription
WP REST API Integration v3Current recommended version. Provides the most complete data structure.
WP REST API Integration v2Legacy version with slightly different field names.
WP REST API Integration v1Oldest version, may lack newer fields.
Legacy API v3Pre-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

  1. Navigate to WooCommerce > Settings > Advanced > Webhooks

  2. Click Add webhook

  3. 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
  4. 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_failures filter:
// 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.updated webhooks
  • 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_payload filter 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:

  1. Create a temporary URL
  2. Configure a WooCommerce webhook with this URL
  3. Trigger the event (create an order, update a product)
  4. 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.


Author picture

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.