API Deprecation Plan Inside Your Contract

API Deprecation Plan Inside Your Contract
Spec Coding Editorial Team · Spec-first engineering notes

How to build the deprecation plan into the API contract from day one: Sunset headers, deprecation timelines, customer communication, and the usage-reporting step that's actually required. If you ship the endpoint without naming how it dies, you have not finished the design.

Published on 2026-03-09 · Updated 2026-05-06 · 8 min read · Author: Spec Coding Editorial Team · Review policy: Editorial Policy

Deprecation is a launch-day concern, not a shutdown-day concern

I have never watched a team successfully deprecate an endpoint that was not planned for deprecation at launch. The ones that worked had a sunset policy written into the original contract. The ones that failed ran a six-month Slack campaign, extended the timeline twice, and still had a VP call on the day of removal because a payments integration nobody tracked went down.

The reason is structural. Once the endpoint ships, every customer who integrates treats the shape as permanent. If you did not tell them at signup that v1 has a sunset horizon and that you will measure their traffic and email them with numbers, you do not have a deprecation plan. You have a prayer.

The RFC-standard signals you should already be emitting

There are three machine-readable signals. Every deprecated response should carry all three, and your contract should commit to emitting them from the moment deprecation is announced.

If your client library does not surface these headers as warnings, your customers will miss them. I also add a Warning: 299 - "Deprecated API" header for older HTTP clients that do not parse the newer ones, and I log every deprecated call on the server side with customer ID, endpoint, and timestamp.

You cannot deprecate what you cannot measure

This is the step most teams skip, and it is the one that decides whether the plan is real. Before you announce deprecation, you need per-customer, per-endpoint call logs going back at least 90 days. Not aggregate metrics. Not a dashboard. A query you can run that returns: customer X called GET /v1/invoices 41,203 times last week, across these 7 IP addresses, with this user agent.

Without that, your T-6mo email says "you may be affected." With it, your email says "you called this endpoint 41,203 times last week and here are the exact callsites we detected." One of those gets ignored. The other gets a reply from their engineering lead within the hour.

A communication cadence with numbers attached

The default cadence I use for a public API is T-12mo, T-6mo, T-3mo, T-1mo, T-2wk, T-1wk, T-24h. Each email includes the customer's actual traffic for the last 30 days on the deprecated endpoint. The T-3mo email is the important one, because that is when integration teams have enough runway to schedule real work but not enough to procrastinate.

Internal APIs get a tighter window, 3-6 months, because you control the consumers. Partner APIs follow whatever the contract says, which means you need the sunset clause in the contract, not in a blog post. If the contract is silent, you effectively owe partners indefinite support.

Draft the migration docs before you announce anything

This sounds obvious. It is violated constantly. The migration guide must exist, be complete, and include a working code sample before the first deprecation email goes out. If the guide ships a week after the announcement, you just told customers to start planning around a target that does not exist yet, and you will hear about it.

I write the migration doc as the last step of building v2. If I cannot write a clear migration paragraph, v2 is not done. That is the check: a backend engineer on the customer's team should be able to read the doc, map old field names to new ones, and estimate the work without opening a support ticket.

Graceful sunset vs breaking change: the two-way door

A graceful sunset keeps the old endpoint returning valid data up until the sunset date. It just adds the deprecation headers. This is a two-way door: customers who miss the email still function, you can extend the timeline if a big integration is stuck, and you learn about late-mover customers through your usage logs.

A breaking change flips the endpoint to 410 Gone on the sunset date. This is a one-way door. Use it when the old endpoint is genuinely incompatible, leaks data, or has a security flaw. Do not use it because you are tired of maintaining the old handler. Tired is not an outage-worthy reason.

A concrete example: deprecating GET /v1/invoices

Say v2 changes pagination from offset-based to cursor-based, which is a breaking change to the request contract. The plan:

Acceptance criteria

- Given the v1 endpoint is in its deprecation window
  When any customer calls GET /v1/invoices
  Then the response includes Deprecation, Sunset, and Link: rel="deprecation" headers
  And the call is logged with customer ID, timestamp, and user agent
  And that customer appears in the next scheduled deprecation email with their call count

- Given the sunset date has passed
  When any customer calls GET /v1/invoices
  Then the response is 410 Gone
  And the body includes a link to the v2 migration guide
  And the call is still logged for the post-sunset stragglers report

Testing the deprecation itself

Synthetic tests should verify the headers are present on every deprecated endpoint, the Sunset date matches the announced date exactly, and the migration docs linked in the Link header return 200. I have seen the migration URL 404 in production because a docs repo was restructured after the deprecation launched. The synthetic test catches that in a minute. A customer email catches it a week later and costs you trust.

When to break the policy

Security issues justify shorter timelines. A credential leak in the old endpoint does not get 12 months. A vulnerability that permits account takeover can legitimately be a 72-hour sunset. Write this exception into the policy explicitly so the security team does not have to argue for it during an incident. "Security override: sunset can be as short as 24 hours with VP of Engineering sign-off" is the kind of clause that makes the policy real instead of aspirational.

Contract Review Packet to Copy

Use this when the work touches API behavior, schema, events, retries, or consumer expectations. The packet makes compatibility and release evidence explicit.

API contract review packet: API Deprecation Plan Inside Your Contract

Decision to make:
- How to build the deprecation plan into the API contract from day one: Sunset headers, deprecation timelines, customer communication, and the usage-reporting step that's actually required.

Owner check:
- Product owner:
- Engineering owner:
- QA or operations reviewer:

Scope boundary:
- In scope:
- Out of scope:
- Assumption that still needs approval:

Acceptance evidence:
- Test or fixture:
- Log, metric, or screenshot:
- Manual review step:

Contract boundary: no release without compatibility classification, consumer impact, retry behavior, and rollback notes.

Reviewer prompt:
- What would still be ambiguous to someone who missed the planning meeting?
- What evidence would make this safe enough to ship?

Editorial Review Note

Reviewed Apr 28, 2026. This update added a reusable artifact, checked the article against the related topic hub, and tightened the next-step links so the page works as a practical reference rather than a standalone essay.

Keywords: API deprecation · Sunset header · RFC 8594 · RFC 9745 · API versioning · migration guide · API contract

Deprecation Readiness Checklist

Before announcing removal, turn the contract plan into a reviewable checklist. The checklist should prove that consumers can discover the change, migrate in time, and recover if the cutoff exposes a missed dependency.

Download: api-deprecation-plan-template.md

Evidence to Track

The useful evidence is consumer movement, not whether the deprecation document looks complete. Track traffic on old endpoints, support tickets from affected clients, migration-guide views, failed requests after warning headers launch, and any consumer that needs an explicit extension.

Topic Path

This article belongs to the API Contracts track. Start with the hub, then use the checklist, template, or tool below on a real project.

Editorial Note

Last reviewed Apr 28, 2026: examples, internal links, and reusable review blocks were checked for practical specificity.