跳转到内容

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).


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.


Every request targets exactly one API version. Resolution order (from the 2017 write-up, still the model):

  1. Stripe-Version header (or SDK equivalent) if set — per-request or per-client override for testing upgrades.
  2. OAuth app version when acting on a user’s behalf.
  3. 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.


  • 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.”

Documented in Introducing Stripe’s new API release process:

CadenceName shapeBreaking?
~Twice yearly majorPlant name (Acacia, Dahlia, …) on a dated versionYes — plan code changes
Monthly under that majorSame 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:

  1. Implement API resources for the current (newest) shape.
  2. Encapsulate each breaking change as a version-change module (docs + transform + which resource types it touches).
  3. 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.


  • Prefer pinning the version in code (Stripe-Version / SDK apiVersion) 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.

Webhook (and snapshot event destination) API version is independent of the server SDK version (webhook versioning).

Safe upgrade pattern Stripe documents:

  1. Create a second endpoint (same URL + query flag, or separate destination) pointed at the new api_version / snapshot_api_version, initially disabled or ignored.
  2. 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.
  3. 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.


Stripe practiceSteal for your platform
Pin consumers to a version on first useDon’t surprise existing clients with shape changes
Small / scheduled breaking trains + additive monthlyPredictable upgrade cost vs “big bang v2”
Explicit BC allowlistDocument what clients may assume
Transform-back version modulesKeep “current” code clean; isolate legacy
Header override + sandboxTest upgrades before flipping the default
Version webhooks separatelyEvent payloads are part of the contract
Keep /v1 stable; version the contractPath majors are expensive for everyone