Skip to content

Define webhook replay and delivery-age checks

Separate signature freshness from duplicate event processing. Build tests for stale signed deliveries, retries, clock skew, and endpoint-specific replay policy.

A webhook can be authentic and still be an old delivery. Decide what freshness means for the provider before adding a timestamp comparison. Some providers sign a delivery timestamp; others expose an event creation time that is not a substitute for signed request freshness.

Stripe documents timestamp-based replay protection in its signature verification flow. Apply provider-specific rules. Do not copy a tolerance or header name into another integration without checking its contract.

Separate three different clocks

Record event creation time, delivery-signature time where available, and local receipt time. A delayed business event may arrive in a newly signed delivery. Rejecting it merely because the event was created earlier can break legitimate recovery after an outage.

CaseFreshness questionSeparate processing question
Recent signed deliveryDoes the verifier accept its age?Has this event already changed state?
Stale signed requestIs it outside the permitted window?No side effect should begin
Provider retryIs this delivery authentic now?Reuse the existing event disposition
Future timestampIs clock skew within the defined policy?Investigate abnormal clock behavior

Write the tolerance, time source, and failure behavior in the endpoint's review record. Keep the values configurable only through trusted configuration.

Build bounded replay fixtures

Use a controlled test event and the provider's documented verification mechanism. Exercise an accepted delivery and a retained old request separately. Confirm rejection before a queue item, database update, or outbound notification is created.

Simulate clock offsets in the test harness rather than changing the production host clock. Include the edges around the accepted window. Record which timestamp the verifier actually uses so a refactor cannot silently switch from signed delivery time to an untrusted payload field.

Plan the recovery path

A rejected delivery needs an operational disposition. Preserve a minimal event reference and reason, then use an approved provider redelivery or reconciliation path when appropriate. Do not create an administrative bypass that processes arbitrary unsigned payloads.

Keep freshness enforcement and deduplication as separate tests. A fresh retry can be legitimate but must not charge, provision, or notify twice. Conversely, a deduplication store does not make an unverified old request acceptable. Review retention against the documented retry and recovery windows. End with evidence that stale requests stop before business effects and that valid recovery deliveries can still be reconciled safely.

Find the gaps before your next launch

Use the release checklist to identify changed security boundaries and missing evidence. Discuss the application paths you want reviewed before launch.

Sources

  1. Stripe: Receive events in your webhook endpointAccessed