APIVersioning

Versioning

How the formats grow without breaking the apps that read them.

Every format is versioned by a single integer, and most changes don't need a new one. The rules are the same for themes, cards and the public page.

The rules

  1. Schemas are versioned by integer

    Documents that carry a version say so: "schema": 1 in themes and cards. The version is also in the schema's URL: theme-1.json, card-1.json, profile-1.json.

  2. Additive changes keep the version

    New enum values (a new stat id, a new theme font), new optional fields and new endpoints ship under the same version. The served schema is updated in place.

  3. Readers ignore what they don't know

    Skip unknown fields. Skip a stat whose id you don't recognize (it still has a label and value if you want to show it generically). Treat an unknown theme enum value as that field's default: the value terminal uses.

  4. Breaking changes bump the version

    Removing or renaming a field or enum value, changing a type, or changing what a value means ships as a new file, {name}-{n+1}.json. The old version stays served.

  5. Unknown themes fall back to terminal

    A theme id that no longer exists renders as terminal. A broken theme never breaks a profile.

  6. Retired stats disappear quietly

    If a stat id is ever retired, cards that chose it drop it when read, topped up from the defaults so a card always shows at least 2.

  7. Writes are strict, reads are lenient

    PUT /profile/card and theme submissions are checked against the current schema. Responses may contain values newer than your client.

What that means for your client

ChangeVersionWhat you do
A new stat id, e.g. kudossameSkip it, or show its label and value
A new theme font or layout variantsameUse terminal's value for that field
A new optional field on the pagesameIgnore it
A new endpointsameNothing
A field renamed or removedn+1Keep reading n; move when you're ready
A value's meaning changesn+1Same

Validating

  • Validate against the schema served now (cached for at most a day), not a copy vendored into your app: a stale copy rejects values that were added since.
  • A failed validation on read is a reason to render less, never a reason to render nothing.

Errors

Errors are JSON with a stable error code and the matching HTTP status:

StatuserrorWhen
400invalidBad input. PUT /profile/card adds a human-readable message
401unauthorizedMissing or expired token on an authenticated endpoint
404not_foundUnknown profile, username or schema
429rate_limitedToo many requests

Some endpoints add a reason (see Handle). Don't rely on any other field in an error.