Upgrading webhooks to payload version 2
Payload version 2 changes the shape of data in the webhooks you receive. It is optional and separate from the API version. It has no deadline: a subscriber on payload version 1 keeps receiving it after Core API v1 stops being served on 4 November 2026.
What version 2 gives you
datais the resource at the moment of the event. On version 1,datais read when the webhook is built, so it can include changes made after the event. Version 2 takes its snapshot together with the change, with a rare exception.- A fixed shape you can generate types from. Each resource has a fixed set of fields, published for every event in the Webhooks section of the API reference. The shape does not change when an API response changes.
- Deletion events carry the deleted resource. Version 2 normally sends the resource as it was when it was removed. Version 1 sends only its
id. - An
.updatedevent for every change. On payments, customers, subscriptions, invoices, products and prices,<resource>.updatedis sent whenever a version 2 field changes. - Smaller payloads. Related objects are sent as their ID instead of as expanded objects.
Payload versions compares the two versions in full.
Move a subscriber to version 2
You cannot change the payload version of an existing subscriber. Run a second subscriber on version 2 next to the one you have, then deactivate the old one when the new one works.
-
Make room for one more active subscriber. You can have at most five active subscribers, and a new subscriber is always created active. If you already have five, deactivate one you can spare first. Don't deactivate the subscriber you are replacing yet.
-
Prepare your endpoint for version 2. Update your handler to read the version 2 fields. Until you finish the move, your endpoint receives both versions. If both subscribers use the same URL, read
profilefrom the body to know which subscriber sent the request. Then verify the signature with that subscriber's shared secret. You can also give the new subscriber a different URL, for example by adding a query parameter. -
Create the version 2 subscriber. Subscribe it to the same events as the old one, and set
payloadVersionto2inPOST /v2/webhook-subscribers, or choose Version 2 when you add it in the dashboard. Save the shared secret, because it is shown only once.Creating a version 2 subscriber{"recipientUrl": "https://example.com/webhooks","subscribedTo": ["payment.succeeded", "payment.captured"],"payloadVersion": 2} -
Handle both versions while they overlap. Each event is delivered to each subscriber separately, with the same
eventId. Record theeventIds you have processed and skip one you have already handled, so that you act on each event once. -
Confirm the new subscriber works. Check that your endpoint accepts and processes the version 2 deliveries for every event type you subscribe to.
-
Let the old subscriber finish retrying. Deactivating it pauses the retries of its failed deliveries. List them with
GET /v2/webhook-deliveries:GET /v2/webhook-deliveries?subscriber[eq]=<old_subscriber_id>&success[eq]=falsePage through all of the results with the cursor each time you check, starting from the first page, because deliveries that have stopped being retried stay in the list. Repeat the check until every delivery it returns has stopped being retried:
attemptshas reached 10 orabandonedAtis set. A delivery that succeeds on a retry drops out of the list. Retries normally finish within about 3 days, but a delivery can stay pending for longer. -
Deactivate the old subscriber, at least an hour after you created the new one. A new subscriber receives only events that happen after you create it. An earlier event that has not been sent to the old subscriber when you deactivate it is not sent to either, and the waiting time leaves room for those events to be sent. Then send
{ "active": false }toPOST /v2/webhook-subscribers/{id}, or select Archive for it in the dashboard. There is no delete operation. A deactivated subscriber stays in your list.
Fields by resource
This section explains the fields of data on payload version 2. Each resource has a fixed set of fields, and every event about that resource uses the same set. The Webhooks section of the API reference lists the fields of each event, with their types.
How to read the fields
- Every top-level field listed in the API reference is present. When the resource has no value for a field, the field is
null. The one exception is the rare case wheredatacontains onlyid. - Dates are strings in ISO 8601 format.
- A related object is sent as its ID only, never as the expanded object. To get the full object, retrieve it from the API with that ID. A price's
billingScheduleand a subscription'ssetupLineItemhave no retrieve endpoint of their own. metadatais the resource's metadata after the change that caused the event.- A field the API reference does not list is not sent. If you need it, retrieve the resource from the API. After a deletion event the resource can no longer be retrieved, so keep any field you need for handling a deletion on your side beforehand.
Version 2 sends fewer fields than version 1, but it still contains customer personal data, such as email addresses, phone numbers, dates of birth and addresses. Protect webhook payloads in the same way for both versions.
Payment
Sent with every payment.* event.
Fields: payment.created in the API reference.
captureMethod is the payment's capture method. In the API response, it is inside paymentMethodOptions. The payment response fields displayStatus, cart and paymentMethodOptions, and the is… flags such as isCaptured, are not sent.
Transaction
Sent with every transaction.* event.
Fields: transaction.capture_failed in the API reference.
Customer
Sent with every customer.* event.
Fields: customer.created in the API reference.
dateOfBirth is a date only, in YYYY-MM-DD format. The customer's payment methods and subscriptions are not sent.
Blacklist entry
Sent with every blacklist.* event.
Fields: blacklist.added in the API reference.
blacklist.removed normally carries the entry as it was when it was removed.
Subscription
Sent with every subscription.* event.
Fields: subscription.active in the API reference.
Invoice
Sent with every invoice.* event.
Fields: invoice.created in the API reference.
Product
Sent with every product.* event.
Fields: product.created in the API reference.
Price
Sent with every price.* event.
Fields: price.created in the API reference.
price.deleted normally carries the price as it was when it was deleted.