CommerceHook

WooCommerce webhooks

This page is about setting a WooCommerce webhook integration up. If one already exists and is misbehaving, the WooCommerce webhook inspector covers why deliveries fail, arrive late, or stop entirely.

Two ways in

WooCommerce gives you the same webhook two ways, and which you pick mostly depends on whether you will do it again.

In the admin

Go to WooCommerce, then Settings, Advanced, Webhooks, and Add webhook. It is the quickest route for one or two, and the delivery log that appears afterwards is per webhook.

Over the REST API

POST /wp-json/wc/v3/webhooks, authenticated with a consumer key and secret created under Settings, Advanced, REST API. This is the route worth learning if you manage more than one store, because it is scriptable and the same call works against every install.

curl -X POST "https://example.com/wp-json/wc/v3/webhooks" \
  -u "$WC_CONSUMER_KEY:$WC_CONSUMER_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Orders to CommerceHook",
    "topic": "order.created",
    "delivery_url": "https://hooks.commercehook.app/{endpoint_id}",
    "secret": "a-long-random-string",
    "status": "active"
  }'

The four fields that matter

Topic

The event. The built-in resources are orders, products, customers and coupons, each with created, updated and deleted, so order.created and product.updated are the shapes you will use most. Anything WordPress fires as an action hook can also be a topic, written action.hook_name, which is how you reach behaviour a plugin adds rather than WooCommerce itself.

Delivery URL

Where it goes. It has to be reachable from the store, which is the part that catches people developing locally: a localhost URL is reachable from your laptop and from nowhere else.

Secret

What signs the delivery. It is optional, and it should not be: set one explicitly, a long random string, and keep it where your handler can read it. Leaving it to chance is how you end up unable to tell a real delivery from anything else that can reach a public URL.

Status

active, paused or disabled. Worth knowing now because WooCommerce will change it for you later, and that is the single most common reason a working integration stops.

You will see a delivery straight away

The moment a webhook is created, WooCommerce pings the delivery URL with a small form-encoded body, webhook_id=123, to check something answers.

It is expected. It carries no event type and it is the only non-JSON delivery a healthy webhook sends, so a handler that assumes JSON on every request will throw on the very first one it ever receives. Handle it or ignore it, but know it is coming.

Verifying a delivery

Every delivery carries X-WC-Webhook-Signature: a base64-encoded HMAC-SHA256 of the raw request body, keyed with the secret you set.

Compute the same HMAC over the exact bytes you received and compare. Over the raw bytes, not a re-serialised object: parsing the JSON and stringifying it again changes the whitespace, which changes the digest, and produces a mismatch that looks like an attack and is actually your own framework.

Three things that will bite you

Deliveries run through wp-cron

WooCommerce queues a delivery and sends it in the background, and wp-cron only runs when somebody visits the site. A quiet store can sit on a delivery until the next page load. If timing matters, disable the pseudo-cron and run a real one on a schedule.

Five consecutive failures disables the webhook

Anything that is not a 2xx counts. After the fifth, WooCommerce sets the status to disabled and stops trying, without telling you. The webhook is still listed, it simply never fires again, which is why an integration that worked in testing can be silently dead by the time it matters.

Staging gates and basic auth count as failures

A staging site behind a password answers 401 to WooCommerce exactly as it would to anybody else, and five of those are enough.

Pointing it at CommerceHook

An endpoint answers 2xx immediately and always, so the five-failure counter never starts counting, and the delivery is kept whether or not whatever you are building behind it was up at the time.

Create an endpoint, then register from the dashboard by saving the store’s consumer key and secret, and tick the topics you want. We create the webhooks over the REST API, set the signing secret, and verify signatures on delivery from then on. If you would rather not save credentials, the dashboard prints the same curl command for you to run yourself.

From there every delivery is readable in full: the payload as a JSON tree or raw, every header including the signature, the method, and the query string it arrived with. Deliveries can be replayed at your own handler once it is ready, and commercehook listen streams them straight to a handler on your laptop, which is the part that removes the tunnel from the loop.

Free for one endpoint, no card. The quick start is the five-minute version, and /woocommerce covers what the integration does once it is running.