Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions api-reference/openapi/releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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)."
}
}
},
Comment on lines +2078 to +2092

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce the “name or from required” rule in the schema.

Line 2094 documents a required constraint, but the schema currently allows {}. Add an OpenAPI constraint so tooling and generated clients enforce the same rule.

Suggested schema fix
       "CreateCatalogRequest": {
         "type": "object",
         "description": "Request body for creating a catalog. At least one of name or from must be supplied. The owning account is taken from the request credentials, never from this body.",
+        "anyOf": [
+          { "required": ["name"] },
+          { "required": ["from"] }
+        ],
         "properties": {
           "name": {
             "type": "string",
             "description": "Optional. Display name for the catalog. If omitted when from is supplied, a name is derived from the source run."
           },
           "from": {
             "$ref": "`#/components/schemas/CatalogMaterializationSource`"
           }
         }
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api-reference/openapi/releases.json` around lines 2092 - 2104, The
CreateCatalogRequest schema has a documented constraint that at least one of
name or from must be supplied, but this is not enforced in the OpenAPI schema
itself, allowing invalid empty objects. Add an OpenAPI constraint to the schema
definition to enforce this rule, such as adding minProperties: 1 to ensure at
least one property is present, or use oneOf/anyOf to explicitly define the valid
combinations of properties that satisfy the documented requirement.

"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",
Expand Down
2 changes: 1 addition & 1 deletion api-reference/openapi/research.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
4 changes: 4 additions & 0 deletions api-reference/songs/catalogs-create.mdx

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KISS - too complex. follow existing docs architecture.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 70f75d1catalogs-create.mdx is now frontmatter-only (title + openapi), matching the sibling reference pages like catalogs.mdx. Removed the custom Note callout.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: Create Catalog
openapi: post /api/catalogs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the required OpenAPI frontmatter format.

Line 3 should follow the documented openapi: 'METHOD /path' format (uppercase method).

Suggested frontmatter adjustment
-openapi: post /api/catalogs
+openapi: 'POST /api/catalogs'

As per coding guidelines, “Include OpenAPI spec reference in API reference MDX frontmatter using format: openapi: 'METHOD /path'.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
openapi: post /api/catalogs
openapi: 'POST /api/catalogs'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api-reference/songs/catalogs-create.mdx` at line 3, The OpenAPI frontmatter
on line 3 does not follow the required format. The HTTP method should be
uppercase and the entire path should be wrapped in single quotes. Update the
frontmatter from the current format to use uppercase POST and wrap the method
and path together in single quotes following the documented `openapi: 'METHOD
/path'` format.

Source: Coding guidelines

---
12 changes: 9 additions & 3 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down