Setting up Webhooks
To start receiving webhook events, reach out to the customer success team to:- Register the URL of an endpoint to receive webhooks
- Receive a secret signing key that will be generated and shared with you and is essential for ensuring the authenticity of incoming payloads and preventing malicious requests
Ensure that the URL you specify points to a secure endpoint using HTTPS, and can receive POST requests.
The secret signing key can be rotated on request.
Using Webhooks
Request Headers
When we send a webhook event, the HTTP request includes three essential headers:-
webhook-id: An ID that uniquely identifies each message. -
webhook-timestamp: A Unix timestamp indicating when the event was signed. This timestamp is included in the signature to guard against replay attacks. See Verifying Events for more details. -
webhook-signature: A list of signatures (HMAC-SHA256) created using a secret signing key and will be used to validate the authenticity of an event. Typically, the signature list consists of only one element, but it can contain any number of signatures. It is formatted as a space delimited list of signatures and their corresponding version identifiers. See Verifying Events for more details. For example:
Processing Events
The endpoint on your server must be capable of receiving HTTPS POST requests, and it’s important to respond with a 2xx status code within a reasonable timeframe. We recommend sending the response within 15 seconds to ensure timely confirmation. This endpoint will be triggered whenever an important event occurs, for example the status of a payment changes, thus enabling you to handle events in real time. It’s important to respond with a 2xx status code within a reasonable timeframe. We recommend sending the response within 15 seconds to ensure timely confirmation. If no 2xx status code is received, the system retries delivery according to this schedule:- Immediately
- 5 seconds
- 5 minutes
- 30 minutes
- 2 hours
- 5 hours
- 10 hours
- Additional 10 hours
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 above above with the signature sent in the
webhook-signatureheader. If the generated signature matches the provided signature, you can proceed with processing the event.
Structure of an Event
The event payload of a webhook request sent to the registered endpoint will contain:id: An ID that uniquely identifies each event.type: The type of event that triggered the webhook, for example,payment_session.createdorpayment_session.updatedeventsdata: The actual payload, which can be a different object depending on the event type. For example, forpayment_session.*events, the payload will always be thepayment_sessionobject.
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 in the Verifying Events section 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. To do this, make sure to whitelist the following source static IP addresses in your network configuration so that webhook traffic isn’t blocked or dropped by your security settings.Sample Code
Our code sample below will help you get started quickly.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

