Additions arrive quietly, nothing you rely on disappears without warning, and when a route has to change shape it gets a new version next to the old one. This page is the contract behind that sentence.

The version is in the path

Every route carries its major version as the first path segment: /v1/..., /v2/.... There is no version header and no per-account version setting. The version you call is the version you get. Versions are per route, not per API. A new major version is introduced for the routes that need one; the rest stay where they are. One integration calls routes on different versions with the same token, so there is nothing to upgrade all at once. Where a route exists on two versions, the reference page for the older one names the newer one. Use the newest version for new work. The older version is unchanged and stays supported: a new version never retires the old one by itself.

What changes without a new version

Within a version we make additive changes without notice. Build your client to tolerate them: Field order in JSON is never significant.

What counts as a breaking change

Anything that could make a correct integration stop working:
  • Removing or renaming an endpoint, a response field, or a request field.
  • Changing a field’s type or format: a string becoming a number, a timestamp changing shape, a pattern tightening.
  • Making an optional request field, parameter, or header required.
  • Removing a value from an enum, or changing what an existing value means.
  • Removing an error response, or changing the HTTP status of an existing outcome.
  • Changing how authentication or step-up works on an existing route.
We don’t ship these inside an existing version. A breaking change arrives one of two ways:
  1. A new version of the route. The old version keeps working unchanged.
  2. Deprecation and removal, following the process below, for something that has a replacement and shouldn’t be needed any more.
Idempotency enforcement is a breaking change by this definition. The Idempotency-Key header is optional today. Rejecting requests without one is a tightening, so it will be announced with the notice period below before it is enforced, not switched on quietly.

Deprecation

When a route or field is going away:
  1. It’s marked. Deprecated endpoints and fields are flagged deprecated in the API Reference, and the page names the replacement.
  2. It keeps working. A deprecated route behaves exactly as before for the whole notice period. Deprecation changes the docs, not the wire.
  3. You hear about it. We email your program’s technical contact when the deprecation starts, and again before removal.
  4. It stays for at least six months. Nothing is removed less than six months after it was marked deprecated. If your program is still calling it near the end of that period, we contact you before we switch it off.
Deprecations in effect are visible in the reference, not listed here.

Features that are off for your program

Not every route is available to every program, and that is not a versioning matter. A feature your program doesn’t have returns 501 not_available; the Program capabilities page tells you in advance which ones those are. A feature being turned on for your program is a change to your program, not to the API.

Build for this

Three habits make an integration safe against everything above:
  • Ignore what you don’t recognise: extra fields, new enum values, new event types.
  • Switch on status and code, never on message.
  • Read the deprecation emails, and move to the named replacement when a page you use says deprecated.