API Deprecation Plan Inside Your Contract
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.
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.
Deprecation:header (RFC 9745): an HTTP date indicating when the endpoint was marked deprecated. Not when it goes away. When the clock started.Sunset:header (RFC 8594): an HTTP date indicating when the endpoint will stop responding. Pick it once, do not move it casually.Link: <https://docs.example.com/migrate/v2-invoices>; rel="deprecation": point at the exact migration guide, not your docs homepage.
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:
- Ship GET /v2/invoices alongside v1. Keep v1 responding normally.
- Add
Deprecation: @1736899200,Sunset: Wed, 15 Apr 2026 00:00:00 GMT, and a Link header to /migrate/v2-invoices on every v1 response. - Instrument per-customer v1 call counts, exported daily to the CRM so the account team sees it.
- Send scheduled emails at T-12mo, T-6mo, T-3mo, T-1mo, T-2wk, T-1wk with the customer's last-30-days v1 call count.
- At T-0, v1 returns 410 Gone with a body pointing at /v2/invoices. Keep the 410 response live for at least 90 days so stragglers see a useful error, not a 404.
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.
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
- Inventory known consumers and assign an owner for each migration conversation.
- Add Sunset, Deprecation, and Link headers to the deprecated endpoint before any removal date is announced.
- Publish one migration example that shows old request, new request, old response, and new response.
- Run a synthetic test that verifies headers and migration documentation stay live through the notice window.
- Document the emergency exception path separately from the normal deprecation window.
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.
Keep Reading
Editorial Note
Last reviewed Apr 28, 2026: examples, internal links, and reusable review blocks were checked for practical specificity.
- Author details: Spec Coding Editorial Team
- Editorial policy: How we review and update articles
- Corrections: Contact the editor