Skip to content
WooRescueHQ

WooCommerce / Integrations

WooCommerce Webhook Troubleshooting

Short answer

Check the webhook's status and its delivery logs first. WooCommerce delivers webhooks in the background through Action Scheduler and disables a webhook after repeated consecutive failed deliveries — any response outside 2xx, or a timeout, counts as a failure. Late deliveries point to Action Scheduler or WP-Cron; failures point to the receiving endpoint (errors, slowness, signature checks, firewalls); duplicates point to receivers that are not idempotent.

Symptoms

  • The receiving system never gets some or all events.
  • Events arrive minutes or hours late.
  • The webhook's status changed to Disabled without anyone touching it.
  • The receiver processes the same order twice.
  • The receiver rejects deliveries as unauthenticated.

Most common causes

  1. The receiver fails: it returns 4xx/5xx because of its own bugs, authentication rules or validation of unexpected payloads.
  2. The receiver is too slow: it does all its work before answering, so the request times out.
  3. Something blocks the delivery: the receiver's firewall, WAF or IP allowlist; or the store's host blocking outgoing requests.
  4. Signature verification is wrong: computed over parsed JSON instead of the raw body, or with an old secret.
  5. Background processing is stuck: Action Scheduler or WP-Cron is not running, so deliveries queue up.
  6. Receivers are not idempotent: order.updated fires on many saves, and retries resend events, so the same order arrives several times.

Diagnosis

1. Check status and delivery logs

Under WooCommerce → Settings → Advanced → Webhooks, check the status (Active, Paused, Disabled), the topic and the delivery URL. Then check WooCommerce → Status → Logs for the webhook delivery log, which records each delivery with its response code and duration.

What the log shows Meaning
No deliveries for recent events Event not triggered, or deliveries still queued
2xx Delivered; the problem is on the receiving side
3xx The delivery URL redirects; use the final URL
401 / 403 The receiver or its firewall rejects the request
5xx The receiver failed while processing
Timeout / connection error The receiver is too slow or unreachable

2. Check the queue

Deliveries run as woocommerce_deliver_webhook_async actions. In Tools → Scheduled Actions, search for that hook: many Pending or past-due actions mean the queue is not being processed; Failed actions contain the error.

3. Check the receiver

On the receiving side, log the raw request, the headers and the response it returns for one test delivery. WooCommerce sends useful headers: X-WC-Webhook-Topic, X-WC-Webhook-Resource, X-WC-Webhook-ID, X-WC-Webhook-Delivery-ID and X-WC-Webhook-Signature.

Logs and technical checks

  • Webhook settings: status, topic, delivery URL, secret, API version.
  • WooCommerce logs for webhook deliveries.
  • Tools → Scheduled Actions: pending, past-due and failed delivery actions.
  • Receiver logs: raw body, headers, response code, processing time.
  • Firewalls on both sides: outgoing requests from the store, incoming at the receiver.

Solutions

  • Answer fast, process later: the receiver should verify the signature, store the event and return 200 immediately, then process it in its own queue.
  • Verify signatures over the raw body:
PHPreceiver/webhook.php
$payload   = file_get_contents( 'php://input' );
$signature = $_SERVER['HTTP_X_WC_WEBHOOK_SIGNATURE'] ?? '';
$expected  = base64_encode( hash_hmac( 'sha256', $payload, WEBHOOK_SECRET, true ) );

if ( ! hash_equals( $expected, $signature ) ) {
    http_response_code( 401 );
    exit;
}

// Store the event, return 200 now, process it asynchronously.
  • Accept the ping that WooCommerce sends when a webhook is saved (form data with webhook_id) and return 200.
  • Make processing idempotent: use the delivery id and the resource id plus its modification time to skip events you have already applied.
  • Keep the queue moving: make sure WP-Cron runs — preferably from a real server cron job — and clear any Action Scheduler backlog.
  • Re-enable the webhook after fixing the receiver: set its status back to Active.

What not to do

  • Do not skip signature verification on the receiver; anyone could post fake orders to it.
  • Do not run slow work — ERP calls, emails, PDF generation — before answering the webhook.
  • Do not re-enable a disabled webhook repeatedly without fixing the receiver; it will be disabled again.
  • Do not delete pending delivery actions to "clean up"; they are events the receiver has not seen.

When to contact an expert

Bring in an engineer when the integration loses or duplicates orders, when you control only one side of the integration and need evidence for the other, or when the receiver needs to be rebuilt to be fast, verified and idempotent.

Frequently asked questions

Why was my WooCommerce webhook disabled?

WooCommerce disables a webhook after a number of consecutive failed deliveries. A delivery fails when the receiver responds outside the 2xx range or does not respond in time. Fix the receiver, then set the webhook back to Active.

Why do webhooks arrive late?

Deliveries are queued as background actions. If WP-Cron or the Action Scheduler runner is not processing the queue — low traffic, a disabled WP-Cron without a real cron job, or a backlog — deliveries wait.

How do I verify a webhook's signature?

Compute a base64-encoded HMAC-SHA256 of the raw request body using the webhook's secret, and compare it with the X-WC-Webhook-Signature header using a constant-time comparison.

Why does my receiver get a request that is not JSON?

When a webhook is saved, WooCommerce sends a ping containing only webhook_id as form data. Receivers should accept it and answer 200, otherwise the first delivery already counts as a failure.

$ describe the problem

Have a WooCommerce problem?Request a diagnosis.

Tell us what is failing, what changed recently and the business impact. We review every request and recommend the appropriate next step.

Request a diagnosis See services and prices

Never send passwords, API keys or card data through the form.

Diagnosis from€299

Request a diagnosis