POST request with a JSON body to the webhook URL you configure in your dashboard. New to receiving webhooks? Start with Receiving Webhooks for setup steps and a minimal handler.
Before writing a handler, read Checkouts, Charges and Reviews. Most events below report a transition of a charge, not of the checkout session, and one checkout can produce several charges — which determines what you should key your ledger on.
The Envelope
Every webhook delivery is a JSON object with three top-level keys. The wire key order is alwaysevent_id, data, type — note that data comes before type, even though it reads more naturally last.
Here’s a complete, realistic
checkout.succeeded delivery for a deposit with a populated line item:
charge.failure_code and charge.failure_message are present on every charge-based event and are null when there’s no failure. subscription is present on every one of the 12 events — it’s null for non-subscription checkouts, otherwise an object describing the recurring plan.
Events
Soap sends 12 event types. Four are always sent and cannot be disabled. The other eight are off by default and will never fire until you enable them. Not every charge status has an event: a charge that becomescancelled or refunded is not notified. See the charge status lifecycle for the full status-to-event mapping.
Verifying Webhook Signatures
To prevent malicious actors from sending you fake events, every delivery includes a signature. Soap sends the identical signature value in two headers:
There is no separate timestamp header and no event-id header — the timestamp is embedded in the signature value, and
event_id is in the body.
Both headers use the same format:
tis the Unix timestamp when the signature was generated.v1is the signature itself, an HMAC-SHA256 hex digest.
"<timestamp>.<raw_request_body>" — the timestamp, a literal period, then the exact raw bytes of the request body. Recompute this using your webhook signing secret (wss_..., from Dashboard → Developers) and compare it to the received signature.
Updating Your Internal Ledger
It depends on how you implement your accounting system. In this example, we only use credits and debits and update the customer’s balance or internal ledger accordingly.Retries, Timeouts and Idempotency
- Each delivery attempt has a 10 second timeout.
- Soap retries up to 5 total attempts (the initial attempt plus 4 retries) for events that fail or time out.
checkout.holdis delivered synchronously and is never retried — seecheckout.holdfor why your response status matters.- Each delivery attempt generates a new timestamp and therefore a new signature — even retries of the same logical event will have a different
SOAP-WEBHOOK-SIGNATUREvalue. event_idis stable for a given logical event across all of its delivery attempts. Use it as your idempotency key so you don’t reprocess the same event twice.

