Skip to main content
Phosra ships changes additively. A stable v1 surface — an endpoint, a response field, an SDK export — never disappears or changes shape under you without a named deprecation, an advance-notice window, and a machine-readable signal on the wire. This page is that contract: what “deprecated” means, how long you get, what you will see, and how to migrate.
Verified live. The endpoint behavior and header presence on this page were captured from https://phosra-api-sandbox-production.up.railway.app on 2026-07-06. For how the four version layers pin together, start with Versioning.

The one guarantee

We do not make silent, breaking, immediate changes to a stable v1 surface. Every breaking change is announced first, carries a machine-readable deprecation signal, and honors a minimum notice window before the old behavior can be removed.
Everything below is the mechanism behind that sentence.

Lifecycle stages

Every stable surface moves through four stages, in order. It can sit in a stage indefinitely, but it can never skip one.
“Deprecated” is not “broken.” A deprecated endpoint returns the same status codes and the same body it always did. The only thing that changes is that we have told you a better path exists and, eventually, when the old one stops. You are never forced to move on the day of the announcement.

Advance-notice windows

The minimum notice you are guaranteed depends on the blast radius of the change. These are floors, not targets — most changes get more.
Pre-1.0 SDKs are the exception you opt into. The published packages are 0.x (@phosra/sdk@0.1.0, @phosra/link@0.6.1, @openchildsafety/ocss@0.1.5, …). Under semver, a 0.x minor bump may carry a breaking change with no separate notice window — that is what 0.x means. Pin exact and read the changelog before you bump. The wire API (v1) gives you the windows above; a floating SDK caret range does not. See SDK versioning.

On the wire: Deprecation and Sunset headers

Phosra follows the two IETF standards for signalling this in HTTP, so a generic client or gateway can detect a deprecation without reading these docs:
  • DeprecationRFC 9745. A Structured-Field Date (@ + Unix seconds) marking when the resource became (or will become) deprecated. It may be a future date.
  • SunsetRFC 8594. An HTTP-date marking the moment the resource stops responding.
  • Link — a pointer to the human migration guide, related by rel="deprecation" and/or rel="sunset".
A response from an endpoint that has entered a sunset window looks like this:
Deprecated response (format)
Read it as: “this became deprecated on 2026-09-01 (@1788220800), it stops responding after 2026-12-01, and the migration guide is at the linked URL.” The 200 and the body are exactly what you got before — the headers are the only difference.
Deprecation carries a Structured-Field Date, so the value is @ followed by Unix seconds — not an HTTP-date. Sunset carries a classic HTTP-date. They are intentionally different formats because they come from two different RFCs; parse each accordingly.

What you see today

No v1 endpoint has entered a sunset window yet, so you will not see a Sunset header on a live response today — and we would rather you verify that than take our word for it:
Verify — no live sunset headers today
The block above is the exact shape you will parse when a surface does enter its window. Wiring the check now means your integration lights up the day one appears, instead of the day it breaks.

The one deprecation live today

There is exactly one deprecated route on the current census: the legacy advisor self-registration shim. It still works — it forwards by the did discriminator and returns the same result the replacement would:
The deprecated route still forwards (live)
Migration is a one-line URL change — the request and response shapes are identical:
The published @phosra/cli already calls self-register, so phosra register needs no change — only hand-rolled callers of the old path do. See the CLI reference and self-serve registration.

How a deprecation reaches you

We announce on every channel a developer actually watches — no single point of failure:

The changelog

Every deprecation lands as a dated entry, tagged with the replacement and the removal date. This is the canonical, human record.

Response headers

Deprecation + Sunset + Link on the wire, so automated monitoring catches it without reading docs.

Your account email

Owners of orgs with recent traffic on a deprecated surface get a direct notice at announcement and before the sunset date.

The SDK

Deprecated exports carry a @deprecated JSDoc tag (your editor and tsc flag it) and a 0.x → next-minor removal follows semver. Read the changelog before bumping.

Detect deprecations programmatically

Fail loud in CI or a health check the moment any endpoint you depend on starts advertising a sunset. This runs today (and prints nothing, correctly):

What we will never do

Break v1 in place

A breaking change gets a new path (/api/v2), never a mutation of v1.

Remove without a Sunset

Nothing in v1 is removed until a Sunset date has been published and the notice window has elapsed.

Change a field's meaning

We add a new field beside the old one; we do not silently repurpose an existing field or enum value.

Substitute frozen artifacts

A pinned Annex B edition is content-addressed — its bytes never change, and a missing edition is a clean 404, never a nearby substitution. See Versioning.

Migration expectations

When you receive a deprecation notice, the contract you can count on:
  1. A named replacement exists before the notice ships. We never deprecate a path without a place to go.
  2. The old and new surfaces overlap for at least the notice window — you can run both, cut over, and verify without a flag-day.
  3. The migration is documented at the Link: rel="deprecation" URL, with a before/after example like the one above.
  4. Idempotent, replay-safe cutover. Re-issuing the same write against the new path is safe — see Idempotency.

Next steps

Versioning

The four version layers — API path, OCSS spec version, Annex B editions, SDK semver — and how to pin each.

Changelog

The canonical dated record of every change, deprecation, and SDK release.

Forward compatibility

Why most evolution reaches you as runtime data with no reinstall — and how to write a client that never breaks on an additive change.

Errors

The closed set of error classes a deprecated or removed surface returns.