Webhooks connect a store to everything else: accounting, shipping, inventory, a mailing list. They work well and they fail in a way that is unusually hard to notice.
What a webhook is
When something happens in the store, WooCommerce sends an HTTP request to an address you configured, carrying the details.
Under WooCommerce → Settings → Advanced → Webhooks, each one has a topic (order created, order updated, product updated), a delivery URL and a secret used to sign the request.
It is a push rather than a poll, which is why it is efficient and why its failures are quiet.
The failure mode
If the receiving system is down, slow, or returns an error, the store retries a limited number of times and then gives up.
Nothing about the store changes. Orders keep arriving, customers are unaffected, and the only symptom is that the receiving system has no recent data, which nobody checks, because it has always just worked.
Integrations are typically discovered broken weeks later, during a reconciliation or an audit, with a gap of orders that never arrived anywhere.
Watch the delivery log
Each webhook keeps a record of its deliveries, with the response code returned.
Read it after setting one up, and check it periodically afterwards. A webhook whose status has become disabled after repeated failures is the specific thing to look for. It will not re-enable itself.
Better still, reconcile: once a month, compare the number of orders in the store against the number in the receiving system for the same period. That catches gaps regardless of why they happened.
The receiver must tolerate duplicates
This is the requirement that gets missed and produces the worst outcome.
If the receiving system processes a request and then takes too long to respond, the store sees a timeout and retries. The event is processed twice.
An accounting system that creates a second invoice, or a shipping system that prints a second label, has now made a real-world mistake from a technically correct retry.
The receiver should record which events it has already handled, by order ID, or by the delivery identifier the request carries, and ignore repeats. This is not optional for anything that costs money.
Verify the signature
The delivery URL is a public endpoint that accepts data claiming to be from your store.
Every request is signed with the webhook's secret, and the receiver should check that signature before acting on anything. Without it, anyone who learns the URL can create orders in your accounting system.
An endpoint that skips verification because it was quicker to build is a real exposure, not a theoretical one.
The REST API is the other half
Webhooks push events out. The REST API is how another system reads from or writes to the store.
Keys are created under Advanced → REST API, with read or read-write permission. Grant read-only wherever the integration only needs to look, because a read-write key that leaks can modify orders and products.
Each integration should have its own key, so one can be revoked without breaking the others; the same reasoning as any other credential. Using cPanel API tokens goes over the equivalent habit at the hosting level.
Scheduled work is involved
Webhook delivery goes through WooCommerce's background job system rather than happening during the order itself. If that system is stalled, deliveries queue and never send.
A store where several unrelated things stopped working (emails, stock sync, webhooks) usually has one cause: scheduled tasks are not running. Why scheduled tasks do not run deals with fixing it, and WooCommerce database growth walks through the rows it leaves behind.
Test with a real failure
Before relying on an integration, take the receiving system offline briefly and place a test order.
What you want to know is whether anything tells you. If the answer is nothing at all, add the monitoring now: that is the exact scenario that will happen unannounced later. The WooCommerce troubleshooting guide goes into the rest of the store's failure modes.
Make the endpoint answer quickly
The receiving end has a time limit, and exceeding it is what causes the duplicate deliveries described above.
The correct shape is to acknowledge immediately and process afterwards: record the event, return a success response, and do the actual work in a background job.
An endpoint that creates an invoice, sends an email and updates a spreadsheet before responding is an endpoint that will time out under load, and every timeout is a retry, which is how one order becomes three invoices.
The store is not waiting for your processing; it is waiting to be told the message arrived.
Replaying what was missed
When an integration has been broken for a period, the orders that were not delivered still exist and can be sent again.
WooCommerce keeps delivery records per webhook, and individual deliveries can be retried from there. For a longer gap, the practical route is to export the orders for that period and import them into the receiving system directly, rather than replaying hundreds of webhooks.
Either way, this is where duplicate tolerance earns its keep. A replay sends events the receiver may already have, and a system that cannot recognise them creates a second copy of everything.
Reconcile afterwards instead of assuming the replay was complete. Reading WooCommerce analytics and reports walks through the store's side of that comparison.
Log what you send, not only what you receive
When an integration disagrees, the argument is about what was sent. Without a record on your side, that argument cannot be settled.
Keep the delivery identifier, the event type, the order reference and the response code for every attempt, not the full payload, which contains customer data and grows quickly.
That record answers the two questions that actually arise: was this event ever sent, and what did the other end say. Both are otherwise unanswerable a week later.
Set a retention period on it, since a log of every event since the store opened is itself a data-protection consideration. What a website needs for privacy compliance goes over the obligations.
Alert on absence, not only on failure
Failure alerts catch an endpoint returning errors. They do not catch a webhook that was disabled, deleted, or never fired because the queue stalled.
The check that covers all of those is the opposite one: alert when the receiving system has seen no events for longer than normal.
For a store taking daily orders, silence for a day is a signal. That single check catches every variety of this failure, including the ones that produce no error anywhere. For the commonest underlying cause, see why scheduled tasks do not run.