Back to Blog
Engineering

How to Upgrade Your Stripe API Version Without Breaking Billing

Billy Team·September 24, 2026·3 min read

Your account's default API version is from some year you'd rather not say out loud. stripe-node is pinned because the last attempt to bump it produced four hundred TypeScript errors. Every quarter someone raises the upgrade, and every quarter it loses to "we'd have to regression-test all of billing."

1. Stripe never forces you, which is the actual problem

Stripe doesn't switch old versions off. Your integration from 2019 keeps returning 200s. No deadline, no outage — so the cost arrives as a slow tax rather than an incident, and nothing on your roadmap ever outranks it.

The tax has three parts. New Billing capabilities ship on newer versions, so you can't adopt them. Your SDK is frozen with your version — since stripe-node v12 and stripe-python v6, the library pins the API version current when it shipped, so a library upgrade is an API upgrade. And every month you wait adds another breaking change to the single sitting where you finally swallow them all.

2. What you'd be catching up on

As of 13 September 2026 the current version is 2026-08-26.dahlia. Since September 2024, Stripe ships additive monthly releases and, twice a year, a named release whose first version carries the breaking changes: Acacia (2024-09-30), Basil (2025-03-31), Clover (2025-09-30), Dahlia (2026-03-25). Every version before 2024-09-30.acacia contained breaking changes, so a long-pinned account crosses several at once.

If you're behind Basil, the expensive ones are billing-shaped:

  • current_period_start and current_period_end moved off the Subscription onto its items (2025-03-31.basil).
  • The Upcoming Invoice API was replaced by Create Preview Invoice, and legacy usage-based billing was removed (2025-03-31.basil).
  • Flexible billing mode became the default for new subscriptions, changing prorations, trials and usage handling (2025-09-30.clover).
  • Discount's coupon property gave way to source, and iterations was removed from subscription schedule phases (2025-09-30.clover).
  • Deprecated Payment Intents, Setup Intents and Sources methods were removed from Stripe.js (2026-03-25.dahlia).

Notice which of those rewrite numbers rather than field names. A proration model change doesn't throw an exception — it invoices a different amount.

3. Pin the version in code, not on the account

Before you change anything centrally, stop depending on the account default. Send Stripe-Version: 2026-08-26.dahlia on individual requests, in a sandbox or in production, and exercise the new shape one endpoint at a time. Stripe's upgrade guide recommends specifying the version you integrate against explicitly — that's the difference between an upgrade you can stage and one that lands everywhere at once.

4. Prove the money still adds up

Test clocks, now surfaced as Simulations, check invoice totals rather than status codes. Sandbox only: set a frozen_time, advance forward only, up to two billing intervals at a time, three customers and three subscriptions each, self-deleted after 30 days. That's enough to run a renewal, a mid-cycle upgrade with prorations and a trial ending against both versions, then diff the invoices.

5. Run both webhook versions side by side

Endpoints carry their own api_version, which is what makes this safe. Stripe's webhook versioning guide sets out the pattern: create a second endpoint at the same URL with a distinguishing query parameter and the same enabled_events, pinned to the new version, so every event arrives twice. Ignore the new copy with a 200, then flip — process the new one and return 400 on the old, so Stripe retries it if you need to fall back. Replay real traffic with stripe events resend evt_123 --webhook-endpoint=we_123, but only for events from the last 30 days.

6. Flip it, and keep the escape hatch

The switch itself lives in Workbench, under Overview → API versions → Upgrade available. For 72 hours afterwards you can roll back, and webhooks that failed under the new object shape are retried with the old one. Plan for one wrinkle: events you fetch later render at whatever your default was when they occurred, so historical replays stay mixed for a while.

The part nobody schedules

This stalls because the person who understands the proration math and the person who owns the deploy are rarely the same person — and checking what a subscription actually bills means someone with dashboard access reading it off. That's the job Agent Billy does: the question asked in plain language, answered from your own Stripe data, scoped to whoever asked.

Point your agent at your billing data

Billy speaks MCP, so Claude Desktop, Cursor, or anything else you use can query billing under the same roles as the dashboard.