{
  "schemaVersion": "1.0",
  "item": {
    "slug": "api-versioning",
    "name": "api-versioning",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/wpank/api-versioning",
    "canonicalUrl": "https://clawhub.ai/wpank/api-versioning",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/api-versioning",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=api-versioning",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-07T17:22:31.273Z",
      "expiresAt": "2026-05-14T17:22:31.273Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
        "contentDisposition": "attachment; filename=\"afrexai-annual-report-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/api-versioning"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/api-versioning",
    "agentPageUrl": "https://openagent3.xyz/skills/api-versioning/agent",
    "manifestUrl": "https://openagent3.xyz/skills/api-versioning/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/api-versioning/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "API Versioning Patterns",
        "body": "Evolve your API confidently. Version correctly, deprecate gracefully, migrate safely — without breaking existing consumers."
      },
      {
        "title": "Versioning Strategies",
        "body": "Pick one strategy and apply it consistently across your entire API surface.\n\nStrategyFormatVisibilityCacheabilityBest ForURL Path/api/v1/usersHighExcellentPublic APIs, third-party integrationsQuery Param/api/users?v=1MediumModerateSimple APIs, prototypingHeaderAccept-Version: v1LowGoodInternal APIs, coordinated consumersContent NegotiationAccept: application/vnd.api.v1+jsonLowGoodEnterprise, strict REST compliance"
      },
      {
        "title": "URL Path Versioning",
        "body": "The most common strategy. Version lives in the URL, making it immediately visible.\n\nfrom fastapi import FastAPI, APIRouter\n\nv1 = APIRouter(prefix=\"/api/v1\")\nv2 = APIRouter(prefix=\"/api/v2\")\n\n@v1.get(\"/users\")\nasync def list_users_v1():\n    return {\"users\": [...]}\n\n@v2.get(\"/users\")\nasync def list_users_v2():\n    return {\"data\": {\"users\": [...]}, \"meta\": {...}}\n\napp = FastAPI()\napp.include_router(v1)\napp.include_router(v2)\n\nRules:\n\nAlways prefix: /api/v1/... not /v1/api/...\nMajor version only: /api/v1/, never /api/v1.2/ or /api/v1.2.3/\nEvery endpoint must be versioned — no mixing versioned and unversioned paths"
      },
      {
        "title": "Header Versioning",
        "body": "Version specified via request headers, keeping URLs clean.\n\nfunction versionRouter(req, res, next) {\n  const version = req.headers['accept-version'] || 'v2'; // default to latest\n  req.apiVersion = version;\n  next();\n}\n\napp.get('/api/users', versionRouter, (req, res) => {\n  if (req.apiVersion === 'v1') return res.json({ users: [...] });\n  if (req.apiVersion === 'v2') return res.json({ data: { users: [...] }, meta: {} });\n  return res.status(400).json({ error: `Unsupported version: ${req.apiVersion}` });\n});\n\nAlways define fallback behavior when no version header is sent — default to latest stable or return 400 Bad Request."
      },
      {
        "title": "Semantic Versioning for APIs",
        "body": "SemVer ComponentAPI MeaningAction RequiredMAJOR (v1 → v2)Breaking changes — remove field, rename endpoint, change authClients must migrateMINOR (v1.1 → v1.2)Additive, backward-compatible — new optional field, new endpointNo client changesPATCH (v1.1.0 → v1.1.1)Bug fixes, no behavior changeNo client changes\n\nOnly MAJOR versions appear in URL paths. Communicate MINOR and PATCH through changelogs."
      },
      {
        "title": "Breaking — Require New Version",
        "body": "ChangeWhy It BreaksRemove a response fieldClients reading that field get undefinedRename a fieldSame as removal from the client's perspectiveChange a field's type\"id\": 123 → \"id\": \"123\" breaks typed clientsRemove an endpointClients calling it get 404Make optional param requiredExisting requests missing it start failingChange URL structureBookmarked/hardcoded URLs breakChange error response formatClient error-handling logic breaksChange authentication mechanismExisting credentials stop working"
      },
      {
        "title": "Non-Breaking — Safe Under Same Version",
        "body": "ChangeWhy It's SafeAdd new optional response fieldClients ignore unknown fieldsAdd new endpointDoesn't affect existing endpointsAdd new optional query/body paramExisting requests work without itAdd new enum valueSafe if clients handle unknown values gracefullyRelax a validation constraintPreviously valid requests remain validImprove performanceSame interface, faster response"
      },
      {
        "title": "Deprecation Strategy",
        "body": "Never remove a version without warning. Follow this timeline:\n\nPhase 1: ANNOUNCE\n  • Sunset header on responses  • Changelog entry\n  • Email/webhook to consumers  • Docs marked \"deprecated\"\n\nPhase 2: SUNSET PERIOD\n  • v1 still works but warns     • Monitor v1 traffic\n  • Contact remaining consumers  • Provide migration support\n\nPhase 3: REMOVAL\n  • v1 returns 410 Gone\n  • Response body includes migration guide URL\n  • Redirect docs to v2\n\nMinimum deprecation periods: Public API: 12 months · Partner API: 6 months · Internal API: 1–3 months"
      },
      {
        "title": "Sunset HTTP Header (RFC 8594)",
        "body": "Include on every response from a deprecated version:\n\nHTTP/1.1 200 OK\nSunset: Sat, 01 Mar 2025 00:00:00 GMT\nDeprecation: true\nLink: <https://api.example.com/docs/migrate-v1-v2>; rel=\"sunset\"\nX-API-Warn: \"v1 is deprecated. Migrate to v2 by 2025-03-01.\""
      },
      {
        "title": "Retired Version Response",
        "body": "When past sunset, return 410 Gone:\n\n{\n  \"error\": \"VersionRetired\",\n  \"message\": \"API v1 was retired on 2025-03-01.\",\n  \"migration_guide\": \"https://api.example.com/docs/migrate-v1-v2\",\n  \"current_version\": \"v2\"\n}"
      },
      {
        "title": "Adapter Pattern",
        "body": "Shared business logic, version-specific serialization:\n\nclass UserService:\n    async def get_user(self, user_id: str) -> User:\n        return await self.repo.find(user_id)\n\ndef to_v1(user: User) -> dict:\n    return {\"id\": user.id, \"name\": user.full_name, \"email\": user.email}\n\ndef to_v2(user: User) -> dict:\n    return {\n        \"id\": user.id,\n        \"name\": {\"first\": user.first_name, \"last\": user.last_name},\n        \"emails\": [{\"address\": e, \"primary\": i == 0} for i, e in enumerate(user.emails)],\n        \"created_at\": user.created_at.isoformat(),\n    }"
      },
      {
        "title": "Facade Pattern",
        "body": "Single entry point delegates to the correct versioned handler:\n\nasync def get_user(user_id: str, version: int):\n    user = await user_service.get_user(user_id)\n    serializers = {1: to_v1, 2: to_v2}\n    serialize = serializers.get(version)\n    if not serialize:\n        raise UnsupportedVersionError(version)\n    return serialize(user)"
      },
      {
        "title": "Versioned Controllers",
        "body": "Separate controller files per version, shared service layer:\n\napi/\n  v1/\n    users.py      # v1 request/response shapes\n    orders.py\n  v2/\n    users.py      # v2 request/response shapes\n    orders.py\n  services/\n    user_service.py   # version-agnostic business logic\n    order_service.py"
      },
      {
        "title": "API Gateway Routing",
        "body": "Route versions at infrastructure layer:\n\nroutes:\n  - match: /api/v1/*\n    upstream: api-v1-service:8080\n  - match: /api/v2/*\n    upstream: api-v2-service:8080"
      },
      {
        "title": "Multi-Version Support",
        "body": "Architecture:\n\nRequest → API Gateway → Version Router → v1 Handler → Shared Service Layer → DB\n                                        → v2 Handler ↗\n\nPrinciples:\n\nBusiness logic is version-agnostic. Services, repositories, and domain models are shared.\nSerialization is version-specific. Each version has its own request validators and response serializers.\nTransformations are explicit. A v1_to_v2 transformer documents every field mapping.\nTests cover all active versions. Every supported version has its own integration test suite.\n\nMaximum concurrent versions: 2–3 active (current + 1–2 deprecated). More than 3 creates unsustainable maintenance burden."
      },
      {
        "title": "Changelog",
        "body": "Publish a changelog for every release, tagged by version and change type:\n\n## v2.3.0 — 2025-02-01\n### Added\n- `avatar_url` field on User response\n- `GET /api/v2/users/{id}/activity` endpoint\n### Deprecated\n- `name` field on User — use `first_name` and `last_name` (removal in v3)"
      },
      {
        "title": "Migration Guides",
        "body": "For every major version bump, provide:\n\nField-by-field mapping table (v1 → v2)\nBefore/after request and response examples\nCode snippets for common languages/SDKs\nTimeline with key dates (announcement, sunset, removal)"
      },
      {
        "title": "SDK Versioning",
        "body": "Align SDK major versions with API major versions:\n\napi-client@1.x  →  /api/v1\napi-client@2.x  →  /api/v2\n\nShip the new SDK before announcing API deprecation."
      },
      {
        "title": "Anti-Patterns",
        "body": "Anti-PatternFixVersioning too frequentlyBatch breaking changes into infrequent major releasesBreaking without noticeAlways follow the deprecation timelineEternal version supportSet and enforce sunset datesInconsistent versioningOne version scheme, applied uniformlyVersion per endpointVersion the entire API surface togetherUsing versions to gate featuresUse feature flags separately; versions are for contractsNo default versionAlways define a default or return explicit 400"
      },
      {
        "title": "NEVER Do",
        "body": "NEVER remove a field, endpoint, or change a type without bumping the major version\nNEVER sunset a public API version with less than 6 months notice\nNEVER mix versioning strategies in the same API (URL path for some, headers for others)\nNEVER use minor or patch versions in URL paths (/api/v1.2/ is wrong — use /api/v1/)\nNEVER version individual endpoints independently — version the entire API surface as a unit\nNEVER deploy a breaking change under an existing version number, even if \"nobody uses that field\"\nNEVER skip documenting differences between versions — every breaking change needs a migration guide entry"
      }
    ],
    "body": "API Versioning Patterns\n\nEvolve your API confidently. Version correctly, deprecate gracefully, migrate safely — without breaking existing consumers.\n\nVersioning Strategies\n\nPick one strategy and apply it consistently across your entire API surface.\n\nStrategy\tFormat\tVisibility\tCacheability\tBest For\nURL Path\t/api/v1/users\tHigh\tExcellent\tPublic APIs, third-party integrations\nQuery Param\t/api/users?v=1\tMedium\tModerate\tSimple APIs, prototyping\nHeader\tAccept-Version: v1\tLow\tGood\tInternal APIs, coordinated consumers\nContent Negotiation\tAccept: application/vnd.api.v1+json\tLow\tGood\tEnterprise, strict REST compliance\nURL Path Versioning\n\nThe most common strategy. Version lives in the URL, making it immediately visible.\n\nfrom fastapi import FastAPI, APIRouter\n\nv1 = APIRouter(prefix=\"/api/v1\")\nv2 = APIRouter(prefix=\"/api/v2\")\n\n@v1.get(\"/users\")\nasync def list_users_v1():\n    return {\"users\": [...]}\n\n@v2.get(\"/users\")\nasync def list_users_v2():\n    return {\"data\": {\"users\": [...]}, \"meta\": {...}}\n\napp = FastAPI()\napp.include_router(v1)\napp.include_router(v2)\n\n\nRules:\n\nAlways prefix: /api/v1/... not /v1/api/...\nMajor version only: /api/v1/, never /api/v1.2/ or /api/v1.2.3/\nEvery endpoint must be versioned — no mixing versioned and unversioned paths\nHeader Versioning\n\nVersion specified via request headers, keeping URLs clean.\n\nfunction versionRouter(req, res, next) {\n  const version = req.headers['accept-version'] || 'v2'; // default to latest\n  req.apiVersion = version;\n  next();\n}\n\napp.get('/api/users', versionRouter, (req, res) => {\n  if (req.apiVersion === 'v1') return res.json({ users: [...] });\n  if (req.apiVersion === 'v2') return res.json({ data: { users: [...] }, meta: {} });\n  return res.status(400).json({ error: `Unsupported version: ${req.apiVersion}` });\n});\n\n\nAlways define fallback behavior when no version header is sent — default to latest stable or return 400 Bad Request.\n\nSemantic Versioning for APIs\nSemVer Component\tAPI Meaning\tAction Required\nMAJOR (v1 → v2)\tBreaking changes — remove field, rename endpoint, change auth\tClients must migrate\nMINOR (v1.1 → v1.2)\tAdditive, backward-compatible — new optional field, new endpoint\tNo client changes\nPATCH (v1.1.0 → v1.1.1)\tBug fixes, no behavior change\tNo client changes\n\nOnly MAJOR versions appear in URL paths. Communicate MINOR and PATCH through changelogs.\n\nBreaking vs Non-Breaking Changes\nBreaking — Require New Version\nChange\tWhy It Breaks\nRemove a response field\tClients reading that field get undefined\nRename a field\tSame as removal from the client's perspective\nChange a field's type\t\"id\": 123 → \"id\": \"123\" breaks typed clients\nRemove an endpoint\tClients calling it get 404\nMake optional param required\tExisting requests missing it start failing\nChange URL structure\tBookmarked/hardcoded URLs break\nChange error response format\tClient error-handling logic breaks\nChange authentication mechanism\tExisting credentials stop working\nNon-Breaking — Safe Under Same Version\nChange\tWhy It's Safe\nAdd new optional response field\tClients ignore unknown fields\nAdd new endpoint\tDoesn't affect existing endpoints\nAdd new optional query/body param\tExisting requests work without it\nAdd new enum value\tSafe if clients handle unknown values gracefully\nRelax a validation constraint\tPreviously valid requests remain valid\nImprove performance\tSame interface, faster response\nDeprecation Strategy\n\nNever remove a version without warning. Follow this timeline:\n\nPhase 1: ANNOUNCE\n  • Sunset header on responses  • Changelog entry\n  • Email/webhook to consumers  • Docs marked \"deprecated\"\n\nPhase 2: SUNSET PERIOD\n  • v1 still works but warns     • Monitor v1 traffic\n  • Contact remaining consumers  • Provide migration support\n\nPhase 3: REMOVAL\n  • v1 returns 410 Gone\n  • Response body includes migration guide URL\n  • Redirect docs to v2\n\n\nMinimum deprecation periods: Public API: 12 months · Partner API: 6 months · Internal API: 1–3 months\n\nSunset HTTP Header (RFC 8594)\n\nInclude on every response from a deprecated version:\n\nHTTP/1.1 200 OK\nSunset: Sat, 01 Mar 2025 00:00:00 GMT\nDeprecation: true\nLink: <https://api.example.com/docs/migrate-v1-v2>; rel=\"sunset\"\nX-API-Warn: \"v1 is deprecated. Migrate to v2 by 2025-03-01.\"\n\nRetired Version Response\n\nWhen past sunset, return 410 Gone:\n\n{\n  \"error\": \"VersionRetired\",\n  \"message\": \"API v1 was retired on 2025-03-01.\",\n  \"migration_guide\": \"https://api.example.com/docs/migrate-v1-v2\",\n  \"current_version\": \"v2\"\n}\n\nMigration Patterns\nAdapter Pattern\n\nShared business logic, version-specific serialization:\n\nclass UserService:\n    async def get_user(self, user_id: str) -> User:\n        return await self.repo.find(user_id)\n\ndef to_v1(user: User) -> dict:\n    return {\"id\": user.id, \"name\": user.full_name, \"email\": user.email}\n\ndef to_v2(user: User) -> dict:\n    return {\n        \"id\": user.id,\n        \"name\": {\"first\": user.first_name, \"last\": user.last_name},\n        \"emails\": [{\"address\": e, \"primary\": i == 0} for i, e in enumerate(user.emails)],\n        \"created_at\": user.created_at.isoformat(),\n    }\n\nFacade Pattern\n\nSingle entry point delegates to the correct versioned handler:\n\nasync def get_user(user_id: str, version: int):\n    user = await user_service.get_user(user_id)\n    serializers = {1: to_v1, 2: to_v2}\n    serialize = serializers.get(version)\n    if not serialize:\n        raise UnsupportedVersionError(version)\n    return serialize(user)\n\nVersioned Controllers\n\nSeparate controller files per version, shared service layer:\n\napi/\n  v1/\n    users.py      # v1 request/response shapes\n    orders.py\n  v2/\n    users.py      # v2 request/response shapes\n    orders.py\n  services/\n    user_service.py   # version-agnostic business logic\n    order_service.py\n\nAPI Gateway Routing\n\nRoute versions at infrastructure layer:\n\nroutes:\n  - match: /api/v1/*\n    upstream: api-v1-service:8080\n  - match: /api/v2/*\n    upstream: api-v2-service:8080\n\nMulti-Version Support\n\nArchitecture:\n\nRequest → API Gateway → Version Router → v1 Handler → Shared Service Layer → DB\n                                        → v2 Handler ↗\n\n\nPrinciples:\n\nBusiness logic is version-agnostic. Services, repositories, and domain models are shared.\nSerialization is version-specific. Each version has its own request validators and response serializers.\nTransformations are explicit. A v1_to_v2 transformer documents every field mapping.\nTests cover all active versions. Every supported version has its own integration test suite.\n\nMaximum concurrent versions: 2–3 active (current + 1–2 deprecated). More than 3 creates unsustainable maintenance burden.\n\nClient Communication\nChangelog\n\nPublish a changelog for every release, tagged by version and change type:\n\n## v2.3.0 — 2025-02-01\n### Added\n- `avatar_url` field on User response\n- `GET /api/v2/users/{id}/activity` endpoint\n### Deprecated\n- `name` field on User — use `first_name` and `last_name` (removal in v3)\n\nMigration Guides\n\nFor every major version bump, provide:\n\nField-by-field mapping table (v1 → v2)\nBefore/after request and response examples\nCode snippets for common languages/SDKs\nTimeline with key dates (announcement, sunset, removal)\nSDK Versioning\n\nAlign SDK major versions with API major versions:\n\napi-client@1.x  →  /api/v1\napi-client@2.x  →  /api/v2\n\n\nShip the new SDK before announcing API deprecation.\n\nAnti-Patterns\nAnti-Pattern\tFix\nVersioning too frequently\tBatch breaking changes into infrequent major releases\nBreaking without notice\tAlways follow the deprecation timeline\nEternal version support\tSet and enforce sunset dates\nInconsistent versioning\tOne version scheme, applied uniformly\nVersion per endpoint\tVersion the entire API surface together\nUsing versions to gate features\tUse feature flags separately; versions are for contracts\nNo default version\tAlways define a default or return explicit 400\nNEVER Do\nNEVER remove a field, endpoint, or change a type without bumping the major version\nNEVER sunset a public API version with less than 6 months notice\nNEVER mix versioning strategies in the same API (URL path for some, headers for others)\nNEVER use minor or patch versions in URL paths (/api/v1.2/ is wrong — use /api/v1/)\nNEVER version individual endpoints independently — version the entire API surface as a unit\nNEVER deploy a breaking change under an existing version number, even if \"nobody uses that field\"\nNEVER skip documenting differences between versions — every breaking change needs a migration guide entry"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/wpank/api-versioning",
    "publisherUrl": "https://clawhub.ai/wpank/api-versioning",
    "owner": "wpank",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/api-versioning",
    "downloadUrl": "https://openagent3.xyz/downloads/api-versioning",
    "agentUrl": "https://openagent3.xyz/skills/api-versioning/agent",
    "manifestUrl": "https://openagent3.xyz/skills/api-versioning/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/api-versioning/agent.md"
  }
}