← All articles

November 4, 2026 · Muhammad Rehan · More by Muhammad Rehan

PayPal's 25 Webhook Retries Aren't Exactly-Once

PayPal retries a failing webhook up to 25 times over 3 days — but that's not deduplication. Here's why you still need an event-ID store.

PayPal's webhook documentation guarantees delivery: a failing endpoint gets retried up to 25 times over 3 days until it returns an HTTP 2xx response. It's easy to read that guarantee as safety from duplicates. It isn't. PayPal's Invoicing webhooks guide is explicit about the gap: one of its listed endpoint requirements is "Handle retries: the same event may arrive multiple times." Retrying delivery and deduplicating processing are two separate jobs, and integrators who only build one of them end up either reprocessing events they shouldn't, or assuming a header that doesn't cover webhooks at all.

What the retry window actually promises

A listener that doesn't respond with a 2xx status gets retried by PayPal up to 25 times over 3 days. After 3 days without success, the delivery is marked Failed, though it can still be resent manually from the Webhooks Events dashboard. That dashboard's own date-range filter only reaches back 30 days, and it also shows which deliveries succeeded (a green check mark) versus which are still pending (a yellow exclamation point) — useful for auditing, but not a substitute for your own long-term event log if you need history past that window.

None of that retry mechanics implies exactly-once delivery. The Invoicing webhooks guide's endpoint requirements table lists "Handle retries" right alongside "Return 200 OK within 30 seconds" and "Validate signatures" — it's a baseline requirement, not an edge case to shrug off. The fix PayPal documents is to use the webhook event's own id field, described plainly as the "unique webhook event identifier" to "use this for deduplication." There's no separate idempotency-key header on inbound webhook deliveries the way there is on outbound calls — the event id is what you store and check against before processing.

PayPal-Request-Id solves a different problem

Separately, PayPal's REST APIs expose a PayPal-Request-Id header to enforce idempotency on POST calls you make to PayPal — capturing an authorized payment, for instance. Include a previously used PayPal-Request-Id and PayPal returns the latest status of that earlier request instead of repeating the action; omit the header and PayPal will duplicate the request. PayPal is explicit that "not all APIs support this header," so it has to be checked per endpoint, and the header's value must be unique per request and per call type — authorizing a payment and capturing it count as different call types even on the same order. PayPal recommends UUID format, which fits the header's 38 single-byte character limit.

The header has one more sharp edge: send two simultaneous requests with the same PayPal-Request-Id, and PayPal processes the first and may fail the second, rather than treating both as safely idempotent. That's a reasonable design for outbound calls you control the timing of. But none of this applies to webhooks arriving at your endpoint — PayPal-Request-Id is scoped to REST POST calls you send, and inbound webhook deliveries have no equivalent header.

Build both, not either

The practical shape of a correct integration: use PayPal-Request-Id on the outbound POST calls that support it, so a timed-out capture request doesn't get processed twice on PayPal's side. Separately, maintain your own dedup store keyed on each webhook event's id, so a redelivered INVOICING.INVOICE.PAID or similar event doesn't get processed twice on your side. They're not redundant with each other, and treating either one as covering both directions is exactly the assumption PayPal's own documentation is warning against.

Sources

Payments & Marketplaces

ASSOSIATIX works on this every day. See our Custom Shopify App Development service

$./start-project.sh

READY TO BUILD
WHAT'S NEXT?

Tell us where you are and where you want to go. We'll help you choose the right path—storefront, system, product, or a focused growth sprint.