Stripe API backward compatibility
Stripe API backward compatibility
Section titled “Stripe API backward compatibility”Primary sources: Upgrade your integration · API versioning · APIs as infrastructure (2017) · New API release process (2024) · SDK versioning · Webhook versioning · Changelog
Pin checked 2026-09-28: docs list current as 2026-08-26.dahlia (monthly under the Dahlia major train).
The contract
Section titled “The contract”Stripe treats the HTTP API like long-lived infrastructure: fields that existed stay, types and names stay stable for a given version, and integrations should not break because Stripe shipped a feature. Compatibility with every version since 2011 is an explicit design goal (2017 blog).
The public path prefix stays /v1/.... Rolling dated (and now dated + plant-name) versions carry the real contract; a /v2 path bump is reserved and has not been the upgrade vehicle.
How a request picks a version
Section titled “How a request picks a version”Every request targets exactly one API version. Resolution order (from the 2017 write-up, still the model):
Stripe-Versionheader (or SDK equivalent) if set — per-request or per-client override for testing upgrades.- OAuth app version when acting on a user’s behalf.
- Otherwise the account’s pinned default (Dashboard / Workbench).
First successful API request on an account pins that account to the then-current version. Later majors do not silently rewrite the account’s default. That is the main safety net against accidental breaking changes.
Two eras of release shape
Section titled “Two eras of release shape”Historical (pre-Acacia)
Section titled “Historical (pre-Acacia)”- New version whenever a breaking change shipped.
- Versions named by date (
2017-05-24, …). - Small, frequent incompatibilities — upgrades meant to be incremental, not “rewrite the integration.”
Current (from 2024-09-30.acacia)
Section titled “Current (from 2024-09-30.acacia)”Documented in Introducing Stripe’s new API release process:
| Cadence | Name shape | Breaking? |
|---|---|---|
| ~Twice yearly major | Plant name (Acacia, Dahlia, …) on a dated version | Yes — plan code changes |
| Monthly under that major | Same plant suffix (….dahlia) | No — safe to move within the train |
Out-of-cycle breaking releases are possible but rare; they get a new plant name so the train boundary stays obvious.
Each API release ships matching SDK builds and changelog entries. SDK packages still use semver; each SDK release is tied to a specific API version.
What Stripe calls “backward-compatible”
Section titled “What Stripe calls “backward-compatible””From docs/upgrades — these do not require a new major / do not break pinned clients:
- New API resources
- New optional request parameters
- New properties on existing responses
- Reordering properties in responses
- Changing length/format of opaque strings (IDs, messages), including ID prefixes (
ch_, …) — store IDs in flexible columns (e.g.VARCHAR(255)case-sensitive) - New event types — webhook handlers must ignore unknown types
Anything outside that list (remove/rename/retype a field, change required params, change semantics of an existing field, etc.) is a breaking change and lands in a major release (or a new dated version in the old scheme).
Under the hood: rewrite responses, don’t fork the product
Section titled “Under the hood: rewrite responses, don’t fork the product”Core idea from Brandur Leach’s APIs as infrastructure:
- Implement API resources for the current (newest) shape.
- Encapsulate each breaking change as a version-change module (docs + transform + which resource types it touches).
- On response generation: build the newest representation, then walk backward through version modules until the request’s target version is reached.
So old clients see old shapes without the product code carrying forever-branching “if version < X” logic in the hot path. Modules that cannot be pure response transforms are marked has_side_effects and checked explicitly elsewhere — Stripe tries to minimize those.
Changelog and per-user API reference warnings are partly generated from those modules, so versioning stays first-class in docs and tooling.
Design discipline: API review before shipping; prefer additive design so version modules stay rare.
Client / SDK practice
Section titled “Client / SDK practice”- Prefer pinning the version in code (
Stripe-Version/ SDKapiVersion) over relying only on the account default — upgrades become intentional. - Dynamic SDKs (Node, Python, Ruby, PHP, …): set version globally or per request; recent majors often default to the API version current when that SDK package was released.
- Strongly typed SDKs (Go, Java, .NET): API version is fixed to the SDK release. Jumping API versions usually means upgrading/downgrading the package so types match payloads.
- Test a candidate version with the header (or a sandbox with a different default) before changing the account pin in Workbench.
Webhooks and event destinations
Section titled “Webhooks and event destinations”Webhook (and snapshot event destination) API version is independent of the server SDK version (webhook versioning).
Safe upgrade pattern Stripe documents:
- Create a second endpoint (same URL + query flag, or separate destination) pointed at the new
api_version/snapshot_api_version, initially disabled or ignored. - Dual-deliver: ignore new-version events until handlers are ready; then process new and return 400 on old so Stripe can retry if you revert.
- Cut over; disable the old endpoint.
Within one major plant train (post-Acacia), monthly webhook upgrades stay BC; crossing a major (e.g. into Acacia / Dahlia) may need the dual-endpoint dance.
Thin events (private preview for v1 resources) aim to reduce version churn on webhook config — payloads described as unversioned relative to snapshot events.
Takeaways for designing your own API
Section titled “Takeaways for designing your own API”| Stripe practice | Steal for your platform |
|---|---|
| Pin consumers to a version on first use | Don’t surprise existing clients with shape changes |
| Small / scheduled breaking trains + additive monthly | Predictable upgrade cost vs “big bang v2” |
| Explicit BC allowlist | Document what clients may assume |
| Transform-back version modules | Keep “current” code clean; isolate legacy |
| Header override + sandbox | Test upgrades before flipping the default |
| Version webhooks separately | Event payloads are part of the contract |
Keep /v1 stable; version the contract | Path majors are expensive for everyone |
Related
Section titled “Related”- 2026-09-28 Effect.ts for TypeScript services — typed error channels when your own APIs evolve
- Stripe Developer changelog for concrete break lists per version