From 1b0246f2c581c18c39dd4689b6afc13b1bd0240f Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Thu, 30 Jul 2026 14:41:35 -0500 Subject: [PATCH] docs(catalogs): catalog songs require authentication (chat#1912 row 6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /api/catalogs/songs is documented and implemented as unauthenticated while GET /api/catalogs/{catalogId}/measurements requires credentials, so the catalog report page tells a stranger the valuation belongs to another account on the same screen that hands them every track and ISRC. Verified on prod 2026-07-30 that POST and DELETE are unauthenticated too: both reach body validation and return 400 with no credentials, so anyone holding a catalog id can add or remove its songs. This is the contract half of the decision, written first so it can be reviewed before any code moves: a catalog is readable and writable only by the accounts it belongs to. All three operations gain apiKeyAuth/bearerAuth plus 401 and 403, matching the measurements endpoint. If we would rather catalogs be readable by anyone holding the link, this is the PR to reject — the api change follows from whatever lands here. Co-Authored-By: Claude Opus 5 (1M context) --- api-reference/openapi/releases.json | 84 +++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/api-reference/openapi/releases.json b/api-reference/openapi/releases.json index 57e0068..2d608a8 100644 --- a/api-reference/openapi/releases.json +++ b/api-reference/openapi/releases.json @@ -1500,6 +1500,14 @@ }, "/api/catalogs/songs": { "get": { + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], "description": "Retrieve songs within a specific catalog with pagination support. This endpoint joins catalog_songs with songs, song_artists, and accounts to provide comprehensive song information for a given catalog.", "parameters": [ { @@ -1564,6 +1572,26 @@ } } }, + "401": { + "description": "Unauthorized - missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden - the catalog does not belong to the authenticated account", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, "404": { "description": "Catalog not found or no songs in catalog", "content": { @@ -1577,6 +1605,14 @@ } }, "post": { + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], "description": "Batch add songs to a catalog by ISRC. For each song, the API attempts to look up metadata via internal search. If no data is found, optional fallback fields (name, album, notes, artists) are used.", "requestBody": { "description": "Array of songs to add to catalog", @@ -1609,10 +1645,38 @@ } } } + }, + "401": { + "description": "Unauthorized - missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden - the catalog does not belong to the authenticated account", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } } } }, "delete": { + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], "description": "Batch remove songs from a catalog by ISRC. Deletes the relationship in catalog_songs for each catalog_id and ISRC pair.", "requestBody": { "description": "Array of songs to remove from catalog", @@ -1645,6 +1709,26 @@ } } } + }, + "401": { + "description": "Unauthorized - missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden - the catalog does not belong to the authenticated account", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } } } }