API Versioning skill for Codex

Break APIs rarely, retire old versions on schedule. Codex first checks whether a change is actually breaking and looks for an additive route before adding a version. When a version is needed it builds it as a thin adapter over shared domain logic, emits RFC 9745 Deprecation and RFC 8594 Sunset headers, measures remaining usage per client from logs, and plans brownouts and the final 410 with you.

  • Backend & APIs
  • $api-versioning
  • 171 lines
  • 2 files
  • ~1,850 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- api-versioning

Installs to ~/.agents/skills/api-versioning/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $api-versioning, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Chooses and implements an API versioning strategy (URL major versions, media-type or header versions, Stripe-style dated versions) and runs deprecations end to end with Deprecation (RFC 9745) and Sunset (RFC 8594) headers, usage tracking per client, brownouts and removal. Use when a breaking change is unavoidable, when introducing /v2, deciding how clients pin a version, deprecating an endpoint or field, or planning the shutdown of an old API version.

Use it when

  • A breaking API change is on the table
  • Introducing a new major API version or dated versions
  • Deprecating endpoints, fields or a whole version
  • Planning brownouts and the sunset of an old version

Use something else when

  • Evolving a GraphQL schema, which deprecates fields instead of versioning: use graphql-schema-design
  • Detecting breaking changes in a spec file automatically: use openapi-first

How to check the result

Contract tests pass for every supported version, deprecated routes return correct Deprecation, Sunset and Link headers, and a usage report lists every remaining client.

The mistake it prevents

Defaulting unpinned clients to the latest version, which turns every release with a breaking change into an outage for integrations that never chose to upgrade.

~/.agents/skills/api-versioning/SKILL.md

--- name: api-versioning description: Chooses and implements an API versioning strategy (URL major versions, media-type or header versions, Stripe-style dated versions) and runs… license: MIT ---

Versioning is done well when most changes need no new version at all, the rare breaking change ships behind an explicit version clients opt into, and old versions are retired on a published date with evidence that remaining traffic has been contacted, not guessed.

First question: is this change breaking?

ChangeBreaking?
Add an endpoint, optional request field, response field, optional query paramNo
Add an enum value to a responseUsually no, if docs say enums are open; yes for strict clients
Remove or rename a field, endpoint or enum valueYes
Make an optional request field required, or add a required paramYes
Change a type, format, unit or default valueYes
Change status codes, error codes or pagination semanticsYes
Tighten validation (shorter max length, new format rule)Yes
Change ordering of a list that clients may rely onTreat as yes

If the change is not breaking, ship it without a version bump. If it is, first look for an additive alternative: a new field next to the old one, a new endpoint, a new optional parameter that opts into new behavior. Version only when the additive route would leave the API permanently confusing.

Choose a strategy

StrategyHow clients selectBest forCost
URL major version (/v1/orders)PathMost public and internal REST APIsCoarse; v2 tends to be a big-bang
Media type (Accept: application/vnd.acme.v2+json)HeaderPer-resource evolution, hypermedia APIsHarder to test in a browser, easy to forget in clients
Dated version header (Api-Version: 2026-03-01) pinned per accountHeader, default from accountHigh-churn public APIs with many small breaksNeeds a version-transform layer and strict tests
Query param (?version=2)QueryRarely; caches and logs handle it poorlyAvoid for new APIs

Default recommendation: URL major version for the big structural breaks, additive evolution inside a major, and dated versions only if the team will build and maintain a transform layer. GraphQL APIs do not version; they deprecate fields (see graphql-schema-design). gRPC versions live in the proto package (acme.orders.v2).

Workflow

- [ ] 1. Confirm the change is breaking and no additive path works
- [ ] 2. Implement the new version beside the old one
- [ ] 3. Announce deprecation with headers, docs and changelog
- [ ] 4. Measure remaining usage per client
- [ ] 5. Contact, brown out, then sunset
- [ ] 6. Remove code and spec after sunset

1. Inspect

Read AGENTS.md, the OpenAPI spec and router setup. Identify how versions are routed today, where the version is logged, and which clients (API keys, apps, partners) exist.

