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
136 changes: 135 additions & 1 deletion api-reference/openapi/releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -1414,7 +1414,7 @@
"/api/valuation": {
"post": {
"summary": "Run valuation",
"description": "Generate a catalog from a Spotify artist in one call. Resolves the artist's releases, captures current Spotify play counts (spending the account's credits), materializes an account-owned catalog from the resulting snapshot (idempotent - see [Create catalog](/api-reference/songs/catalogs-create)), and returns the catalog with its estimated value band. The owning account is taken from the credentials, never the body. The searched artist is also linked to the caller's roster (so a funnel signup lands with a populated `GET /api/artists` it can confirm); when the catalog's songs already resolve a canonical artist that one is used, otherwise the searched Spotify artist is linked directly. Synchronous: the request waits for the capture to land (typically under two minutes).",
"description": "Generate a catalog from a Spotify artist in one call. Resolves the artist's releases, captures current Spotify play counts (spending the account's credits), materializes an account-owned catalog from the resulting snapshot (idempotent - see [Create catalog](/api-reference/songs/catalogs-create)), and returns the catalog with its estimated value band. The owning account is taken from the credentials, never the body. The searched artist is also linked to the caller's roster (so a funnel signup lands with a populated `GET /api/artists` it can confirm); when the catalog's songs already resolve a canonical artist that one is used, otherwise the searched Spotify artist is linked directly. Synchronous: the request waits for the capture to land (typically under two minutes). Each run also persists a row in the catalog's valuation history, readable via [Get Catalog Valuations](/api-reference/songs/catalog-valuations).",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reconcile per-run persistence with daily deduplication.

These descriptions say each run writes a row, but also say there is at most one row per catalog per day. Clarify whether the daily row is coalesced or replaced so clients can reason correctly about history length and week-over-week comparisons.

Also applies to: 1654-1654

🤖 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` at line 1417, Update the endpoint
descriptions at both referenced entries to explicitly reconcile per-run
valuation persistence with the one-row-per-catalog-per-day limit, stating
whether same-day runs are coalesced into one row or replace the existing row.
Clarify the resulting history-length and week-over-week comparison behavior
without changing unrelated endpoint details.

"security": [
{
"apiKeyAuth": []
Expand Down Expand Up @@ -1649,6 +1649,85 @@
}
}
},
"/api/catalogs/{catalogId}/valuations": {
"get": {
"description": "Get the persisted valuation history for a catalog, latest first. A row is written each time a valuation band is computed for the whole catalog (valuation runs and measurement reads persist at most one row per catalog per day). Use limit=1 for the current value. History is what makes week-over-week deltas possible.",
"security": [
{
"apiKeyAuth": []
},
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "catalogId",
"in": "path",
"description": "The unique identifier of the catalog. The catalog must belong to the authenticated account. Malformed (non-uuid) values are rejected with 400.",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of valuation rows to return, latest first (default 30, max 100). limit=1 returns the current value. Invalid values are rejected with 400.",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 30
}
}
],
"responses": {
"200": {
"description": "The catalog's persisted valuation rows, latest first. Empty when no valuation has been persisted yet.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CatalogValuationsResponse"
}
}
}
},
"400": {
"description": "Malformed catalogId or limit",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Missing or invalid credentials",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Catalog not found or not owned by the authenticated account",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/api/catalogs/{catalogId}/measurements": {
"get": {
"description": "Get the latest play counts and a derived valuation band for a catalog. Measurements are captured by [Create measurement job](/api-reference/research/measurement-jobs) runs; the band is computed at read time from the latest capture per song.",
Expand Down Expand Up @@ -2821,6 +2900,61 @@
}
}
},
"CatalogValuationsResponse": {
"type": "object",
"required": [
"status",
"valuations"
],
"properties": {
"status": {
"type": "string",
"example": "success"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The status property in CatalogValuationsResponse uses "example": "success" and lacks a description, but every other schema in this file that defines a status field uses "enum" (e.g., ["success"], ["error"], or ["success", "error"]) plus a "description". Since this endpoint returns 200 only on success (errors use 4xx HTTP responses), align with the established pattern by replacing example with enum: ["success"] and adding description: "Status of the request".

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api-reference/openapi/releases.json, line 2912:

<comment>The `status` property in `CatalogValuationsResponse` uses `"example": "success"` and lacks a `description`, but every other schema in this file that defines a `status` field uses `"enum"` (e.g., `["success"]`, `["error"]`, or `["success", "error"]`) plus a `"description"`. Since this endpoint returns 200 only on success (errors use 4xx HTTP responses), align with the established pattern by replacing `example` with `enum: ["success"]` and adding `description: "Status of the request"`.</comment>

<file context>
@@ -2821,6 +2900,61 @@
+        "properties": {
+          "status": {
+            "type": "string",
+            "example": "success"
+          },
+          "valuations": {
</file context>

},
Comment on lines +2910 to +2913

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Constrain successful response status to success.

The example implies "success", but the schema accepts any string. Add enum: ["success"] so generated clients receive the actual 200-response contract.

Proposed fix
 "status": {
   "type": "string",
+  "enum": ["success"],
   "example": "success"
 },
📝 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
"status": {
"type": "string",
"example": "success"
},
"status": {
"type": "string",
"enum": ["success"],
"example": "success"
},
🤖 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 2910 - 2913, Update the
successful response status schema near the existing status property to add an
enum constraint containing only "success", while preserving its string type and
example.

"valuations": {
"type": "array",
"description": "Persisted valuation rows, latest first.",
"items": {
"type": "object",
"required": [
"low",
"mid",
"high",
"measured_song_count",
"total_streams",
"measured_at"
],
"properties": {
"low": {
"type": "number",
"description": "Low end of the estimated catalog value band, USD."
},
"mid": {
"type": "number",
"description": "Midpoint of the estimated catalog value band, USD."
},
"high": {
"type": "number",
"description": "High end of the estimated catalog value band, USD."
},
"measured_song_count": {
"type": "integer",
"description": "Songs measured in the capture this valuation was computed from."
},
"total_streams": {
"type": "integer",
"description": "Whole-catalog lifetime stream total at measurement time."
},
"measured_at": {
"type": "string",
"format": "date-time",
"description": "When the underlying measurement was taken."
}
}
}
}
}
},
"CreateArtistRequest": {
"type": "object",
"required": [
Expand Down
4 changes: 4 additions & 0 deletions api-reference/songs/catalog-valuations.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: Get Catalog Valuations
openapi: get /api/catalogs/{catalogId}/valuations
---
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
"api-reference/songs/catalogs-create",
"api-reference/songs/valuation-run",
"api-reference/songs/catalog-measurements",
"api-reference/songs/catalog-valuations",
"api-reference/songs/catalog-songs",
"api-reference/songs/catalog-songs-add",
"api-reference/songs/catalog-songs-delete"
Expand Down