Versioning and deprecating integrations without breaking customers
How to version and deprecate integrations without breaking customers. Version strategies, deprecation notices, migration windows, and the communication that keeps partners building on you.
The fastest way to lose a partner you spent months winning is to break their integration without warning. Not with a bad feature or a slow response, but with a quiet change to a field name, a stricter validation rule, or a removed endpoint that worked yesterday and returns an error today. The partner's integration goes down, their customers notice before they do, and the trust you built over a long sales cycle evaporates in the time it takes to read a stack trace. Versioning is how you keep evolving your API without ever putting a partner in that position.
This is not a problem you can avoid by never changing anything. Every API that lives long enough has to change: new fields, better defaults, corrected mistakes, removed cruft. The question is never whether you will change the contract, only whether you will do it in a way that gives the people who built on you time to follow. Good versioning is a promise to your partners that change will be predictable, announced, and survivable. This guide covers the strategies for versioning an integration surface, how to write a deprecation notice that actually reaches the right people, and how to run a migration window that ends with everyone moved over instead of half your partners on fire.
It pairs with our guide to a partner-ready API, where a changelog and versioning policy are part of what partner engineers check before they commit. This post goes deep on the versioning and deprecation half of that story.
The 60-second version
- Breaking changes are the enemy, not all changes. Additive changes, new fields, new endpoints, new optional parameters, are safe. Removing or renaming anything a partner depends on is what breaks them.
- Pick a versioning strategy and apply it consistently: URL path versions, header versions, or date-based versions each have tradeoffs, but the cardinal rule is to never change the meaning of an existing version.
- Follow a clear contract for what a version number means. Semantic versioning gives you a shared vocabulary for "this is safe" versus "this will break you."
- A deprecation is a process, not an announcement. It has a notice, a migration window, reminders, and a sunset date, with telemetry behind each step.
- Migration windows are measured in months, not weeks, for anything partners run in production. Long enough that a busy partner team can fit the work into a real roadmap.
- Communicate in more than one channel. A changelog entry alone does not reach the engineer who built the integration two years ago and never looks at your changelog.
- Track who is still on the old version and reach out to the stragglers directly before you turn anything off.
Breaking versus non-breaking: the distinction that drives everything
Before any strategy, get the core distinction right, because it determines when you even need a new version. A change is non-breaking if every existing integration keeps working without anyone touching their code. A change is breaking if it forces a partner to change their code to keep functioning. The whole discipline of versioning is about isolating breaking changes so they never land on a partner by surprise.
These changes are safe to ship to an existing version, because a correctly built client ignores what it does not expect:
- Adding a new field to a response. Existing clients read the fields they know and skip the rest.
- Adding a new optional request parameter. Clients that omit it get the old behavior.
- Adding a new endpoint or a new event type. Nobody depends on something they have never called.
- Adding a new optional value to an enum, as long as old clients are not promised an exhaustive set.
These changes are breaking, and they need a new version or a long migration:
- Removing or renaming a field, endpoint, or parameter. Anything a client reads or sends can break if it moves.
- Changing a field's type or format, like turning a string into an object or changing a date format.
- Making an optional parameter required, or tightening validation so previously accepted requests now fail.
- Changing a default value or the meaning of an existing field. This is the most dangerous kind, because it breaks clients silently, with no error, just wrong behavior.
| Change | Breaking? | What to do |
|---|---|---|
| Add a response field | No | Ship it to the current version |
| Add an optional request parameter | No | Ship it to the current version |
| Add an endpoint or event type | No | Ship it to the current version |
| Remove or rename a field | Yes | New version, deprecate the old |
| Change a field's type or format | Yes | New version, deprecate the old |
| Tighten validation or make a field required | Yes | New version, or a long announced window |
| Change a default or a field's meaning | Yes, and silent | New version, communicate loudly |
The rule that prevents most disasters is simple: never change the meaning of an existing version. Once partners build against version one, version one keeps behaving exactly as it always did, forever or until you sunset it on an announced schedule. New behavior goes into new versions or into additive changes that no one has to react to.
Version strategies: URL, header, and date-based
There are three common ways to express a version, and reasonable teams disagree on which is best. What matters more than the choice is consistency and a clear rule for what triggers a new version.
Version in the URL path. The version is part of the address, like /v1/contacts and later /v2/contacts. This is the most visible and the easiest for a partner to reason about: the version they are using is right there in every request, and switching is an obvious, deliberate act. The downside is that a major version bump touches every path, which nudges teams toward large, infrequent version jumps rather than small ones. It is the most common choice for partner-facing APIs precisely because it is hard to use by accident.
Version in a header. The client sends a version in a request header, and the URL stays clean. This keeps resource addresses stable and lets you version more granularly, but it is less discoverable: a partner debugging a request will not see the version unless they inspect headers, and a forgotten default header is a class of subtle bug. It suits APIs with many resources where path versioning would be unwieldy.
Date-based versions. The client pins a date, like 2026-06-01, often via a header or account setting, and gets the API as it behaved on that date. New changes ship under new dated versions, and a partner upgrades by moving their pinned date forward and testing. This makes every change, however small, into a version a partner can opt into deliberately, which is excellent for a frequently evolving API. The cost is operational: you maintain transformation layers that can serve older dated behavior, which is real engineering work.
| Strategy | Visibility | Granularity | Cost to maintain | Good fit |
|---|---|---|---|---|
| URL path | High, in every request | Coarse, major versions | Moderate | Most partner APIs |
| Header | Low, hidden in headers | Fine | Moderate | Many-resource APIs |
| Date-based | Medium, an opt-in date | Very fine | Higher | Fast-moving APIs |
Whichever you choose, document it as policy, not as folklore. A partner engineer should be able to read one page that says how you version, what counts as a breaking change, how long old versions live, and where deprecations are announced. That page is part of being partner-ready, and its absence is something partner engineers notice and price into their estimate.
What a version number should promise
A version is only useful if everyone agrees on what its parts mean. The widely adopted convention is semantic versioning, which structures a version as major.minor.patch and assigns each part a precise meaning: a major bump signals a breaking change, a minor bump adds functionality in a backward-compatible way, and a patch fixes a bug without changing the contract. The full specification is published at semver.org, and adopting it gives you and your partners a shared vocabulary so that "we are releasing 2.0" carries real, agreed information rather than marketing vibes.
The practical value for an integration surface is that a partner can read a version change and know, before reading a single line of release notes, whether they are obligated to do anything. A minor or patch release means they can upgrade freely. A major release means there is migration work and a window in which to do it. That predictability is itself a feature. It is the difference between a partner who upgrades on your schedule and a partner who freezes on an old version because they no longer trust that your "small update" will not break them.
Even if you do not surface a three-part number to partners, adopt its discipline internally. The categories, breaking, additive, fix, are exactly the categories that decide whether a change needs a new version and a deprecation, or can ship quietly to everyone. Getting that classification right at the moment of change is what keeps breaking changes from leaking into a version partners thought was stable. Our guide to API error design covers the related discipline of changing error responses carefully, since error shapes are part of the contract too and a quietly changed error body breaks clients that parse it.
Deprecation is a process, not an announcement
Deciding to remove something old is the easy part. Doing it without breaking anyone is a process with several stages, each of which exists because skipping it strands some partners.
1. Announce the deprecation with a sunset date. The moment you decide to retire a version or endpoint, say so, and commit to a specific date it will stop working. A deprecation without a date is ignored, because there is no deadline to plan around. A date turns it into a roadmap item.
2. Signal deprecation in the responses themselves. Beyond the changelog, mark deprecated responses programmatically so a partner's monitoring can catch it. The HTTP Sunset header, defined in RFC 8594, carries the date a resource will become unresponsive, and a Deprecation header can flag that an endpoint is on its way out. A partner who logs these gets warned by their own systems, even if no human read your announcement.
3. Provide a migration path before you start the clock. Never deprecate version one until version two exists, is documented, and a partner can actually move to it. Deprecating something with no replacement ready is just telling partners their integration is going to break with no way to prevent it. The migration guide, what changed, what to do, with examples, should ship with the deprecation, not after.
4. Remind, escalating as the date approaches. One announcement is not enough. Send reminders on a schedule, and make them more direct as the sunset nears: a heads-up at announcement, a reminder at the halfway point, a more urgent one a month out, and a final warning in the last week. Each reminder should say exactly what will stop working and link the migration guide.
5. Sunset, and have a rollback plan. On the date, turn it off, but watch closely and be ready to extend if telemetry shows a meaningful number of partners still on the old version. A sunset that takes down three major partners because they missed every notice is a worse outcome than a two-week extension. The goal is migration, not a hard deadline for its own sake.
| Stage | Purpose | Without it |
|---|---|---|
| Announce with a date | Give partners a deadline to plan around | Deprecation gets ignored |
| Signal in responses | Let partner monitoring catch it automatically | Only changelog readers find out |
| Ship the migration path first | Make the move actually possible | Partners stranded with no replacement |
| Escalating reminders | Reach people who missed the first notice | Late partners get surprised |
| Sunset with a rollback plan | Avoid taking down stragglers | A clean date becomes an incident |
How long should a migration window be?
The honest answer is longer than you want it to be. From the partner's side, migrating an integration is unplanned work that competes with their own roadmap, and the engineer who built it may have moved teams or companies. A window that feels generous from inside your company often looks tight from inside theirs.
A few guidelines that hold up:
- Measure production-facing migration windows in months, not weeks. Anything partners run in production for real customers deserves a window long enough to fit into a normal planning cycle. Many mature platforms give six months or more for major breaking changes, and that length is a feature, not a weakness.
- Scale the window to the blast radius. Removing an obscure optional field that almost no one uses is a different magnitude from changing the auth flow every integration depends on. Bigger and more central changes deserve longer windows and louder communication.
- Run both versions in parallel for the whole window. The old and new versions should both work, side by side, for the entire migration period. This is what lets a partner move at their own pace and roll back if their migration hits a snag. Parallel operation is the cost of not breaking people, and it is worth paying.
- Do not start the clock until the replacement is solid. The window begins when partners can actually migrate, which means the new version is documented, stable, and not itself changing under them. A window that overlaps with a still-shifting new version is shorter than it looks.
The throttling concern is worth a note. If a migration involves partners re-running large backfills against the new version, expect a load spike, and make sure your rate limiting handles it gracefully rather than meeting a migrating partner with a wall of 429 Too Many Requests responses. A migration window is the wrong time to surprise a partner with limits they have never hit before.
Communicate so the right person actually hears it
The most carefully planned deprecation fails if the announcement does not reach the person who can act on it. The engineer who built the integration two years ago does not read your changelog every morning, and the partner manager who gets your email may not know which of their company's integrations is affected. Reaching the right person is a multi-channel problem.
- Changelog and docs. The system of record. Every deprecation lives here with its date and migration guide, but treat this as the floor, not the whole effort.
- Direct email to integration owners. If you know which accounts use the affected version, email the technical contacts on those accounts specifically. A targeted "your integration uses an endpoint that sunsets on this date" beats any broadcast.
- In-response signals. The
SunsetandDeprecationheaders reach the integration itself, so a partner's logging and alerting can surface the warning even when no human read the email. This is the channel that catches the silent, long-running integration. - Dashboard or portal notices. If partners have a developer dashboard, surface active deprecations there, ideally scoped to the versions that partner is actually using.
- Direct outreach to the stragglers. Near the sunset date, look at who is still calling the old version and contact them directly, one to one. This is the step that prevents the post-sunset incident, and it is only possible if you have the telemetry to know who they are.
That last point depends on instrumentation. You need to know, at any moment, how much traffic each version is getting and which partners are behind it. Without that, you are sunsetting blind, hoping no one important still depends on the thing you are about to remove. Tracking adoption and usage per version is the same discipline we cover in integration adoption metrics, applied to the version dimension: you cannot safely retire what you cannot see.
Versioning across a partner portfolio
As your integration surface grows, versioning stops being a per-endpoint decision and becomes a portfolio policy. Partners who build on several of your APIs, or who use several of your connectors, should encounter one coherent versioning model, not a different scheme and a different deprecation cadence for each surface. A partner who learned how you version one product should not have to relearn it for the next.
That consistency is part of treating your integrations as a designed system rather than a pile of independent projects. It also feeds the trust that makes partners willing to build on you in the first place. A platform with a published versioning policy, generous migration windows, and a track record of never breaking anyone without warning is one engineers recommend internally. A platform that has broken integrations before, even once, gets a permanent risk premium in every future estimate. The reliability you build into your versioning is reputation you spend, or save, on every partnership conversation after, which is the throughline of our guide to a partner-ready API.
Common mistakes, and the fix
Changing the meaning of an existing version. The fix: treat a shipped version as frozen. New behavior goes in additive changes or a new version, never as a quiet redefinition of something partners already built against. Silent meaning changes are the worst kind because they break clients with no error.
Deprecating without a migration path ready. The fix: ship the replacement, its docs, and a migration guide before the deprecation clock starts. Announcing a sunset with nowhere to go is just scheduling an outage for your partners.
Migration windows measured in weeks. The fix: give production-facing breaking changes months, scaled to blast radius, with both versions running in parallel the whole time. Partners need room to fit unplanned migration work into a real roadmap.
Announcing once, in one channel. The fix: use the changelog, targeted email, in-response Sunset and Deprecation headers, dashboard notices, and direct outreach to stragglers. The engineer who needs to act may never see your changelog.
Sunsetting without knowing who is still on the old version. The fix: instrument usage per version, watch adoption move, and contact the remaining holdouts directly before the date. A sunset run blind is an incident waiting for its trigger.
Hiding deprecation only in prose. The fix: signal it programmatically with the Sunset and Deprecation headers so partner monitoring can catch it. Machine-readable warnings reach integrations that no human is actively watching.
FAQ
What counts as a breaking change in an API? Any change that forces an existing client to modify its code to keep working: removing or renaming a field, endpoint, or parameter; changing a type or format; tightening validation; making an optional field required; or changing a default or the meaning of an existing field. Additive changes like new fields or new endpoints are not breaking.
Should I version in the URL or in a header? Both work; pick one and apply it consistently. URL path versioning is the most visible and hardest to use by accident, which is why it is common for partner APIs. Header and date-based versioning allow finer granularity at the cost of discoverability. The choice matters less than documenting your policy and never redefining an existing version.
How long should a deprecation or migration window be? For anything partners run in production, measure it in months, not weeks, scaled to how disruptive the change is. Many mature platforms give six months or more for major breaking changes, run both versions in parallel for the whole window, and only start the clock once a stable replacement and migration guide exist.
How do I tell partners an endpoint is deprecated? Use multiple channels: a dated changelog entry with a migration guide, the HTTP Sunset and Deprecation headers so their monitoring catches it, targeted email to the technical contacts on affected accounts, dashboard notices, and direct outreach to whoever is still on the old version as the date nears.
What is the Sunset header? The Sunset HTTP response header, defined in RFC 8594, communicates the date and time a resource is expected to become unresponsive. Returning it on deprecated endpoints lets a partner's own systems detect and warn about the upcoming removal without anyone reading your announcement.
Can I just keep the old version running forever instead of deprecating? You can for a while, but every live version is maintenance, security, and testing surface, and old versions accumulate. Deprecating on an announced, generous schedule, with full communication, is how you retire that cost without breaking anyone. The goal is controlled retirement, not indefinite support of everything you ever shipped.
How do I know if it is safe to sunset a version? Instrument traffic per version so you can see how many requests, and which partners, still depend on it. When usage has dropped to a small set of identifiable holdouts you have contacted directly, it is safe. Sunsetting without that telemetry is guessing, and the guess that is wrong becomes an incident.
Further reading
- Semantic Versioning for a shared vocabulary that tells partners whether a release is safe or breaking.
- RFC 8594, the Sunset HTTP header for signaling a resource's retirement date in the response itself.
- The OpenAPI Initiative for describing each version of your API in a spec partners can diff and build against.
- MDN on 429 Too Many Requests for handling the load spikes a migration can create.
The short version
You will change your API; the only question is whether partners survive it. Isolate breaking changes from additive ones, because additive changes ship freely and breaking ones need a new version. Pick a versioning strategy, URL path, header, or date-based, and apply it consistently, and never change the meaning of a version partners already built against. Treat deprecation as a process: announce with a sunset date, signal it in responses with the Sunset and Deprecation headers, ship the migration path first, remind on an escalating schedule, and sunset only after you have moved the stragglers over. Make migration windows months long, run both versions in parallel, and instrument usage so you always know who is still on the old version.
Do this and partners keep building on you with confidence, because they trust that change will be predictable and survivable. Skip it and the first surprise breakage teaches every partner to treat your platform as a risk, a reputation that costs you on every deal after.
If you want help designing a versioning and deprecation policy that lets you keep shipping without breaking the partners you worked to win, that is exactly what a Partner Audit is for. We review your product, API, and partner potential, then define what to build, who to approach, and how to ship it.