2. Implement side by side

  • Share the domain layer; version only the HTTP adapter (request parsing, response shaping). Two copies of business logic will diverge.
  • For dated versions, keep the newest representation as the internal model and write one small transform per version change that downgrades responses and upgrades requests. Apply transforms in order from the client’s pinned version.
// one entry per breaking change, newest first
const changes: VersionChange[] = [
  {
    version: '2026-03-01',
    description: 'Order.total split into total_amount + currency',
    downgradeResponse: (o) => ({ ...o, total: `${o.total_amount / 100} ${o.currency}` }),
  },
];

export function renderForVersion(body: unknown, clientVersion: string) {
  return changes
    .filter((c) => c.version > clientVersion)
    .reduce((acc, c) => c.downgradeResponse(acc), body);
}
  • Unknown or missing version: for URL versions that is a 404; for header versions use the client’s pinned default or the oldest supported version, never “latest” (latest as default breaks every client on each release).

3. Announce deprecation

Send standard headers on every response from the deprecated surface:

Deprecation: @1767225600
Sunset: Thu, 01 Oct 2026 00:00:00 GMT
Link: <https://developer.example.com/migrations/v1-to-v2>; rel="deprecation"; type="text/html"
  • Deprecation (RFC 9745) is a structured-field date: @ plus a Unix timestamp of when the resource is or was deprecated.
  • Sunset (RFC 8594) is an HTTP-date after which the resource may stop responding.
  • Sunset no earlier than the deprecation date; give at least 6 months for public APIs, 12 for paid partner integrations, unless contracts say otherwise.
  • Publish the migration guide, changelog entry and the date in the same release.
export function deprecated(opts: { since: Date; sunset: Date; link: string }) {
  return (req, res, next) => {
    res.set('Deprecation', `@${Math.floor(opts.since.getTime() / 1000)}`);
    res.set('Sunset', opts.sunset.toUTCString());
    res.append('Link', `<${opts.link}>; rel="deprecation"; type="text/html"`);
    req.log?.info({ deprecated: true, route: req.route?.path, client: req.clientId });
    next();
  };
}

4. Measure usage

  • Log API version, route, and client identity on every request; build a query or dashboard of “requests to deprecated surface by client, last 30 days”.
  • Usage counts must come from production logs or metrics, never from assumptions about who “should” have migrated.

5. Contact, brown out, sunset

  • Notify remaining clients directly (email, dashboard banner) with their own usage numbers. Sending messages to customers needs the user’s explicit go-ahead.
  • Brownouts: one to four weeks before sunset, return the post-sunset error for short, announced windows (for example 10 minutes, then 1 hour) so unmonitored integrations surface. Schedule them with the user; they are deliberate production failures.
  • At sunset, respond 410 Gone with a problem body linking the migration guide. Keep that stub for a few months before deleting the route.

6. Remove

Delete the old adapter code, transforms, spec paths and tests in one change. Confirm the OpenAPI diff shows only the intended removals.

Gotchas

  • Bumping the major version for every small break trains clients to ignore upgrades and multiplies the versions you must run.
  • Versioning the whole database or service instead of the adapter doubles every bug fix.
  • SDKs that default to “latest” version on upgrade make a library bump a production break for your customers; pin the version in SDK releases.
  • CDN and proxy caches must vary on any version header (Vary: Api-Version) or clients receive another version’s response.
  • Webhook payloads need versioning too, usually pinned to the account’s API version.

Verification

  • Contract tests run against every supported version, including the transform chain.
  • curl -i on a deprecated route shows Deprecation, Sunset and Link headers with correct values.
  • Usage dashboard shows the deprecated surface and identifies each remaining client.
  • After sunset, the route returns 410 with the migration link.

Output template

## Versioning plan: <change>
- Breaking because: <rule from table>
- Additive alternative considered: <why rejected>
- Strategy: <URL v2 | dated version 2026-03-01 | ...>
- Deprecation date / sunset date: <dates>
- Remaining clients (last 30 days): <count, top clients>
- Communication and brownout schedule: <dates, owner>
- Removal PR: <after sunset + N weeks>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "API Versioning"
  short_description: "Break APIs rarely, retire old versions on schedule"
  default_prompt: "$api-versioning We need to change the order total format, do we need a v2?"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem