Repository navigation
Migration guide for v23
This version uses API version 2026-09-30.endive. If the format of this API version looks new to you, see our new API release process.
Please review our API changelog for 2026-09-30.endive to understand all the breaking changes to the Stripe API, the reasons behind them and potential alternatives.
The Node SDK specific changelog for v23 has the corresponding changes in the SDKs as well as SDK specific breaking changes.
Node.js versions lower than 20 are no longer supported.
Action required: Upgrade to Node 20 or later before updating to the latest SDK version. For more information, see our language version support policy.
V2 list methods return Stripe.V2List<T>, whose pagination fields are next_page_url and previous_page_url. In previous versions, Stripe.V2List<T> also declared the V1-only object, has_more, and url fields for type compatibility, even though those fields were never populated at runtime.
There is no specific migration for this change. To paginate a V2 list response, you can continue to use the V2 pagination URLs:
const events: Stripe.V2List<Stripe.V2.Core.Event> = await stripe.v2.core.events.list();
if (events.next_page_url) {
// ...
}
V2 request address properties now use operation-specific V2 interfaces instead of shared V1 types such as Stripe.AddressParam and Stripe.JapanAddressParam. This includes ordinary, shipping, kana, and kanji addresses. Prefer inferring these nested types from the operation parameters. When an explicit type is needed, use the interface exposed by the operation. For example:
// Before
const address: Stripe.JapanAddressParam = {
town: 'town',
};
const params: Stripe.V2.Core.AccountUpdateParams = {
identity: {individual: {address}},
};
const account = await stripe.v2.core.accounts.update(accountId, params);
// After
const address: Stripe.V2.Core.AccountUpdateParams.Identity.Individual.Address = {
town: 'town',
};
const params: Stripe.V2.Core.AccountUpdateParams = {
identity: {individual: {address}},
};
const account = await stripe.v2.core.accounts.update(accountId, params);V1 addresses continue to use the shared V1 address parameter types.
The Stripe.ErrorType namespace has been removed. It only ever provided types (for example, Stripe.ErrorType.StripeCardError), and every one of those types is also available under Stripe.errors.
To migrate, find and replace Stripe.ErrorType with Stripe.errors in your type annotations:
// Before
function handleError(err: Stripe.ErrorType.StripeCardError) {
// ...
}
// After
function handleError(err: Stripe.errors.StripeCardError) {
// ...
}The top-level constructEventWithoutVerification(...) method that was mistakenly added to the Stripe client instance in a recent release has been removed. Use stripe.webhooks.constructEventWithoutVerification(...) instead.
// Old (Removed)
const event = stripe.constructEventWithoutVerification(payload);
// New (Required)
const event = stripe.webhooks.constructEventWithoutVerification(payload);
If your integration handles StripeAPIError specifically for a connection that closes while the response body is being read, update it to handle StripeConnectionError instead.
Before:
try {
await stripe.balance.retrieve();
} catch (error) {
if (error instanceof Stripe.errors.StripeAPIError) {
handleResponseFailure(error);
} else {
throw error;
}
}After:
try {
await stripe.balance.retrieve();
} catch (error) {
if (error instanceof Stripe.errors.StripeConnectionError) {
handleResponseFailure(error);
} else {
throw error;
}
}Handling for fully received API error responses and malformed JSON responses continue to throw StripeAPIError.
stripe.webhooks.signature.verifyHeader() and verifyHeaderAsync() previously skipped timestamp tolerance verification when the tolerance argument was omitted. They now default to Webhook.DEFAULT_TOLERANCE
If your integration relied on the old default of not checking the timestamp, pass 0 explicitly to preserve that behavior:
// Before: no tolerance check by default
const isValid = stripe.webhooks.signature.verifyHeader(payload, header, secret);
// After: pass 0 explicitly to skip timestamp tolerance verification
const isValid = stripe.webhooks.signature.verifyHeader(payload, header, secret, 0);Most integrations that process webhooks as they arrive should not need this opt-out and can rely on the new default tolerance. This mainly affects tests or offline processing where the delta between an event being sent and received isn't meaningful.