How Paddle delivers webhooks

Up to 60 delivery attempts over about 2 days in live mode, about 10 in the first hour. Paddle's ts/h1 signature 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 budget5s

Check whether your handler answers in time

RetryLive: up to 60 attempts over about 2 days, about 10 in the first hour and about 45 in the first day. Sandbox: 3 attempts within about 2 min
Gives upnot published by the vendor
Sourcehttps://developer.paddle.com/webhooks/about/respond-to-webhooks

Signature

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

AlgorithmHMAC-SHA256
Signed payload{timestamp}:{body}
Encodinghex
Headerpaddle-signature = ts=X;h1=hex
Tolerance5s

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");

// paddle-signature: "ts=1700000000;h1=abc..." — HMAC-SHA256 over "{ts}:{body}"
const pairs = Object.fromEntries(
  req.headers["paddle-signature"].split(";").map((p) => p.split("=")),
);
const expected = crypto
  .createHmac("sha256", secretKey) // per destination: pdl_ntfset_...
  .update(`${pairs.ts}:${rawBody}`)
  .digest("hex");
const received = Buffer.from(pairs.h1 ?? "");
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);

Paddle's SDK enforces a 5-second timestamp tolerance, the tightest of any sender here. Behind a relay or a queue, check the timestamp against when the event arrived at the edge, not when your worker got to it.

What bites

The retry curve is dense where Stripe's is sparse: about 10 attempts in the first hour, about 45 in the first day, up to 60 over about 2 days. A handler that is merely slow runs again every few minutes at first, and without idempotency each run repeats its side effects.

Sandbox behaves nothing like live (3 attempts within about 2 minutes), so retry handling that looks fine in testing has seen 5% of the pressure.

Read more

AnyHook sits in front of endpoints that receive from Paddle: 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; to accept retries and replays, check AnyHook-Signature, because Paddle's own signature expires after 5s.

How it works →