WooCommerce Webhooks: Setup, Topics, Security and Debugging

How WooCommerce webhooks work, how to create one, which topics are available, how to verify the signature, and how to debug webhooks that are not firing.

WooCommerce webhooks are automatic HTTP POST requests that WooCommerce sends to a URL you choose whenever a selected event happens, such as an order being created, a product updated or a customer deleted. The request contains the resource as JSON and a signature header you verify with a shared secret. They are the simplest way to connect a store to other systems in real time.

I use them to connect stores to accounting tools, shipping systems, CRMs and automation platforms. This guide is part of my AI and automation series.

How do WooCommerce webhooks work?

When the chosen event occurs, WooCommerce builds a JSON payload of the resource (for example the full order) and queues a delivery to your URL. Deliveries are processed in the background through Action Scheduler by default. The receiving server responds with a 2xx status to confirm; other responses count as failures.

How to create a webhook

  1. Go to WooCommerce → Settings → Advanced → Webhooks.
  2. Click Add webhook.
  3. Fill in:
    – Name: something descriptive, e.g. “Orders to accounting.”
    – Status: Active.
    – Topic: the event (see below).
    – Delivery URL: your HTTPS endpoint.
    – Secret: a long random string, used to sign payloads.
    – API version: the REST API version used to build the payload.
  4. Save. WooCommerce sends a ping to the URL.

Available topics

Built-in topics cover the main resources and actions:

Resource Topics
Order order.created, order.updated, order.deleted, order.restored
Product product.created, product.updated, product.deleted, product.restored
Customer customer.created, customer.updated, customer.deleted
Coupon coupon.created, coupon.updated, coupon.deleted, coupon.restored

There is also an Action topic, which fires on any WordPress action hook you name, and developers can register custom topics.

Note that order.updated fires on many changes, including status changes and notes. If you only care about paid orders, filter on the receiving side by status, or use a custom topic.

Verifying the signature

Every delivery includes headers such as X-WC-Webhook-Topic, X-WC-Webhook-Resource, X-WC-Webhook-Delivery-ID and X-WC-Webhook-Signature. The signature is a base64-encoded HMAC-SHA256 hash of the raw request body, using your secret.

PHP receiver:

$raw       = file_get_contents( 'php://input' );
$signature = $_SERVER['HTTP_X_WC_WEBHOOK_SIGNATURE'] ?? '';
$expected  = base64_encode( hash_hmac( 'sha256', $raw, getenv( 'WC_WEBHOOK_SECRET' ), true ) );

if ( ! hash_equals( $expected, $signature ) ) {
    http_response_code( 401 );
    exit;
}
$order = json_decode( $raw, true );
// Queue the work, respond fast.
http_response_code( 200 );

Node.js (Express) receiver:

app.post('/wc-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = crypto.createHmac('sha256', process.env.WC_WEBHOOK_SECRET)
    .update(req.body).digest('base64');
  const given = req.get('X-WC-Webhook-Signature') || '';
  if (given.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
    return res.sendStatus(401);
  }
  res.sendStatus(200);
  // process JSON.parse(req.body) asynchronously
});

Always compute the hash on the raw body, before any JSON parsing changes whitespace.

Designing a reliable receiver

  • Respond quickly. Return 200 and process the work in a queue. Slow responses cause timeouts and failures.
  • Be idempotent. The same event can arrive more than once. Store the delivery ID or order ID plus status and ignore duplicates.
  • Do not trust order of arrival. An update may arrive before a create in rare cases; fetch the latest state from the REST API if order matters.
  • Log everything for a while: headers, status, processing result.

Debugging webhooks that are not firing

1. Check the webhook status

If deliveries kept failing, WooCommerce disables the webhook after consecutive failures (five by default). Fix the endpoint, then set status back to Active.

2. Check Action Scheduler

Go to Tools → Scheduled Actions and search for woocommerce_deliver_webhook_async. Pending actions that never run usually mean WP-Cron is not running. See my guide to WP-Cron.

3. Check logs

WooCommerce logs webhook deliveries under WooCommerce → Status → Logs. Look for the response code and body from your endpoint.

4. Check the receiving server

  • Firewalls or bot protection blocking WooCommerce’s requests.
  • Basic auth on the endpoint.
  • TLS certificate problems.
  • The endpoint returns a redirect (e.g. http → https) instead of 200.

5. Check the store side

  • Security plugins blocking outgoing requests.
  • Hosting restricting outbound HTTP.
  • The event not actually happening (e.g. orders created via a method that skips hooks).

Testing

Use a temporary request inspection service on staging to see real payloads, then point to your endpoint. Create test orders with each payment method. With WP-CLI you can list and inspect webhooks:

wp wc webhook list --user=admin

More in my WP-CLI commands guide.

Security checklist

  • HTTPS delivery URL only
  • Long random secret, stored in environment variables, not code
  • Signature verified with constant-time comparison
  • Endpoint does minimal work and rejects unexpected topics
  • Personal data handled according to privacy rules

Common mistakes

  • Doing heavy processing before responding, causing timeouts.
  • Parsing JSON before verifying the signature.
  • Using order.created for fulfilment, when the order may still be unpaid.
  • Forgetting webhooks on staging still point to production systems.

Need your store connected to accounting, shipping or a CRM? Contact me. For payment-specific callbacks, see payment gateways in Egypt.

Frequently asked questions

What is a WooCommerce webhook?

It is an automatic notification WooCommerce sends to an external URL when a store event happens, for example an order being created or a product updated, containing the related data as JSON.

What is the difference between webhooks and the REST API?

The REST API is pull: your system asks WooCommerce for data. Webhooks are push: WooCommerce tells your system when something changes. Many integrations use both.

Why was my WooCommerce webhook disabled?

WooCommerce disables a webhook after a number of consecutive delivery failures (five by default). Fix the receiving endpoint, then set the webhook status back to Active.

Can I add custom webhook topics?

Yes. Developers can register custom topics with filters, or use the action topic to fire a webhook on any WordPress action hook.

Muhamed Ahmed

Ecommerce & Web Solutions Developer

I'm Muhamed Ahmed, an ecommerce and web solutions developer with 15 years of experience building fast, secure online stores and websites on WooCommerce, WordPress and custom Laravel platforms. I write about website speed, SEO, automation and conversion.

All articles Work with me

Related articles