River
Y CombinatorBacked by Y Combinator
FREE TEMPLATE

API Deprecation Policy Template

Five documents and five sheets, including the join that says which of your affected callers has an engineer you can write to at all.

Free download  ·  No account needed

The plan is always the same shape. Pick a notice period, email every customer, add a deprecation header, wait. The header is worth adding and it is also inert: RFC 9745 states that the act of deprecation does not change any behavior of the resource, and treats the field as a hint rather than a guarantee. Nothing about announcing a retirement makes a caller move. The two things you actually control are who you tell and when the date is, and both are usually decided without looking at any data.

Start with who. In the worked example inside this pack, 96 of 1,340 accounts had called a deprecated endpoint in ninety days, so the drafted broadcast would have reached 1,244 people it did not concern. Then the join almost nobody runs: a caller is an API key and a recipient is a person. Fifty-eight of those 96 accounts, carrying 40 percent of the traffic, had nobody an engineer could write to, and three of the seven largest were reachable only through a billing address.

Then the date. The standards already separate the two stages, and RFC 8594 says the Sunset header is not appropriate for the first one because the API remains operational, but neither standard tells you how long the second stage should be. Measured against how often each caller's client version actually changed, the drafted ninety days contained 0.38 of a release cycle for the caller at the 90th percentile. The pack derives the window from that number instead of picking one.

The accounts you cannot reach, and the notice that moved nothing

Three sheets from one retirement. The affected list, what each endpoint can honestly be promised, and what each notice actually did to the traffic.

Caller Reachability

Illustrative rows for a fictional shipping-rate API, Ardleigh, retiring v2. Weighted by call volume rather than by account count, because those two rank the list differently.

Reachability stateAccountsv2 calls, 90dShareChannel that reaches themWhat it costs
Named technical contact3824,700,00060.0%One mail naming their endpoints and volumesOne email
Billing contact only278,100,00019.7%Account manager carrying the endpoint listA conversation per account
Shared alias194,300,00010.4%A ticket raised into their own support queueA ticket plus follow-up
No contact record at all124,100,00010.0%Nothing. No message reaches themA scheduled brownout
NO NAMED ENGINEER58 of 9616,500,00040.0%3 of the 7 largest callers sit here58 contacts to obtain

This is the join that reorders the programme. Every deprecation plan assumes the affected list and the mailing list are the same list. They are not. Forty percent of the traffic here belongs to accounts where the only address on file reaches a finance team, a no-reply alias, or nobody, and no amount of rewriting the notice changes that.

The bottom row is a work item, not a statistic. Obtaining 58 contacts has a duration, and it happens before the sequence can run rather than during it. A timeline that skips this step is measuring from the wrong start date.

After a five-minute brownout announced three weeks ahead, nine of the twelve silent accounts raised a support ticket within the hour and produced a named engineer. The remaining three stopped being an engineering problem and became a commercial one.

Endpoint Inventory

Retiring a version sounds like one decision. It was 34, and they split three ways.

EndpointCalls, 90dAccountsStatev3 equivalentIn the published date
POST /v2/rates/quote18,400,00071livePOST /v3/ratesyes
POST /v2/address/validate9,100,00064livePOST /v3/addresses/validateyes
POST /v2/shipments3,300,00029livePOST /v3/shipmentsyes
POST /v2/labels/generate412,0009blockednone existsno, and not on a roadmap
POST /v2/manifests84,0004blockednone existsno, and not on a roadmap
GET /v2/tracking/legacy16,0003blockedGET /v3/trackingno, missing three carrier codes
23 further endpoints00deadvariousretired on a changelog line
TOTAL41,200,0009611 live, 23 dead7 with a successor7 endpoints

Two thirds of the surface left for free. Twenty-three endpoints had no calls in ninety days, so there was nobody to notify and no migration guide to write. Ninety days is the floor for that window rather than a preference, because monthly and quarterly batch jobs exist and a thirty-day sample reports a live endpoint as dead.

The blocked rows are why sunsets slip in public. Four endpoints had live traffic and nowhere to send it, and three of those were on nobody’s roadmap. That is not a deprecation problem, it is unfunded product work standing in front of a retirement, and it gets a dated commitment of its own rather than a place inside the announcement.

The published date covered seven endpoints. Announcing all eleven would have been announcing a date the API could not meet.

Sunset Timeline

Every step carries the traffic before it and the traffic thirty days after. That column is the only evidence a notice did anything.

