{
  "version": "api-versions-2026.1",
  "current": "v1",
  "note": "The contract version in the URL path changes ONLY on a breaking change. Dataset versions move independently and are never breaking on their own. The full algorithm is in VERSIONING.md; this file is the machine-readable form of it, served at /wine/api/v1/versions.json and enforced by scripts/check-contract.mjs.",
  "policy": {
    "breakingChangeRequires": "a new path version (/v2). The previous version keeps serving.",
    "minimumSupportWindowMonths": 12,
    "minimumDeprecationNoticeMonths": 6,
    "deprecationSignals": [
      "Deprecation header (RFC 9745) carrying the date the version was deprecated",
      "Sunset header (RFC 8594) carrying the date it stops serving",
      "Link header with rel=\"deprecation\" pointing at the migration notes",
      "a deprecation block in every JSON response body"
    ],
    "breaking": [
      "removing or renaming a response field",
      "changing the type or units of a response field",
      "removing an endpoint, or changing its path",
      "adding a required request parameter",
      "tightening validation so a previously accepted request now fails",
      "changing a default value, page size or sort order",
      "changing the meaning of an existing field without renaming it",
      "changing the shape of a record inside a published dataset"
    ],
    "additive": [
      "adding a new endpoint",
      "adding a new response field",
      "adding a new OPTIONAL request parameter",
      "adding a new enum value to a field documented as open",
      "widening validation so a previously rejected request now succeeds",
      "correcting data values, or refreshing a dataset to a newer upstream edition"
    ]
  },
  "versions": [
    {
      "version": "v1",
      "status": "current",
      "releasedOn": "2026-09-19",
      "deprecatedOn": null,
      "sunsetOn": null,
      "basePath": "/wine/v1",
      "changelog": [
        {
          "release": "1.1.0",
          "date": "2026-09-19",
          "kind": "breaking-withdrawn",
          "summary": "Label records went out of scope; three label-shaped routes withdrawn and the paid tier redefined.",
          "justification": "Each withdrawn route only ever returned 501 Not Implemented — none served data in any deployed release, and they were withdrawn because the capability was abandoned rather than moved. Withdrawn under the narrow exception in VERSIONING.md rather than by opening /v2, which would have made the version number mean less. /v1/labels and /v1/labels/{ttbId} are RETAINED, still answering 501, because an explicit 'not served here, and here is why' is more useful than a 404.",
          "changes": [
            "withdrawn: GET /v1/labels/export",
            "withdrawn: GET /v1/labels/{ttbId}/history",
            "withdrawn: GET /v1/producers/{permitId}/labels",
            "retained: GET /v1/labels and /v1/labels/{ttbId} — still 501, now stating labels are out of scope",
            "plans: paid tier rebuilt on change diffs and change webhooks; every labels.* feature removed",
            "plans: free tier gains bulk.download, which was always true and is now stated"
          ]
        },
        {
          "release": "1.0.0",
          "date": "2026-09-19",
          "kind": "initial",
          "summary": "Reference tier: producers, appellations, grape varieties, class/type and origin codes.",
          "changes": [
            "GET /v1/producers, /v1/producers/{permitId}",
            "GET /v1/appellations, /v1/appellations/{avaId}",
            "GET /v1/varieties, /v1/varieties/{varietyId}",
            "GET /v1/class-types/{code}, /v1/origin-codes/{code}",
            "GET /v1/plans, /v1/usage, /v1/version",
            "GET /v1/labels returns 501 — label records are out of scope for this service"
          ]
        }
      ]
    }
  ],
  "datasetVersioning": {
    "scheme": "<source>-<year>.<edition>",
    "example": "ttb-permits-2026.1",
    "rule": "A dataset version increments when the upstream data or the build that shapes it changes. Every response carries datasetVersions so a caller can pin, cache and reproduce. Refreshing a dataset is ADDITIVE: values may change, record shape may not. A change to the SHAPE of a dataset record is a breaking API change and takes a new path version.",
    "reproducibility": "Any response can be reproduced from its datasetVersions plus the build in this repo at the matching tag."
  }
}
