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
- Go to WooCommerce → Settings → Advanced → Webhooks.
- Click Add webhook.
- 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. - 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.createdfor 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.


