Verifying Events
Webhooks can be vulnerable to security risks, such as malicious actors sending fake events or replaying intercepted requests. To safeguard against these threats, you should prevent replay attacks, verify the origin and authenticity of events, and prevent duplicate processing of events. A replay attack can be prevented by blocking intercepted requests from being reused. Every webhook includes thewebhook-timestamp request header, which provides the Unix timestamp of when the event was signed. Use this timestamp to prevent replay attacks:
- Ensure the
webhook-timestampis within a valid time window (we recommend up to 3 minutes). - Since retries generate a new timestamp for each attempt, you can confidently validate each request.
- Reject requests with timestamps outside the threshold to block tampered or replayed events.
webhook-signature included as part of the webhook. We sign all webhooks originating from us using the secret signing key. Follow these steps to ensure the event was sent by us:
- Generate the signed content by concatenating the values of the
webhook-idrequest header,webhook-timestamprequest header, and a string representation of the request body into a single string, separated by periods. - Calculate the expected signature, using the secret key and the signed content above to generate a
HMACSHA256signature. Then, encode this value using abase64encoding. - Verify the signature by comparing the expected signature with the signature sent in the
webhook-signatureheader. If the generated signature matches the provided signature, you can proceed with processing the event.
Handling Duplicate Events
Duplicate processing of events can be prevented at the request-level by processing events in an idempotent-safe manner. Each webhook request includes both awebhook-id in the request header and an event-id in the message body. The webhook-id uniquely identifies a single delivery attempt, while the event-id uniquely identifies the event itself across all retries or replays of the same event.
To prevent duplicates, store the webhook-id in a persistent data store and ensure that a request with the same identifier has not been processed before. Using both identifiers ensures that duplicate HTTP deliveries and duplicate logical events are handled safely.
Beyond request-level handling, integrations should always implement operation-level idempotency, as request-level deduplication is often only applied over a limited time window. Operation-level idempotency ensures that repeated or delayed business events do not cause inconsistent state changes after that window has expired.
Before applying an update to a resource such as a payment_session or a payment, inspect the payload and the current state of the object:
- If the
payment_session(as identified by the payment session identifier) is already in a final state, for example,completed,expired, orcanceled; or apayment(as identified by the payment identifier) is already in a final state, for example,succeeded, ignore the event by responding with a 2xx status code to prevent redelivery — since no further state change is needed. - Only apply updates that move the object forward, for example:
payment_session:active→completedpayment:draft→succeeded
Additional Security Precautions
In addition to the security measures used to verify the origin and authenticity of webhook events described above, all webhook events are sent from a specific set of source static IP addresses. If your webhook endpoint is hosted behind a firewall or a NAT (Network Address Translation) gateway, you’ll need to ensure it can receive incoming requests. Whitelist the following source static IP addresses in your network configuration so that webhook traffic is not blocked or dropped by your security settings.Sample Code
Code Snippet - Receive the webhook event
Code Snippet - Receive the webhook event
Code Snippet - Verify the origin and authenticity of the event
Code Snippet - Verify the origin and authenticity of the event
Generate the signed content:Calculate the expected signature:Verify the signature:
Full Sample Code
Full Sample Code