DateStepAudienceTraffic beforeAfter 30dChange
2025-11-03Deprecation header liveall callers41,200,00041,200,0000%
2025-12-15Notice 1, broadcast email1,340 accounts41,200,00039,923,000-3.1%
2026-01-1223 dead endpoints retirednobody00no impact
2026-01-19Notice 2, named engineers38 accounts28,723,00016,947,000-41%
2026-01-26Notice 3, account managers46 accounts11,200,00011,155,000-0.4%
2026-02-25Five-minute brownout12 unreachable4,100,000scheduled9 contacts obtained
2026-09-30Retirement of 7 endpointswhoever is leftplannedplannedplanned

Rows two and four are the same month and the same product. A broadcast to 1,340 accounts moved 3.1 percent of traffic and shifted six accounts of the 96. A mail to 38 named engineers, each naming that account’s own endpoints and its call volume, moved 41 percent of theirs while everybody else moved 0.4 percent.

So escalation is by channel, never by wording. A second identical email to somebody who ignored the first has a measured effect of roughly nothing, and it spends the credibility the later steps need. Broadcast, then named engineer, then the commercial relationship, then a brownout.

The nine-month window came from a 90th-percentile caller cadence of 34 weeks plus a six-week build estimate, and two of the seven largest callers fall outside it and hold dated bilateral plans instead.

What's in the pack

01

Usage by Client and Version

One row per caller with the endpoints it hits, its ninety-day volume, the client version it presents and how often that version changes.

02

Caller Reachability

The join nobody runs, sorting every affected account into four states weighted by call volume, with the channel and the cost of reaching each.

03

Endpoint Inventory

Every endpoint marked dead, live with a successor, or live with none, so the published date covers only what has somewhere to go.

04

Migration Status Tracker

Traffic per account before and after each notice, split into moved, partially moved and did not move, since only the third list is work.

05

Sunset Timeline

Every step dated, with a traffic-after column, so a notice is scored on what it changed rather than on how many people it reached.

06

Deprecation Policy

The customer-facing document, with the notice period stated alongside the derivation behind it, which is what survives the first extension request.

07

Notice Sequence and Migration Guide

Four channels, each aimed at whoever the last one failed to move, and a guide built from complete request and response pairs rather than descriptions of the difference.

How to use it

  1. 1

    Open in River, or take it blank

    Send the pack your API access logs and endpoint inventory inside River, or download the five documents and five sheets and work through them without an account.

  2. 2

    Build the list from traffic

    Ninety days of logs, resolved to accounts. The affected count is usually a small fraction of the customers somebody was about to email.

  3. 3

    Find out who you can reach

    Cross those accounts against contact records before drafting anything, because for some of them the honest answer is that no message arrives.

  4. 4

    Derive the window, then measure

    Set the date from observed caller release cadence, then score each notice on the traffic it moved and escalate the channel for whoever did not.

Frequently asked questions

Is this template free?

Yes, and the download needs no account and no card. Edit with AI is the optional half: it reads the access logs and contact records you send, builds the affected list, and runs the reachability join. Every other pack sits in the template library.

What format are the downloaded files?

Five documents as Word files and five sheets as CSVs, in one zip. The sheets open in Excel, Numbers or Google Sheets with the columns already ordered, and the documents open in Word or Pages. Nothing needs converting to be readable.

We already send a deprecation header. Is that not enough?

It is worth sending and it will not move anybody. RFC 9745 is explicit that a deprecated resource behaves exactly as it did before and that the field is a hint rather than a guarantee. A signal that changes nothing has no forcing function, which is why the sequence escalates by channel.

How long should the notice period be?

However long it takes your slowest significant caller to ship once, plus the migration work. That is measurable from the client version in their traffic. The drafted ninety days in the worked example contained 0.38 of a release cycle for the caller at the 90th percentile, which made it impossible rather than tight.

Is a brownout not just breaking customers deliberately?

It is the only channel left for an account with no contact of any kind, and it is announced in the changelog and in response headers weeks ahead. Five minutes produced nine named engineers from twelve silent accounts. Retiring an endpoint on somebody with no warning is the thing this avoids.

What if the replacement is not ready?

Then that endpoint is not in the published sunset. Four of Ardleigh's eleven live endpoints had no v3 equivalent, so the announcement covered the other seven and the four got a dated commitment to build the replacement first. Announcing all eleven would have been announcing a date the API could not meet.

What else belongs alongside this?

The reference the migration guide sits beside and the trail that records each change. The API documentation pack covers the error reference and the examples, and release notes from commits turns a merge history into what actually changed for a caller. The integration guide is what the affected partners were following before this version needed retiring.

Find out who actually calls the version you are retiring

Take the Word documents and CSV sheets blank, or open this exact pack in River and send it ninety days of access logs.

Edit with AI