[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-changelog\u002F2026-06-26":3,"docs-tabs-changelog\u002F2026-06-26":6},{"content":4,"lastmod":5},"# API changes: June 26, 2026\n\n[← Changelog](\u002Fdocs\u002Fchangelog) · [June 2026](\u002Fdocs\u002Fchangelog\u002F2026-06)\n\n### FIX-0626-1: catalog-prices: system fields priceScale, extraId, timestampX declared in the schema\n\n**Before**\n\n[GET \u002Fv1\u002Fcatalog-prices](\u002Fdocs\u002Fentities\u002Fcatalog-prices\u002Flist) and [GET \u002Fv1\u002Fcatalog-prices\u002F:id](\u002Fdocs\u002Fentities\u002Fcatalog-prices\u002Fget) returned the `priceScale`, `extraId`, and `timestampX` fields, but they were not declared in the schema: they passed through without normalization (the `timestampX` field arrived in an offset format, e.g. `2024-06-17T16:53:24+05:30`) and were absent from the [GET \u002Fv1\u002Fcatalog-prices\u002Ffields](\u002Fdocs\u002Fentities\u002Fcatalog-prices\u002Ffields) response.\n\n**After**\n\nThe three fields are declared as read-only. They are now listed in [GET \u002Fv1\u002Fcatalog-prices\u002Ffields](\u002Fdocs\u002Fentities\u002Fcatalog-prices\u002Ffields), and `timestampX` is normalized to ISO 8601 UTC (`2024-06-17T13:53:24.000Z`), consistent with the other datetime fields.\n\n**Impact on integrators**\n\nThe instant in `timestampX` does not change — only its string representation does (UTC instead of a local offset). Clients that parse the value with a standard date parser keep working unchanged.\n\n### FIX-0626-2: Server and app source download: the signed URL no longer returns 403\n\n**Before**\n\n`GET \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fsources\u002F:versionId\u002Fdownload` and `GET \u002Fv1\u002Fapps\u002F:id\u002Fsources\u002F:versionId\u002Fdownload` returned `200` with a signed URL, but downloading from that URL failed with `403 AccessDenied` when the request used a personal key (`vibe_api_*`). Listing versions worked and the file was physically present in storage.\n\n**After**\n\nThe signed URL is now bound to the object's actual storage location, so the download returns the archive content.\n\n**Impact on integrators**\n\nThe endpoint contract is unchanged — this restores the documented `200` + working signed URL behavior. No client-side changes are required.\n\n### NEW-0626-3: api-bearer token refresh and Gateway rejection reason\n\nThe new endpoint [POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Faccess-tokens\u002F:tokenId\u002Frefresh](\u002Fdocs\u002Finfra\u002Faccess-tokens\u002Frefresh) mints a fresh JWT (up to 10 minutes) for an existing `api-bearer` token — without creating a new record, and without consuming the active-token limit or the hourly mint limit. A long-running client (CI, AI agent) refreshes the token before `jwtExpiresAt` instead of minting a new one. A single revoke kills the original and all refreshed JWTs of one token.\n\nThe `401 BH_LOGIN_REQUIRED` response that the Gateway returns on an app subdomain when it rejects the `Authorization: Bearer` header now carries a `reason` field with the specific cause: `expired`, `signature`, `subdomain`, `type`, `revoked`, `malformed`, or `invalid`. The field is additive — existing clients ignore it.\n",null,{},1794739467585]