How WooCommerce delivers webhooks

Delivery rides Action Scheduler on WP-Cron, so it waits for a page load. More than five failures in a row disable the webhook. Format and a Node verifier.

Delivery behaviour

Read from the vendor's own documentation, last checked 2026-08-06. Where the vendor states no number, this table carries none.

Response budgetnot published by the vendor

Delivery is queued into Action Scheduler and driven by WP-Cron, so it leaves when a page is next loaded rather than when the event happens

Retrynot published by the vendor
Gives upWebhook disabled after more than five consecutive delivery failures
Sourcehttps://woocommerce.com/document/webhooks/

Signature

Verified against a working verifier proven by a test suite, not read from documentation. Last verified 2026-08-20.

AlgorithmHMAC-SHA256
Signed payloadraw body
Encodingbase64
Headerx-wc-webhook-signature
Tolerancenone enforced

Verify it

The raw request body, byte for byte, before any JSON parsing. Every scheme on this page breaks the moment a framework re-serializes the payload.

const crypto = require("node:crypto");

// base64(HMAC-SHA256(secret, raw body))
const expected = crypto
  .createHmac("sha256", webhookSecret)
  .update(rawBody)
  .digest("base64");
const received = Buffer.from(req.headers["x-wc-webhook-signature"] ?? "");
const wanted = Buffer.from(expected);
// timingSafeEqual throws on a length mismatch, so check that first.
const valid =
  received.length === wanted.length && crypto.timingSafeEqual(received, wanted);

What bites

The webhook does not fire when the order happens. Delivery is queued into Action Scheduler, which runs on WP-Cron, which runs when somebody next loads a page — on a quiet store that is minutes of built-in delay before the first byte leaves.

More than five consecutive delivery failures disable the webhook outright, and a store owner rarely finds out until something downstream goes visibly stale.

Read more

AnyHook sits in front of endpoints that receive from WooCommerce: it answers in about a second, retries on its own schedule when your server is down, and keeps every event replayable. Change one URL, keep your code.

How it works →