diff --git a/api-reference/openapi/releases.json b/api-reference/openapi/releases.json index de1c2578..d6b01c37 100644 --- a/api-reference/openapi/releases.json +++ b/api-reference/openapi/releases.json @@ -1200,6 +1200,78 @@ } } }, + "/api/catalogs": { + "post": { + "description": "Create a catalog.", + "security": [ + { "apiKeyAuth": [] }, + { "bearerAuth": [] } + ], + "requestBody": { + "description": "Catalog to create. Provide name, snapshot, or both - at least one is required.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCatalogRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Catalog created, or the existing catalog returned when re-materializing the same snapshot (idempotent)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCatalogResponse" + } + } + } + }, + "400": { + "description": "Bad request - neither name nor snapshot provided, or invalid snapshot", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized - missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden - the snapshot belongs to a different account", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + }, + "404": { + "description": "Not found - no snapshot exists for the supplied snapshot", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CatalogSongsErrorResponse" + } + } + } + } + } + } + }, "/api/catalogs/songs": { "get": { "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.", @@ -2003,6 +2075,46 @@ } } }, + "CreateCatalogRequest": { + "type": "object", + "description": "Request body for creating a catalog. At least one of name or snapshot must be supplied. The owning account is taken from the request credentials, never from this body.", + "properties": { + "name": { + "type": "string", + "description": "Optional. Display name for the catalog. If omitted when snapshot is supplied, a name is derived from the source run." + }, + "snapshot": { + "type": "string", + "format": "uuid", + "description": "Optional. ID of a completed playcount snapshot (valuation run) owned by the authenticated account. Its measured ISRCs are added to the new catalog as catalog songs. Create one with [Create measurement job](/api-reference/research/measurement-jobs)." + } + } + }, + "CreateCatalogResponse": { + "type": "object", + "description": "Response returned after creating, or idempotently re-fetching, a catalog", + "properties": { + "status": { + "type": "string", + "enum": [ + "success", + "error" + ], + "description": "Status of the request" + }, + "catalog": { + "$ref": "#/components/schemas/Catalog" + }, + "songs_added": { + "type": "integer", + "description": "Number of catalog songs materialized from the source. 0 when no snapshot was supplied, or when the run was already materialized (idempotent re-claim)." + }, + "error": { + "type": "string", + "description": "Error message (only present if status is 'error')" + } + } + }, "Catalog": { "type": "object", "description": "A catalog with its metadata", diff --git a/api-reference/openapi/research.json b/api-reference/openapi/research.json index 807f09b8..f149a2d0 100644 --- a/api-reference/openapi/research.json +++ b/api-reference/openapi/research.json @@ -2915,7 +2915,7 @@ "/api/research/measurement-jobs": { "post": { "summary": "Create a measurement job", - "description": "One async ingest resource. `source:\"current\"` captures present counts via the snapshot pipeline. `source:\"historical\"` enqueues each resolved recording for Songstats deep backfill ranked by all-time streams (idempotent — songs already carrying `songstats` history are skipped; no track is fetched twice). Provide exactly one of `catalog_id` / `album_ids` / `isrcs` in `scope`.", + "description": "One async ingest resource. `source:\"current\"` captures present counts via the snapshot pipeline. `source:\"historical\"` enqueues each resolved recording for Songstats deep backfill ranked by all-time streams (idempotent — songs already carrying `songstats` history are skipped; no track is fetched twice). Provide exactly one of `catalog_id` / `album_ids` / `isrcs` in `scope`. The returned `id` is a snapshot id you can pass to [Create catalog](/api-reference/songs/catalogs-create) to materialize the measured tracks into an account-owned catalog.", "requestBody": { "required": true, "content": { diff --git a/api-reference/songs/catalogs-create.mdx b/api-reference/songs/catalogs-create.mdx new file mode 100644 index 00000000..037bb7ff --- /dev/null +++ b/api-reference/songs/catalogs-create.mdx @@ -0,0 +1,4 @@ +--- +title: Create Catalog +openapi: post /api/catalogs +--- diff --git a/docs.json b/docs.json index eb862140..26584304 100644 --- a/docs.json +++ b/docs.json @@ -146,16 +146,22 @@ ] }, { - "tab": "Releases", + "tab": "Catalog", "groups": [ { - "group": "Songs & Catalogs", + "group": "Songs", "pages": [ "api-reference/songs/songs", "api-reference/songs/create", "api-reference/songs/analyze", - "api-reference/songs/analyze-presets", + "api-reference/songs/analyze-presets" + ] + }, + { + "group": "Catalogs", + "pages": [ "api-reference/songs/catalogs", + "api-reference/songs/catalogs-create", "api-reference/songs/catalog-songs", "api-reference/songs/catalog-songs-add", "api-reference/songs/catalog-songs-delete"