{
  "openapi": "3.1.0",
  "info": {
    "title": "Recoup API - Releases",
    "description": "API documentation for the Recoup platform - an AI agent platform for the music industry",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.recoupable.dev"
    }
  ],
  "paths": {
    "/api/tasks": {
      "get": {
        "description": "Retrieve scheduled tasks. Each task includes `recent_runs` (last 5 runs), `upcoming` (next scheduled run times) sourced directly from the Trigger.dev API, and `owner_email` when an account email exists for the task owner. Supports filtering by id, account_id, or artist_account_id.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Filter by task ID (UUID). Returns a single task matching the provided ID. Admin callers may retrieve any task by ID regardless of owner; non-admin callers only receive the task if it belongs to their authenticated account.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "description": "Filter tasks to only include those for the specified account.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "artist_account_id",
            "in": "query",
            "description": "Filter tasks to only include those for the specified artist account.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tasks retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TasksResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is outside caller authorization scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error while retrieving tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Create a new scheduled task that runs a prompt against an artist on a recurring cron schedule. The response matches the [GET endpoint](/api-reference/tasks/get) (a `TasksResponse` with the created task in the `tasks` array).",
        "requestBody": {
          "description": "Task to create",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTaskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TasksResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing required fields or invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account's plan does not allow this task. `limit` is `task_count` when the account already has `task_limit` enabled tasks, or `min_cadence` when the cron schedule fires more often than `min_cadence_minutes` allows. Nothing is written; upgrade at `billingUrl`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanLimitError"
                },
                "examples": {
                  "taskCount": {
                    "summary": "Free account creating a second task",
                    "value": {
                      "status": "error",
                      "error": "plan_limit",
                      "limit": "task_count",
                      "message": "Free includes 1 task. Starter includes 3, Pro is unlimited.",
                      "plan": "free",
                      "task_limit": 1,
                      "min_cadence_minutes": 10080,
                      "current_task_count": 1,
                      "billingUrl": "https://app.recoupable.dev/plan"
                    }
                  },
                  "minCadence": {
                    "summary": "Free account scheduling a daily task",
                    "value": {
                      "status": "error",
                      "error": "plan_limit",
                      "limit": "min_cadence",
                      "message": "Free runs tasks weekly at most. Starter runs daily, Pro runs hourly.",
                      "plan": "free",
                      "task_limit": 1,
                      "min_cadence_minutes": 10080,
                      "current_task_count": 0,
                      "billingUrl": "https://app.recoupable.dev/plan"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is outside caller authorization scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update scheduled task",
        "description": "Update an existing scheduled task. Only the id field is required; any additional fields you include will be updated on the task. The response shape matches the GET endpoint (an array containing the updated task).",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "JSON object with `id` and optional fields to merge onto the task.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTaskRequest"
              },
              "examples": {
                "updateTitleAndSchedule": {
                  "summary": "Change title and cron",
                  "value": {
                    "id": "aade2bce-55c7-468e-a606-c4e76fb2ea2a",
                    "title": "Weekly Genre Pulse Check (rev)",
                    "schedule": "0 10 * * 4"
                  }
                },
                "toggleEnabled": {
                  "summary": "Pause or resume a task",
                  "value": {
                    "id": "aade2bce-55c7-468e-a606-c4e76fb2ea2a",
                    "enabled": false
                  }
                },
                "changeModel": {
                  "summary": "Switch model only",
                  "value": {
                    "id": "aade2bce-55c7-468e-a606-c4e76fb2ea2a",
                    "model": "claude-sonnet-4-20250514"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task updated successfully. Body is a `TasksResponse` with the updated task in `tasks` (typically one task).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TasksResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — missing `id`, empty strings where a field is provided, or other validation failure from the request body. The body reports the first Zod issue via `missing_fields` and `error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateTaskValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account's plan does not allow this task. `limit` is `task_count` when the account already has `task_limit` enabled tasks, or `min_cadence` when the cron schedule fires more often than `min_cadence_minutes` allows. Nothing is written; upgrade at `billingUrl`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanLimitError"
                },
                "examples": {
                  "taskCount": {
                    "summary": "Free account re-enabling a disabled task while another is enabled",
                    "value": {
                      "status": "error",
                      "error": "plan_limit",
                      "limit": "task_count",
                      "message": "Free includes 1 task. Starter includes 3, Pro is unlimited.",
                      "plan": "free",
                      "task_limit": 1,
                      "min_cadence_minutes": 10080,
                      "current_task_count": 1,
                      "billingUrl": "https://app.recoupable.dev/plan"
                    }
                  },
                  "minCadence": {
                    "summary": "Free account changing a task to a daily schedule",
                    "value": {
                      "status": "error",
                      "error": "plan_limit",
                      "limit": "min_cadence",
                      "message": "Free runs tasks weekly at most. Starter runs daily, Pro runs hourly.",
                      "plan": "free",
                      "task_limit": 1,
                      "min_cadence_minutes": 10080,
                      "current_task_count": 0,
                      "billingUrl": "https://app.recoupable.dev/plan"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is outside caller authorization scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No task exists with the given `id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateTaskFailureResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown task id",
                    "value": {
                      "status": "error",
                      "error": "Task not found"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error while updating the task or syncing the Trigger.dev schedule.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateTaskFailureResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete scheduled task",
        "description": "Delete an existing scheduled task by `id`. Returns only the delete operation status.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "JSON object containing the task `id` to delete.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteTaskRequest"
              },
              "examples": {
                "deleteTask": {
                  "summary": "Delete one task",
                  "value": {
                    "id": "aade2bce-55c7-468e-a606-c4e76fb2ea2a"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTaskResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — missing `id` or invalid request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTaskValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is outside caller authorization scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No task exists with the given `id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTaskFailureResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown task id",
                    "value": {
                      "status": "error",
                      "error": "Task not found"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error while deleting the task or deleting its Trigger.dev schedule.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTaskFailureResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/tasks/runs": {
      "get": {
        "summary": "Get task runs",
        "description": "Returns task runs for the authenticated account. When `runId` is provided, the response contains that single run (`runs` length 1) or 404 if not found. When `runId` is omitted, returns recent runs filtered by account context (default authenticated account, or `account_id` override when authorized).",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "query",
            "description": "The unique identifier of a specific task run to retrieve. If omitted, returns a list of recent runs for the authenticated account.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of runs to return when listing (ignored when runId is provided). Default 20, max 100.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "description": "Filter runs by account ID. When provided, returns runs tagged with account:<account_id>. Only applicable when the authenticated account has access to multiple accounts via organization membership.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task runs retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRunListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid query parameters (for example non-numeric `limit`, out-of-range `limit`, or invalid empty values).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRunsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRunsErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is not accessible with the provided credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRunsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Task run not found (only when runId is provided)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRunsErrorResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown run id",
                    "value": {
                      "status": "error",
                      "error": "Task run not found"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error while fetching runs from Trigger.dev.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRunsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists": {
      "get": {
        "description": "Retrieve artists accessible to the authenticated account. The account is derived from the API key or Bearer token. When org_id is omitted, returns only the account's own artists. Pass org_id to view artists in a specific organization. Pass account_id to filter to a specific account the API key has access to.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "description": "Filter to a specific account's artists. Only applicable when the authenticated account has access to multiple accounts via organization membership.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "org_id",
            "in": "query",
            "description": "Filter to artists in a specific organization. When omitted, returns only personal (non-organization) artists.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artists retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - account_id is not accessible with the provided credentials"
          }
        }
      },
      "post": {
        "description": "Create a new artist account. When spotify_artist_id is provided and a canonical artist already exists for it, the existing artist is linked to the account instead of creating a duplicate. The artist can optionally be linked to an organization.",
        "requestBody": {
          "description": "Artist creation parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateArtistRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing canonical artist for the given spotify_artist_id linked to the account (no new artist created)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateArtistResponse"
                }
              }
            }
          },
          "201": {
            "description": "Artist created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateArtistResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - validation error or invalid JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateArtistError"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/{id}/pin": {
      "post": {
        "description": "Pin an artist. This updates the caller's pinned preference for the artist.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Artist ID.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist pinned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid artist ID path parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - the authenticated account cannot access this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Artist not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Unpin an artist. This updates the caller's pinned preference for the artist.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Artist ID.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist unpinned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid artist ID path parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - the authenticated account cannot access this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Artist not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinArtistErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/{id}": {
      "patch": {
        "description": "Update an artist. All body fields are optional, but at least one must be provided. Use this endpoint to set basic profile (name, image, label), AI instructions, knowledge base entries, social profile URLs (mapped by platform), and pinned state for the calling account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Artist account ID to update.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "Fields to update. At least one field must be provided.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateArtistRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Artist updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateArtistResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - validation error (e.g. invalid image URL, no fields provided)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateArtistErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - the authenticated account cannot update this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateArtistErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Artist not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateArtistErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateArtistErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Delete an artist accessible to the authenticated account. This removes the caller's direct artist link and, if no owner links remain, deletes the artist account itself.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Artist account ID to delete.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "Optional account context override.",
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID of the account whose artist should be deleted. Only applicable when the authenticated account has access to multiple accounts via organization membership or Recoup admin access. If not provided, the deletion runs in the API key's own account context."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Artist deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - account_id is not a valid UUID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - the authenticated account cannot delete this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Artist not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/{id}/profile": {
      "get": {
        "description": "Public artist profile: the artist's name, image, connected social profiles, and linked catalogs in one call.\n\n**No authentication.** This endpoint is deliberately public and unbilled — it backs the shareable artist page at `chat.recoupable.dev/artists/{id}` and can be called without an API key. Only public fields are returned; account settings, instructions, and valuation data are never included.\n\nResponses are cacheable (`Cache-Control: public, s-maxage=300, stale-while-revalidate=600`), so a value may be up to a few minutes stale.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the artist account.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist profile retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistPublicProfileResponse"
                }
              }
            }
          },
          "404": {
            "description": "No artist with this id. Returned for unknown ids and for accounts that are not artists — the two cases are deliberately indistinguishable, so the endpoint cannot be used to probe which account ids exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistPublicProfileErrorResponse"
                },
                "example": {
                  "status": "error",
                  "error": "Artist not found"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/{id}/socials": {
      "get": {
        "description": "Retrieve all social media profiles associated with an artist, with the latest follower counts from the most recent scrape. Call this before the Social Posts or Social Scrape endpoints to obtain the social IDs. Pass `history` to also get each profile's follower snapshots, so week-over-week movement is a read instead of something the caller has to remember.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the artist account to fetch social profiles for",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "The page number to retrieve (default: 1)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "The number of records per page (default: 20, max: 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "name": "history",
            "in": "query",
            "description": "Number of days of follower snapshots to include per profile as `history`, newest first. Snapshots are written by every scrape that reports a follower count, one point per social per day (the latest scrape that day wins), so a profile has one point after its first scrape and a second only after a scrape on a later day. Omitted: no `history` field on the profiles.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90
            },
            "example": 14
          }
        ],
        "responses": {
          "200": {
            "description": "Social profiles retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid `id`, `page`, `limit` or `history`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller does not have access to this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Artist not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/{id}/socials/{socialId}": {
      "delete": {
        "description": "Remove a social profile link from an artist. Use this to correct a wrongly auto-matched social. This unlinks the social from the artist account; it does not delete the underlying social record, which may still be linked to other accounts.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the artist account the social is linked to",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "socialId",
            "in": "path",
            "description": "The unique identifier (UUID) of the social profile to unlink from the artist",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Social profile unlinked successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistSocialResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - id or socialId is not a valid UUID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - the authenticated account cannot access this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Artist not found or the social is not linked to this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteArtistErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/artist/socials/scrape": {
      "post": {
        "description": "Trigger scrape jobs for all social profiles linked to an artist. Returns a runId per social profile that you can poll for status and results via the [Scraper Results API](/api-reference/apify/scraper).",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Artist to scrape socials for",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArtistSocialsScrapeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scrape jobs triggered successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsScrapeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing required parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits — the body includes a static `billingUrl`. No Stripe object is created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsInsufficientCreditsResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller does not have access to this artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSocialsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/{id}/fans": {
      "get": {
        "description": "Retrieve the social profiles of an artist's fans: the accounts that commented on the artist's scraped posts (Instagram today). Every Instagram scrape is followed by one batched profile run over the new commenters, which fills `avatar`, `bio`, `follower_count` and `following_count` on each fan; `region` is null unless the platform reports one. Supports pagination for large fan lists.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the artist account to fetch fans for",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "The page number to retrieve (default: 1)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "The number of records per page (default: 20, max: 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist fans retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistFansResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing required parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistFansErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistFansErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistFansErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/songs": {
      "get": {
        "description": "Retrieve songs from the database with optional filtering by ISRC (International Standard Recording Code) or artist account. This endpoint joins the songs table with song_artists and accounts tables to provide comprehensive song information.",
        "parameters": [
          {
            "name": "isrc",
            "in": "query",
            "description": "International Standard Recording Code to filter by specific song",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "artist_account_id",
            "in": "query",
            "description": "Artist account ID to filter songs by artist",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Songs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongsErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongsErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Bulk create or fetch songs 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 create or fetch",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSongsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Songs created or fetched successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/catalogs": {
      "get": {
        "description": "Retrieve the catalogs visible to a specific account. Returns the catalogs the account owns directly, plus the catalogs owned by any organization the account belongs to - so every member of an organization sees that organization's catalogs without passing the organization's id. A catalog owned both directly and through an organization is returned once. An account that belongs to no organization receives only its own catalogs. Create an organization-owned catalog with [Create catalog](/api-reference/songs/catalogs-create) or [Run valuation](/api-reference/songs/valuation-run).\n\nEach catalog carries its estimated value band and its owner, so a list of catalogs can be ranked by worth and a member can tell an organization's catalog from their own. `valuation` is null for a catalog with no measured songs; `owner.image` is null when the owner account has no avatar.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the account to query. The catalogs of every organization this account belongs to are included in the response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalogs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid account id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller lacks access to the target account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/catalogs": {
      "post": {
        "description": "Create a catalog, owned by the calling account or - with organization_id - by one of the caller's organizations, in which case every member of that organization sees it via [Get Catalogs](/api-reference/songs/catalogs). When materializing from a valuation snapshot, the endpoint also attaches the snapshot's canonical artist to the caller's roster: the measured songs' existing song-artist links are resolved to the dominant artist account, which is added to the caller's roster if not already present. The attach also runs on idempotent re-claims of an already-materialized snapshot. The roster attach always targets the calling account, even when the catalog is owned by an organization.",
        "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, or the caller is not a member of the organization named by organization_id",
            "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/runs": {
      "get": {
        "summary": "Get runs",
        "description": "Get background runs, newest first. A run is the generic status resource for long-running work. `kind` selects the run type (`valuation` and `music` today); future kinds are new enum values here, never new endpoints.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "valuation",
                "music"
              ]
            },
            "description": "Required. The run type to list. Unknown kinds are rejected with 400."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 1
            },
            "description": "Optional. Maximum runs to return, newest first. Defaults to 1 (the latest run)."
          }
        ],
        "responses": {
          "200": {
            "description": "The calling account's runs of the requested kind, newest first. Empty when the account has never run one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetRunsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing or unknown kind, or invalid limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/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 a catalog from the resulting snapshot (idempotent - see [Create catalog](/api-reference/songs/catalogs-create)), and returns the catalog with its estimated value band. The catalog is owned by the calling account unless organization_id names one of the caller's organizations to own it instead, in which case every member of that organization sees it. The calling account is always taken from the credentials, never the body, and it is the account whose credits are spent. 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).",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "The Spotify artist to value.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunValuationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Catalog created (or idempotently re-fetched) with its estimated value band.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunValuationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid spotify_artist_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits to measure the catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the caller is not a member of the organization named by organization_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found - no releases found for the Spotify artist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "504": {
            "description": "The measurement did not complete within the wait window - retry shortly",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/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": [
          {
            "name": "catalog_id",
            "in": "query",
            "description": "The unique identifier of the catalog to query songs for",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "artistName",
            "in": "query",
            "description": "Optional. Filters songs to only include those with matching artist name",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number for pagination (default: 1)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of songs per page (default: 20, max: 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalog songs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid catalog_id",
            "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 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": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          }
        }
      },
      "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",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddCatalogSongsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Songs added to catalog successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing required fields",
            "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 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",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteCatalogSongsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Songs removed from catalog successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing catalog_id or isrc",
            "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 catalog does not belong to the authenticated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/catalogs/{catalogId}": {
      "patch": {
        "description": "Rename a catalog. The only mutable field is the display name — catalog membership is managed through [Add catalog songs](/api-reference/songs/catalog-songs-add) and [Remove catalog songs](/api-reference/songs/catalog-songs-delete).\n\nCatalogs created by [Run valuation](/api-reference/songs/valuation-run) are named after the measured artist. Catalogs created before that shipped are all named `Valuation Catalog` — this endpoint is how you give them real names.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "catalogId",
            "in": "path",
            "description": "ID of the catalog to rename.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "The new catalog name.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCatalogRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Catalog renamed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateCatalogResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - catalogId is not a valid UUID, or name is missing or empty",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Catalog not found - no catalog with this ID is visible to the caller. A catalog that exists but belongs to neither the authenticated account nor one of its organizations returns 404, not 403: the same visibility rule the catalog read paths use, so a catalog you cannot see is indistinguishable from one that does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Delete a catalog. Removes the catalog, its song membership (catalog_songs), its ownership links (account_catalogs) and its stored valuation history (catalog_valuations).\n\nThe measurement that produced the catalog is **not** deleted. A playcount snapshot is a metered capture that spent credits and cannot be reproduced for a past date, while a catalog is a label over songs that can be rebuilt from it in one call — so deleting a catalog only clears the snapshot's reference to it. The snapshot then reads as unclaimed, and [Create catalog](/api-reference/songs/catalogs-create) can re-materialize it under a correct name. The released snapshot IDs are returned so that recovery does not require hunting for them.\n\nUse this for genuine duplicates. To fix a catalog's name, use [Rename catalog](/api-reference/songs/catalog-rename) instead — it keeps the catalog's measurements and valuation history intact.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "catalogId",
            "in": "path",
            "description": "ID of the catalog to delete.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalog deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteCatalogResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - catalogId is not a valid UUID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Catalog not found - no catalog with this ID is visible to the caller. A catalog that exists but belongs to neither the authenticated account nor one of its organizations returns 404, not 403: the same visibility rule the catalog read paths use, so a catalog you cannot see is indistinguishable from one that does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/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.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "catalogId",
            "in": "path",
            "description": "The unique identifier of the catalog to read measurements for. The catalog must belong to the authenticated account. Malformed (non-uuid) values are rejected with 400.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "description": "Act on behalf of this account (UUID). Only applicable when the authenticated account has access to the target account, for example an organization key acting for a member account. The catalog ownership check then runs against this account instead of the caller. Omitted: the caller's own account.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "artist_account_id",
            "in": "query",
            "description": "Optional artist account to scope the read to. When present, measurements and the valuation band cover only the catalog's songs linked to this artist account (via song_artists); when absent, the whole catalog. A song linked to multiple artists counts for each of them. An artist with no linked measured songs in the catalog yields measured_song_count 0 and an empty measurements array. Malformed (non-uuid) values are rejected with 400.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number of the measurements array (default 1). Pagination only windows the measurements rows - the aggregates always cover the entire scope. Invalid values are rejected with 400.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of measurement rows per page (default 50, max 100). Invalid values are rejected with 400.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Latest per-song measurements and the derived valuation band",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogMeasurementsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - malformed catalogId or artist_account_id, or invalid page/limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the catalog does not exist or belongs to a different account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSongsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/songs/analyze/presets": {
      "get": {
        "description": "Lists all available music analysis presets. Each preset is a curated prompt with optimized generation parameters for a specific use case (e.g. catalog metadata enrichment, sync licensing analysis, audience profiling). Requires authentication via API key or Bearer token.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Presets listed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ]
                    },
                    "presets": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "Preset identifier to pass in the analyze request"
                          },
                          "label": {
                            "type": "string",
                            "description": "Human-readable preset name"
                          },
                          "description": {
                            "type": "string",
                            "description": "What this preset does"
                          },
                          "requiresAudio": {
                            "type": "boolean",
                            "description": "Always `true`: every analyze request requires `audio_url`, whichever preset or prompt it uses. Kept on the response for compatibility.",
                            "example": true
                          },
                          "responseFormat": {
                            "type": "string",
                            "enum": [
                              "json",
                              "text"
                            ],
                            "description": "Expected response format"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — invalid or missing API key / Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/songs/analyze": {
      "post": {
        "description": "Analyze music using a state-of-the-art Audio Language Model ([Music Flamingo](https://www.universalmusic.com/universal-music-group-to-transform-music-experience-for-billions-of-fans-with-nvidia-ai/)) that listens directly to the audio waveform.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Music analysis request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SongAnalyzeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Music analysis completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — missing or invalid fields, including a missing `audio_url` (`missing_fields: [\"audio_url\"]`) or both `preset` and `prompt` in one request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — invalid or missing API key / Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits — the balance cannot cover the base price of this request (`$0.05` per model call, `$0.65` for `full_report`). The model is not called, nothing is charged, and no Stripe object is created. The body includes a static `billingUrl`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeInsufficientCreditsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Unverifiable audio. Before the model is called, `audio_url` is checked with a `HEAD` request (a ranged `GET` for hosts that reject `HEAD`). It must answer 2xx within 10 seconds with an `audio/*` or `application/octet-stream` content type. When it does not, the model is not called and nothing is charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeAudioUrlErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error — upstream model unavailable or inference failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Recoup API key. [Learn more](/quickstart#api-keys)."
      }
    },
    "schemas": {
      "AddCatalogSongInput": {
        "type": "object",
        "required": [
          "catalog_id",
          "isrc"
        ],
        "properties": {
          "catalog_id": {
            "type": "string",
            "format": "uuid",
            "description": "Catalog ID to which the song will be added"
          },
          "isrc": {
            "type": "string",
            "description": "Song ISRC to associate to the catalog"
          },
          "name": {
            "type": "string",
            "description": "Optional. Applied only if internal search cannot find valid info for ISRC"
          },
          "album": {
            "type": "string",
            "description": "Optional. Applied only if internal search cannot find valid info for ISRC"
          },
          "notes": {
            "type": "string",
            "description": "Optional. Applied only if internal search cannot find valid info for ISRC"
          },
          "artists": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional array of artist names. Applied only if internal search lacks info"
          }
        }
      },
      "AddCatalogSongsRequest": {
        "type": "object",
        "required": [
          "songs"
        ],
        "properties": {
          "songs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AddCatalogSongInput"
            },
            "description": "Array of songs for batch updates"
          }
        }
      },
      "ApifyRunResult": {
        "type": "object",
        "properties": {
          "runId": {
            "type": "string",
            "description": "Unique identifier for the Apify run"
          },
          "datasetId": {
            "type": "string",
            "description": "Unique identifier for the dataset containing scraped data"
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Error message if the run failed (null if successful)"
          }
        }
      },
      "Artist": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account. Equal to account_id; use this (or account_id) for /api/artists/{id}/* sub-resources."
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account"
          },
          "name": {
            "type": "string",
            "description": "Artist display name"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Artist profile image URL"
          },
          "instruction": {
            "type": "string",
            "nullable": true,
            "description": "Artist-specific agent instruction / system prompt"
          },
          "knowledges": {
            "type": "array",
            "description": "Artist knowledge-base entries",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                }
              }
            }
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Artist label / category"
          },
          "pinned": {
            "type": "boolean",
            "description": "Whether the account has pinned this artist"
          },
          "account_socials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArtistSocial"
            },
            "description": "Social media profiles linked to the artist (embedded in the /api/artists response)"
          }
        }
      },
      "ArtistFan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the fan's social profile"
          },
          "username": {
            "type": "string",
            "description": "Username or handle on the platform"
          },
          "avatar": {
            "type": "string",
            "description": "URL to the fan's avatar/profile image"
          },
          "profile_url": {
            "type": "string",
            "description": "Full URL to the fan's profile on the platform"
          },
          "region": {
            "type": "string",
            "description": "Geographic region or location of the fan"
          },
          "bio": {
            "type": "string",
            "description": "Fan's biography or profile description"
          },
          "follower_count": {
            "type": "integer",
            "description": "Number of followers the fan has"
          },
          "following_count": {
            "type": "integer",
            "description": "Number of accounts the fan is following"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the fan data was last updated"
          }
        }
      },
      "ArtistFansErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "ArtistFansPagination": {
        "type": "object",
        "properties": {
          "total_count": {
            "type": "integer",
            "description": "Total number of records available"
          },
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "limit": {
            "type": "integer",
            "description": "Number of records per page"
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages available"
          }
        }
      },
      "ArtistFansResponse": {
        "type": "object",
        "required": [
          "status",
          "fans",
          "pagination"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "fans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArtistFan"
            },
            "description": "List of social profiles from fans across all platforms"
          },
          "pagination": {
            "$ref": "#/components/schemas/ArtistFansPagination",
            "description": "Pagination metadata for the response"
          }
        }
      },
      "ArtistProfile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the artist"
          },
          "profiles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArtistProfileSocialProfile"
            },
            "description": "List of social media profiles"
          },
          "total_followers": {
            "type": "integer",
            "description": "Sum of followers across all platforms"
          },
          "total_following": {
            "type": "integer",
            "description": "Sum of following across all platforms"
          },
          "total_posts": {
            "type": "integer",
            "description": "Sum of posts across all platforms"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the data was last updated"
          }
        }
      },
      "ArtistProfileSocialProfile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the social profile"
          },
          "username": {
            "type": "string",
            "description": "Username on the platform"
          },
          "profile_url": {
            "type": "string",
            "description": "Direct URL to the profile"
          },
          "avatar": {
            "type": "string",
            "nullable": true,
            "description": "URL to the profile avatar image"
          },
          "bio": {
            "type": "string",
            "nullable": true,
            "description": "Profile biography or description"
          },
          "follower_count": {
            "type": "integer",
            "nullable": true,
            "description": "Number of followers on this platform"
          },
          "following_count": {
            "type": "integer",
            "nullable": true,
            "description": "Number of accounts followed on this platform"
          },
          "post_count": {
            "type": "integer",
            "nullable": true,
            "description": "Number of posts on this platform"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "description": "Geographic region of the profile"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the profile was last updated"
          }
        }
      },
      "ArtistSocial": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the social profile"
          },
          "platform": {
            "type": "string",
            "description": "Social media platform (e.g., instagram, twitter, tiktok)"
          },
          "username": {
            "type": "string",
            "description": "Username on the platform"
          },
          "profile_url": {
            "type": "string",
            "description": "Full URL to the social media profile"
          }
        }
      },
      "ArtistPublicProfileErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error`."
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "Artist not found"
          }
        }
      },
      "ArtistPublicProfileResponse": {
        "type": "object",
        "required": [
          "id",
          "name",
          "image",
          "socials",
          "catalogs",
          "valuation"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The artist's account id."
          },
          "name": {
            "type": "string",
            "description": "The artist's display name."
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "URL of the artist's profile image, or null when none is set."
          },
          "socials": {
            "type": "array",
            "description": "Connected social profiles, empty when none are linked.",
            "items": {
              "$ref": "#/components/schemas/ArtistPublicProfileSocial"
            }
          },
          "catalogs": {
            "type": "array",
            "description": "Catalogs linked to the artist, newest first; empty when none are linked.",
            "items": {
              "$ref": "#/components/schemas/ArtistPublicProfileCatalog"
            }
          },
          "valuation": {
            "$ref": "#/components/schemas/ArtistPublicProfileValuation",
            "description": "Estimated value band for the artist's catalog(s), from the published Recoup valuation model over all credited songs' latest play counts. Null when the artist has no measured songs.",
            "nullable": true
          }
        }
      },
      "ArtistPublicProfileSocial": {
        "type": "object",
        "required": [
          "type",
          "username",
          "profile_url"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Platform name derived from the profile URL, e.g. SPOTIFY, INSTAGRAM, TIKTOK, YOUTUBE.",
            "example": "SPOTIFY"
          },
          "username": {
            "type": "string",
            "nullable": true,
            "description": "The artist's handle on the platform, when known."
          },
          "profile_url": {
            "type": "string",
            "description": "Public URL of the profile on the platform."
          }
        }
      },
      "ArtistPublicProfileCatalog": {
        "type": "object",
        "required": [
          "id",
          "name",
          "song_count",
          "updated_at",
          "songs"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The catalog id."
          },
          "name": {
            "type": "string",
            "description": "The catalog's display name."
          },
          "song_count": {
            "type": "integer",
            "description": "Number of songs currently in the catalog."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the catalog last changed."
          },
          "songs": {
            "type": "array",
            "description": "The catalog's songs crediting this artist, sorted by plays descending and capped at the top 50 — `song_count` reflects the full catalog and may exceed `songs.length`.",
            "items": {
              "$ref": "#/components/schemas/ArtistPublicProfileSong"
            }
          }
        }
      },
      "ArtistPublicProfileSong": {
        "type": "object",
        "required": [
          "isrc",
          "name",
          "album",
          "artwork_url",
          "plays",
          "est_value_usd"
        ],
        "properties": {
          "isrc": {
            "type": "string",
            "description": "The song's ISRC.",
            "example": "QZTPX2553097"
          },
          "name": {
            "type": "string",
            "description": "The song's title."
          },
          "album": {
            "type": "string",
            "nullable": true,
            "description": "The album or release the song belongs to, when known."
          },
          "artwork_url": {
            "type": "string",
            "nullable": true,
            "description": "Apple Music artwork URL for the song's release, or null when artwork has not been resolved yet."
          },
          "plays": {
            "type": "integer",
            "description": "The song's latest captured Spotify play count. Captures are periodic, so this can lag the live number."
          },
          "est_value_usd": {
            "type": "number",
            "description": "Estimated value of the song in USD — the mid point of the published Recoup valuation model applied to this song's plays."
          }
        }
      },
      "ArtistPublicProfileValuation": {
        "type": "object",
        "required": [
          "low",
          "mid",
          "high"
        ],
        "properties": {
          "low": {
            "type": "number",
            "description": "Low end of the estimated catalog value band, USD."
          },
          "mid": {
            "type": "number",
            "description": "Mid estimate of the catalog value, USD."
          },
          "high": {
            "type": "number",
            "description": "High end of the estimated catalog value band, USD."
          }
        }
      },
      "ArtistSocialsErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "message": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "ArtistSocialsInsufficientCreditsResponse": {
        "type": "object",
        "description": "Returned (402) when the account lacks credits for the scrapes. Nothing is charged and no Stripe object is created. Credits must be bought explicitly via [`POST /api/credits/sessions`](/api-reference/credits/sessions-create).",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "insufficient_credits"
            ]
          },
          "remaining_credits": {
            "type": "integer",
            "example": 0
          },
          "required_credits": {
            "type": "integer",
            "example": 100
          },
          "billingUrl": {
            "type": "string",
            "description": "Static link to the Recoup app, where a human can save a card and buy credits. It is a constant, not a freshly minted Stripe Checkout Session, so a credit-gated endpoint that keeps returning 402 creates nothing. To buy credits programmatically, call [`POST /api/credits/sessions`](/api-reference/credits/sessions-create).",
            "example": "https://app.recoupable.dev"
          }
        },
        "required": [
          "error",
          "remaining_credits",
          "required_credits",
          "billingUrl"
        ]
      },
      "ArtistSocialsPagination": {
        "type": "object",
        "properties": {
          "total_count": {
            "type": "integer",
            "description": "Total number of social profiles available"
          },
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "limit": {
            "type": "integer",
            "description": "Number of social profiles per page"
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages available"
          }
        }
      },
      "ArtistSocialsResponse": {
        "type": "object",
        "required": [
          "status",
          "socials",
          "pagination"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "socials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SocialProfile"
            },
            "description": "List of social media profiles associated with the artist"
          },
          "pagination": {
            "$ref": "#/components/schemas/ArtistSocialsPagination",
            "description": "Pagination metadata for the response"
          }
        }
      },
      "ArtistSocialsScrapeRequest": {
        "type": "object",
        "required": [
          "artist_account_id"
        ],
        "properties": {
          "artist_account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account to scrape socials for",
            "example": "1873859c-dd37-4e9a-9bac-80d35a1b2c3d"
          },
          "posts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "example": 10,
            "description": "Recent-post depth applied to every profile in the batch. Honored on TikTok, YouTube (per content type), X and LinkedIn; ignored on Instagram (always the profile's latest posts, up to 12), Threads and Facebook. Without it TikTok, YouTube and X return a single latest item. Every returned post is stored with its engagement counts; see the per-platform table on [Social Scrape](/api-reference/social/scrape). Credits: `5 + posts` per profile."
          }
        }
      },
      "ArtistSocialsScrapeResponse": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/ApifyRunResult"
        },
        "description": "Array of Apify run results, one for each social profile scraped"
      },
      "ArtistsErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "message": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "ArtistsResponse": {
        "type": "object",
        "required": [
          "status",
          "artists"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artist"
            },
            "description": "List of artist objects"
          },
          "message": {
            "type": "string",
            "description": "Error message (only present if status is error)"
          }
        }
      },
      "RunValuationRequest": {
        "type": "object",
        "required": [
          "spotify_artist_id"
        ],
        "properties": {
          "spotify_artist_id": {
            "type": "string",
            "description": "The Spotify artist id to value (e.g. from a Spotify artist search). The endpoint resolves this artist's releases, measures them, and builds the catalog."
          },
          "organization_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional. ID of an organization to own the resulting catalog instead of the calling account. Every member of that organization then sees it in their own [Get Catalogs](/api-reference/songs/catalogs) response. The caller must be a member of the organization; otherwise the request is rejected with 403. Credits are still spent by the calling account, and the searched artist is still linked to the caller's roster. Omit to own the catalog personally, which is the default."
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional. Act on behalf of this account (UUID). Only applicable when the authenticated account has access to the target account, for example an organization key acting for a member account. Every side effect of the run - the playcount snapshot, the catalog claim, the roster attach, and the report email - is scoped to this account instead of the caller. Omit to run for the caller's own account, which is the default."
          }
        }
      },
      "MusicRun": {
        "type": "object",
        "description": "One music generation, surfaced as a run. `id` is the generation's id: read the full record, including its logs, with [Get Music Generation](/api-reference/music/get).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The generation's id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "music"
            ],
            "description": "The run type."
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "generating",
              "complete",
              "failed"
            ],
            "description": "Domain phase of the run: `queued` before the workflow picks it up, `generating` while the model renders, then `complete` or `failed`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the generation was created."
          },
          "result": {
            "type": "object",
            "nullable": true,
            "description": "Set once the generation completes; null before that.",
            "properties": {
              "generation_id": {
                "type": "string",
                "format": "uuid",
                "description": "The completed generation."
              },
              "audio_url": {
                "type": "string",
                "description": "Playable audio URL."
              }
            }
          }
        }
      },
      "ValuationRun": {
        "type": "object",
        "description": "One background run. `id` is opaque; `state` is a domain phase, not a storage value: `queued` (capture accepted, not yet scraping), `measuring` (capture in flight, or finished moments ago and being claimed), `claimed` (catalog materialized - `result.catalog_id` is set), `failed` (the capture finished but no catalog was claimed, or the capture itself failed).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Opaque run id. Do not infer anything from its format; it is stable for polling a single run across requests."
          },
          "kind": {
            "type": "string",
            "enum": [
              "valuation"
            ],
            "description": "The run type."
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "measuring",
              "claimed",
              "failed"
            ],
            "description": "Domain phase of the run."
          },
          "album_count": {
            "type": "integer",
            "description": "Number of releases in the run's capture scope."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the run was created."
          },
          "result": {
            "type": "object",
            "nullable": true,
            "description": "Set once the run is claimed; null before that.",
            "properties": {
              "catalog_id": {
                "type": "string",
                "format": "uuid",
                "description": "The materialized catalog. Read its value band via [Get Catalog Measurements](/api-reference/songs/catalog-measurements)."
              }
            }
          }
        }
      },
      "GetRunsResponse": {
        "type": "object",
        "description": "The calling account's runs of the requested kind, newest first.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "runs": {
            "type": "array",
            "description": "Runs, newest first. Empty when the account has never run one of this kind. The item shape follows the requested `kind`.",
            "items": {
              "oneOf": [
                {
                  "title": "Valuation run",
                  "$ref": "#/components/schemas/ValuationRun"
                },
                {
                  "title": "Music run",
                  "$ref": "#/components/schemas/MusicRun"
                }
              ]
            }
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "RunValuationResponse": {
        "type": "object",
        "description": "Result of a valuation run: the materialized catalog and its estimated value band.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "catalog": {
            "$ref": "#/components/schemas/Catalog"
          },
          "band": {
            "$ref": "#/components/schemas/ValuationBand"
          },
          "songs_measured": {
            "type": "integer",
            "description": "Number of tracks with a captured play count that were materialized into the catalog."
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "CreateCatalogRequest": {
        "type": "object",
        "description": "Request body for creating a catalog. At least one of name or snapshot must be supplied. The catalog is owned by the authenticated account unless organization_id names an organization to own it instead.",
        "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)."
          },
          "organization_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional. ID of an organization to own the catalog instead of the calling account. Every member of that organization then sees the catalog in their own [Get Catalogs](/api-reference/songs/catalogs) response, without passing the organization's id. The caller must be a member of the organization; otherwise the request is rejected with 403. Omit to own the catalog personally, which is the default."
          }
        }
      },
      "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')"
          }
        }
      },
      "UpdateCatalogRequest": {
        "type": "object",
        "description": "Request body for renaming a catalog.",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "New display name for the catalog. Must not be empty."
          }
        }
      },
      "UpdateCatalogResponse": {
        "type": "object",
        "description": "Response returned after renaming a catalog",
        "required": [
          "status",
          "catalog"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "catalog": {
            "$ref": "#/components/schemas/Catalog"
          }
        }
      },
      "DeleteCatalogResponse": {
        "type": "object",
        "description": "Response returned after deleting a catalog",
        "required": [
          "status",
          "catalog_id",
          "released_snapshot_ids"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "catalog_id": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the deleted catalog"
          },
          "released_snapshot_ids": {
            "type": "array",
            "description": "IDs of the playcount snapshots that pointed at this catalog. Those snapshots still exist - only their catalog reference was cleared - so each can be re-materialized into a new, correctly named catalog with Create catalog. Empty when the catalog was not created from a measurement run.",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "Catalog": {
        "type": "object",
        "description": "A catalog with its metadata",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the catalog"
          },
          "name": {
            "type": "string",
            "description": "Name of the catalog"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the catalog was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the catalog was last updated"
          }
        }
      },
      "CatalogOwner": {
        "type": "object",
        "description": "The account a catalog belongs to. A catalog owned both directly and through an organization reports the organization - that is the ownership worth surfacing to a member.",
        "required": [
          "id",
          "name",
          "image",
          "is_organization"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Owner account id"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Owner display name, null when the account has none"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Owner avatar URL from account_info, null when the account has no image. Clients should fall back to initials rather than render a broken image."
          },
          "is_organization": {
            "type": "boolean",
            "description": "True when the owner is an organization the caller belongs to, false when it is a personal account"
          }
        }
      },
      "CatalogListItem": {
        "type": "object",
        "description": "A catalog as returned by [Get catalogs](/api-reference/songs/catalogs): the catalog plus its estimated value and its owner. Only this list carries those two fields - the create and rename responses return the bare catalog.",
        "required": [
          "id",
          "name",
          "created_at",
          "updated_at",
          "measured_song_count",
          "valuation",
          "owner"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the catalog"
          },
          "name": {
            "type": "string",
            "description": "Name of the catalog"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the catalog was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the catalog was last updated"
          },
          "measured_song_count": {
            "type": "integer",
            "description": "Songs in the catalog with at least one play-count measurement. 0 for a catalog that was never measured, in which case valuation is null."
          },
          "valuation": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ValuationBand"
              },
              {
                "type": "null"
              }
            ],
            "description": "Estimated value band - the same schema and model as [Get catalog measurements](/api-reference/songs/catalog-measurements) returns, so a list and the report it opens cannot disagree. Null when nothing in the catalog has been measured; clients should say so rather than render $0."
          },
          "owner": {
            "$ref": "#/components/schemas/CatalogOwner"
          }
        }
      },
      "CatalogSong": {
        "type": "object",
        "description": "A song within a catalog with its metadata and associated artists",
        "properties": {
          "catalog_id": {
            "type": "string",
            "format": "uuid",
            "description": "Catalog ID this song entry is associated with"
          },
          "isrc": {
            "type": "string",
            "description": "International Standard Recording Code (primary key)"
          },
          "name": {
            "type": "string",
            "description": "Name of the song"
          },
          "album": {
            "type": "string",
            "description": "Name of the album the song belongs to"
          },
          "lyrics": {
            "type": "string",
            "description": "Full lyrics of the song"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the song data was last updated"
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SongArtist"
            },
            "description": "Array of artist objects associated with this song"
          }
        }
      },
      "CatalogSongsErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "CatalogSongsPagination": {
        "type": "object",
        "description": "Pagination metadata for catalog songs response",
        "properties": {
          "total_count": {
            "type": "integer",
            "description": "Total number of songs in the catalog"
          },
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "limit": {
            "type": "integer",
            "description": "Number of songs per page"
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages available"
          }
        }
      },
      "CatalogSongsResponse": {
        "type": "object",
        "description": "Response containing catalog songs data with pagination",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "songs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogSong"
            },
            "description": "Array of song objects with artist information"
          },
          "pagination": {
            "$ref": "#/components/schemas/CatalogSongsPagination",
            "description": "Pagination metadata for the response"
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "CatalogsErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "CatalogsResponse": {
        "type": "object",
        "description": "Response containing catalogs data",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "catalogs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogListItem"
            },
            "description": "Array of catalog objects, each with its estimated value and its owner"
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "CatalogMeasurementsResponse": {
        "type": "object",
        "description": "Latest per-song play counts for a catalog plus the valuation band derived from them",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "measurements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogTrackMeasurement"
            },
            "description": "One page of latest-per-ISRC measurements (the requested page/limit window), sorted by play count descending. One entry per measured song: the newest capture per ISRC; songs never measured are omitted. Use measured_song_count / pagination for the size of the full set - the aggregates are NOT limited to this page."
          },
          "pagination": {
            "$ref": "#/components/schemas/CatalogMeasurementsPagination"
          },
          "measured_song_count": {
            "type": "integer",
            "description": "Total number of measured songs in scope (the whole catalog, or the artist subset when filtered), computed over the entire scope regardless of pagination - no row cap"
          },
          "valuation": {
            "$ref": "#/components/schemas/ValuationBand"
          },
          "total_streams": {
            "type": "integer",
            "description": "Sum of the latest play counts across ALL measured songs in scope, computed in a single SQL aggregate regardless of pagination - no row cap"
          },
          "artist_account_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Echoes the applied artist_account_id filter: the uuid when the response was scoped to that artist, null when the response covers the whole catalog. Clients that request an artist scope should verify this echo before treating the numbers as artist-scoped."
          },
          "catalog_age_years": {
            "type": "integer",
            "description": "Catalog age in years used for the annual run-rate: from the earliest Spotify release date of the source run's albums, minimum 1; defaults to 5 when no release date is resolvable."
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "CatalogMeasurementsPagination": {
        "type": "object",
        "description": "Pagination metadata for the measurements array - the same envelope as catalog songs",
        "properties": {
          "total_count": {
            "type": "integer",
            "description": "Total number of measured songs in scope (equals measured_song_count)"
          },
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "limit": {
            "type": "integer",
            "description": "Number of measurement rows per page"
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages available"
          }
        }
      },
      "CatalogTrackMeasurement": {
        "type": "object",
        "description": "The most recent Spotify play-count measurement for one song (ISRC) in the catalog",
        "properties": {
          "isrc": {
            "type": "string",
            "description": "ISRC of the song"
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Song title, when known"
          },
          "playcount": {
            "type": "integer",
            "description": "Latest Spotify platform-displayed play count"
          },
          "measured_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the play count was captured"
          }
        }
      },
      "ValuationBand": {
        "type": "object",
        "description": "Estimated catalog value in USD, derived at read time from the latest measurements - the same model as the recoupable.dev valuation card. Annual run-rate = lifetime streams / catalog age (lifetime-average proxy), converted to net label share (all-DSP gross-up 1.25/1.4/1.6, 15% distribution fee, 25% royalty share, $0.0035 per Spotify stream) and multiplied by a 10x/13x/16x master-catalog market multiple.",
        "properties": {
          "low": {
            "type": "number",
            "description": "Low estimate in USD (1.25x gross-up, 10x multiple)"
          },
          "mid": {
            "type": "number",
            "description": "Central estimate in USD (1.4x gross-up, 13x multiple)"
          },
          "high": {
            "type": "number",
            "description": "High estimate in USD (1.6x gross-up, 16x multiple)"
          }
        }
      },
      "CreateArtistError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of missing or invalid field names"
          },
          "error": {
            "type": "string",
            "description": "Error message describing the validation failure"
          },
          "message": {
            "type": "string",
            "description": "Error message (for invalid JSON or other errors)"
          }
        }
      },
      "CatalogValuationsResponse": {
        "type": "object",
        "required": [
          "status",
          "valuations"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "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": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "The name of the artist to create"
          },
          "spotify_artist_id": {
            "type": "string",
            "description": "Optional Spotify artist id. When provided, the endpoint resolves-or-creates the canonical artist for that id: if an artist already carries it, that artist is linked to the account and returned (200) instead of creating a duplicate; otherwise the artist is created with its Spotify profile attached (201)."
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to create the artist for. Only applicable when the authenticated account has access to multiple accounts via organization membership. If not provided, the artist is created for the API key's own account."
          },
          "organization_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional organization ID to link the new artist to"
          }
        }
      },
      "CreateArtistResponse": {
        "type": "object",
        "required": [
          "artist"
        ],
        "properties": {
          "artist": {
            "$ref": "#/components/schemas/CreatedArtist"
          }
        }
      },
      "DeleteArtistErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "DeleteArtistResponse": {
        "type": "object",
        "required": [
          "success",
          "artistId"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the artist delete completed successfully"
          },
          "artistId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account that was deleted or unlinked"
          }
        }
      },
      "DeleteArtistSocialResponse": {
        "type": "object",
        "required": [
          "success",
          "socialId"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the social profile was unlinked successfully"
          },
          "socialId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the social profile that was unlinked from the artist"
          }
        }
      },
      "PinArtistErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of missing or invalid field names"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "PinArtistResponse": {
        "type": "object",
        "required": [
          "success",
          "artistId",
          "pinned"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the request succeeded"
          },
          "artistId": {
            "type": "string",
            "format": "uuid",
            "description": "The artist ID"
          },
          "pinned": {
            "type": "boolean",
            "description": "The current pinned state"
          }
        }
      },
      "CreateCatalogInput": {
        "type": "object",
        "required": [
          "account_id"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account to associate the catalog with"
          },
          "name": {
            "type": "string",
            "description": "Catalog name to create if catalog_id is omitted"
          },
          "catalog_id": {
            "type": "string",
            "format": "uuid",
            "description": "Existing catalog ID to link to the account"
          }
        }
      },
      "CreateCatalogsRequest": {
        "type": "object",
        "required": [
          "catalogs"
        ],
        "properties": {
          "catalogs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateCatalogInput"
            },
            "description": "Array of catalog inputs for bulk create/link operations"
          }
        }
      },
      "CreateSongInput": {
        "type": "object",
        "required": [
          "isrc"
        ],
        "properties": {
          "isrc": {
            "type": "string",
            "description": "International Standard Recording Code of the song to create or fetch"
          },
          "name": {
            "type": "string",
            "description": "Optional. Song name, applied only if internal search cannot find valid info"
          },
          "album": {
            "type": "string",
            "description": "Optional. Album name, applied only if internal search cannot find valid info"
          },
          "notes": {
            "type": "string",
            "description": "Optional. Notes for the song, applied only if internal search cannot find valid info"
          },
          "artists": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional array of artist names, applied only if internal search cannot find valid info"
          }
        }
      },
      "CreateSongsRequest": {
        "type": "object",
        "required": [
          "songs"
        ],
        "properties": {
          "songs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateSongInput"
            },
            "description": "Array of song inputs for bulk create/fetch"
          }
        }
      },
      "CreateTaskRequest": {
        "type": "object",
        "required": [
          "title",
          "prompt",
          "schedule",
          "artist_account_id"
        ],
        "properties": {
          "title": {
            "type": "string",
            "description": "Descriptive title of the task",
            "example": "Weekly Genre Pulse Check"
          },
          "prompt": {
            "type": "string",
            "description": "Instruction/prompt executed by the task",
            "example": "Execute this weekly genre analysis workflow and email a summary to the team."
          },
          "schedule": {
            "type": "string",
            "description": "Cron expression defining when the task runs (e.g., '0 9 * * 4' for Thursdays at 9 AM)",
            "example": "0 9 * * 4"
          },
          "timezone": {
            "type": "string",
            "default": "UTC",
            "description": "Optional IANA time zone (e.g. America/New_York) the cron schedule is interpreted in, DST-aware. Defaults to UTC. Applied to the Trigger.dev schedule (the source of truth); not stored separately.",
            "example": "America/New_York"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to create the task for. Only applicable when the authenticated account has access to multiple accounts via organization membership. If not provided, the task is created for the API key's own account.",
            "example": "848cd58d-700f-4b38-ab4c-d9f52a1b2c3d"
          },
          "artist_account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the associated artist account",
            "example": "1873859c-dd37-4e9a-9bac-80d35a1b2c3d"
          }
        }
      },
      "CreatedArtist": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the created artist account"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account (same as id)"
          },
          "name": {
            "type": "string",
            "description": "Name of the artist"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO timestamp of when the artist was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO timestamp of when the artist was last updated"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Artist profile image URL"
          },
          "instruction": {
            "type": "string",
            "nullable": true,
            "description": "Custom AI instruction for this artist"
          },
          "knowledges": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Knowledge base references for this artist"
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Record label name"
          },
          "organization": {
            "type": "string",
            "nullable": true,
            "description": "Organization name"
          },
          "company_name": {
            "type": "string",
            "nullable": true,
            "description": "Company name"
          },
          "job_title": {
            "type": "string",
            "nullable": true,
            "description": "Job title"
          },
          "role_type": {
            "type": "string",
            "nullable": true,
            "description": "Role type"
          },
          "onboarding_status": {
            "type": "string",
            "nullable": true,
            "description": "Onboarding status"
          },
          "onboarding_data": {
            "nullable": true,
            "description": "Onboarding data"
          },
          "account_info": {
            "type": "array",
            "description": "Account info records"
          },
          "account_socials": {
            "type": "array",
            "description": "Linked social media accounts"
          }
        }
      },
      "DeleteCatalogInput": {
        "type": "object",
        "required": [
          "catalog_id",
          "account_id"
        ],
        "properties": {
          "catalog_id": {
            "type": "string",
            "format": "uuid",
            "description": "Catalog ID to remove"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "Account ID whose relationship will be removed"
          }
        }
      },
      "DeleteCatalogSongInput": {
        "type": "object",
        "required": [
          "catalog_id",
          "isrc"
        ],
        "properties": {
          "catalog_id": {
            "type": "string",
            "format": "uuid",
            "description": "Catalog ID from which the song will be removed"
          },
          "isrc": {
            "type": "string",
            "description": "Song ISRC to remove from the catalog"
          }
        }
      },
      "DeleteCatalogSongsRequest": {
        "type": "object",
        "required": [
          "songs"
        ],
        "properties": {
          "songs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeleteCatalogSongInput"
            },
            "description": "Array of songs for batch deletes"
          }
        }
      },
      "DeleteCatalogsRequest": {
        "type": "object",
        "required": [
          "catalogs"
        ],
        "properties": {
          "catalogs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeleteCatalogInput"
            },
            "description": "Array of catalog-account pairs to remove"
          }
        }
      },
      "DeleteTaskRequest": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the task to delete",
            "example": "aade2bce-55c7-468e-a606-c4e76fb2ea2a"
          }
        }
      },
      "DeleteTaskResponse": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the delete operation"
          }
        }
      },
      "DeleteTaskFailureResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error` for this response shape"
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message (for example `Task not found` on 404)"
          }
        }
      },
      "DeleteTaskValidationErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "missing_fields",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error` when validation fails"
          },
          "missing_fields": {
            "type": "array",
            "description": "JSON path segments to the first field that failed validation (from Zod), e.g. `[\"id\"]`",
            "items": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            }
          },
          "error": {
            "type": "string",
            "description": "Validation message for the first failing field"
          }
        }
      },
      "Error": {
        "required": [
          "error",
          "message"
        ],
        "type": "object",
        "properties": {
          "error": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "SocialProfile": {
        "type": "object",
        "properties": {
          "social_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist's socials account"
          },
          "username": {
            "type": "string",
            "description": "Username on the platform"
          },
          "profile_url": {
            "type": "string",
            "description": "Direct URL to the profile"
          },
          "avatar": {
            "type": "string",
            "nullable": true,
            "description": "URL to the profile avatar image"
          },
          "bio": {
            "type": "string",
            "nullable": true,
            "description": "Profile biography or description"
          },
          "follower_count": {
            "type": "integer",
            "nullable": true,
            "description": "Number of followers on this platform"
          },
          "following_count": {
            "type": "integer",
            "nullable": true,
            "description": "Number of accounts followed on this platform"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "description": "Geographic region of the profile"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the profile was last updated"
          },
          "history": {
            "type": "array",
            "description": "Follower snapshots for the last `history` days, newest first. Present only when the `history` query parameter is passed.",
            "items": {
              "$ref": "#/components/schemas/SocialSnapshot"
            }
          }
        }
      },
      "SocialSnapshot": {
        "type": "object",
        "required": [
          "captured_at",
          "follower_count"
        ],
        "properties": {
          "captured_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the scrape that produced this point completed"
          },
          "follower_count": {
            "type": "integer",
            "description": "Followers (subscribers on YouTube) at capture time"
          },
          "following_count": {
            "type": "integer",
            "nullable": true,
            "description": "Accounts followed at capture time; null where the platform does not report it"
          },
          "post_count": {
            "type": "integer",
            "nullable": true,
            "description": "Lifetime post count at capture time (Instagram, TikTok, YouTube, X); null where the platform does not report it"
          }
        }
      },
      "Song": {
        "type": "object",
        "description": "A song with its metadata and associated artists",
        "properties": {
          "isrc": {
            "type": "string",
            "description": "International Standard Recording Code (primary key)"
          },
          "name": {
            "type": "string",
            "description": "Name of the song"
          },
          "album": {
            "type": "string",
            "description": "Name of the album the song belongs to"
          },
          "notes": {
            "type": "string",
            "description": "Notes for the song"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of when the song data was last updated"
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SongArtist"
            },
            "description": "Array of artist objects associated with this song"
          }
        }
      },
      "SongAnalyzeAudioUrlErrorResponse": {
        "type": "object",
        "description": "Returned (422) when `audio_url` could not be verified. The model is not called and nothing is charged.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "error": {
            "type": "string",
            "enum": [
              "audio_url_unreachable",
              "audio_url_not_audio"
            ],
            "description": "`audio_url_unreachable`: the URL did not answer 2xx within 10 seconds. `audio_url_not_audio`: it answered, but with a content type that is not `audio/*` or `application/octet-stream`.",
            "example": "audio_url_not_audio"
          },
          "message": {
            "type": "string",
            "description": "What the check observed, for the caller's logs.",
            "example": "audio_url answered 200 with content type text/html"
          }
        },
        "required": [
          "status",
          "error",
          "message"
        ]
      },
      "SongAnalyzeErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Error status"
          },
          "missing_fields": {
            "type": "array",
            "description": "Path to the first invalid or missing field when validation fails",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "SongAnalyzeInsufficientCreditsResponse": {
        "type": "object",
        "description": "Returned (402) when the account lacks credits for the analysis. Nothing is charged and no Stripe object is created. Credits must be bought explicitly via [`POST /api/credits/sessions`](/api-reference/credits/sessions-create).",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "insufficient_credits"
            ]
          },
          "remaining_credits": {
            "type": "integer",
            "description": "The account's balance, in credits (micro-dollars: 1,000,000 = `$1.00`).",
            "example": 0
          },
          "required_credits": {
            "type": "integer",
            "description": "The base price of this request, in credits: 50000 (`$0.05`) for a single preset or custom prompt, 650000 (`$0.65`) for `full_report`. The per-second inference charge is added after the model returns.",
            "example": 50000
          },
          "billingUrl": {
            "type": "string",
            "description": "Static link to the Recoup app, where a human can save a card and buy credits. It is a constant, not a freshly minted Stripe Checkout Session, so a credit-gated endpoint that keeps returning 402 creates nothing. To buy credits programmatically, call [`POST /api/credits/sessions`](/api-reference/credits/sessions-create).",
            "example": "https://app.recoupable.dev"
          }
        },
        "required": [
          "error",
          "remaining_credits",
          "required_credits",
          "billingUrl"
        ]
      },
      "SongAnalyzeRequest": {
        "type": "object",
        "description": "Provide exactly one of `preset` or `prompt`, plus `audio_url`. Use `preset` for structured analysis workflows, or `prompt` for free-form questions about the audio.",
        "required": [
          "audio_url"
        ],
        "properties": {
          "preset": {
            "type": "string",
            "enum": [
              "catalog_metadata",
              "mood_tags",
              "lyric_transcription",
              "mix_feedback",
              "song_description",
              "music_theory",
              "similar_artists",
              "sample_detection",
              "sync_brief_match",
              "audience_profile",
              "content_advisory",
              "playlist_pitch",
              "artist_development_notes",
              "full_report"
            ],
            "description": "Name of a curated analysis preset. Use instead of prompt for structured, optimized output. The 'full_report' preset runs all 13 presets in parallel and returns a comprehensive report. See [List Analyze Presets](/api-reference/songs/analyze-presets) for the full list of available presets.",
            "example": "catalog_metadata"
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 24000,
            "description": "Text prompt or question about the music",
            "example": "Describe the genre, tempo, and mood of this track."
          },
          "audio_url": {
            "type": "string",
            "format": "uri",
            "description": "Public URL to an audio file (MP3, WAV, or FLAC, up to 20 minutes). Required in every mode, including a custom `prompt`. Verified before the model is called: the URL must answer a `HEAD` request (or a ranged `GET`) with 2xx within 10 seconds and an `audio/*` or `application/octet-stream` content type, else the request returns 422 and nothing is charged.",
            "example": "https://example.com/song.mp3"
          },
          "max_new_tokens": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2048,
            "default": 512,
            "description": "Maximum number of tokens to generate",
            "example": 512
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 1,
            "description": "Controls output creativity — higher values produce more varied responses",
            "example": 0.7
          },
          "top_p": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 1,
            "description": "Nucleus sampling probability cutoff",
            "example": 0.9
          },
          "do_sample": {
            "type": "boolean",
            "default": false,
            "description": "Enable sampling (set true when using temperature or top_p)",
            "example": false
          }
        }
      },
      "SongAnalyzeResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Request status"
          },
          "preset": {
            "type": "string",
            "description": "Preset used for analysis, when applicable",
            "example": "catalog_metadata"
          },
          "response": {
            "description": "Model output for single-preset or custom-prompt analysis. May be plain text or structured JSON depending on the preset."
          },
          "report": {
            "type": "object",
            "description": "Full report payload returned only when using the `full_report` preset"
          },
          "elapsed_seconds": {
            "type": "number",
            "format": "float",
            "description": "Inference time in seconds"
          }
        }
      },
      "SongArtist": {
        "type": "object",
        "description": "Artist associated with a song",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the artist account"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the artist (can be null)"
          },
          "timestamp": {
            "type": "integer",
            "nullable": true,
            "description": "Timestamp associated with the artist account (can be null)"
          }
        }
      },
      "SongsErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "SongsResponse": {
        "type": "object",
        "description": "Response containing songs data",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "songs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Song"
            },
            "description": "Array of song objects with artist information"
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the task"
          },
          "title": {
            "type": "string",
            "description": "Descriptive title or name of the task"
          },
          "prompt": {
            "type": "string",
            "description": "Detailed instruction or prompt for task execution"
          },
          "schedule": {
            "type": "string",
            "description": "Cron expression defining when the task should execute (e.g., '0 10 * * *')"
          },
          "timezone": {
            "type": "string",
            "nullable": true,
            "description": "IANA time zone the task's cron is interpreted in, read from the Trigger.dev schedule (the source of truth). Null when the task has no schedule or the timezone can't be read.",
            "example": "America/New_York"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the associated account"
          },
          "artist_account_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the associated artist account"
          },
          "enabled": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the task is enabled. Defaults to true."
          },
          "trigger_schedule_id": {
            "type": "string",
            "nullable": true,
            "description": "Identifier for the trigger schedule associated with this task"
          },
          "recent_runs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskRunResponse"
            },
            "description": "Last 5 runs for this task, sourced from the Trigger.dev API."
          },
          "upcoming": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Next scheduled run times."
          },
          "owner_email": {
            "type": "string",
            "nullable": true,
            "description": "Primary email address for the task owner account when one exists."
          },
          "artist_name": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the artist account the task runs for (`artist_account_id`), so a task list can label rows without a roster lookup. Null when the artist account no longer exists.",
            "example": "Braden Bales"
          },
          "model": {
            "type": "string",
            "nullable": true,
            "minLength": 1,
            "description": "AI model identifier used when the task runs. When present as a string, must be non-empty; otherwise `null` if unset."
          }
        }
      },
      "TaskRunListResponse": {
        "type": "object",
        "required": [
          "status",
          "runs"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Indicates the request was successful"
          },
          "runs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskRunResponse"
            },
            "description": "List of recent task runs for the authenticated account. Each item is a raw Trigger.dev SDK run object (same shape as TaskRunResponse, but without `output` and `error` fields)."
          }
        }
      },
      "CreateContentRunOutput": {
        "type": "object",
        "description": "Output payload for a `create-content` task run. Returned in `output` when `status` is `COMPLETED`. Agents should poll `/api/tasks/runs?runId=…` and read these fields once the run is done.",
        "required": [
          "videoSourceUrl",
          "imageUrl",
          "captionText",
          "template"
        ],
        "properties": {
          "videoSourceUrl": {
            "type": "string",
            "format": "uri",
            "description": "URL to the rendered final 9:16 video (image + motion + audio + caption already composed)."
          },
          "imageUrl": {
            "type": "string",
            "format": "uri",
            "description": "URL to the base image used for video generation."
          },
          "captionText": {
            "type": "string",
            "description": "The caption text burned into the video."
          },
          "template": {
            "type": "string",
            "description": "Template id used for this run (e.g. `album-record-store`, `artist-caption-bedroom`).",
            "example": "artist-caption-bedroom"
          },
          "lipsync": {
            "type": "boolean",
            "description": "Whether lipsync was applied (audio-driven mouth animation)."
          },
          "audio": {
            "type": "object",
            "description": "Metadata about the song clip used in the final composition.",
            "properties": {
              "songTitle": {
                "type": "string"
              },
              "songFilename": {
                "type": "string"
              },
              "startSeconds": {
                "type": "number",
                "description": "Offset into the source song where the clip starts."
              },
              "durationSeconds": {
                "type": "number",
                "description": "Length of the audio clip in seconds."
              },
              "clipLyrics": {
                "type": "string",
                "nullable": true,
                "description": "Lyrics for the chosen clip, when available."
              },
              "clipMood": {
                "type": "string",
                "nullable": true,
                "description": "Inferred mood for the chosen clip, when available."
              }
            }
          }
        }
      },
      "TaskRunsErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error` for this response shape"
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message"
          }
        }
      },
      "TaskRunResponse": {
        "type": "object",
        "description": "Raw Trigger.dev SDK run object. The API passes through the SDK response without field mapping. See https://trigger.dev/docs/management/runs/retrieve for the full reference. When listing runs, `output`, `error`, `payload`, and `attempts` are not included.",
        "required": [
          "id",
          "status",
          "taskIdentifier",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique run identifier, prefixed with `run_`"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING_VERSION",
              "DELAYED",
              "QUEUED",
              "EXECUTING",
              "REATTEMPTING",
              "FROZEN",
              "COMPLETED",
              "CANCELED",
              "FAILED",
              "CRASHED",
              "INTERRUPTED",
              "SYSTEM_FAILURE"
            ],
            "description": "Current run status"
          },
          "taskIdentifier": {
            "type": "string",
            "description": "The task type identifier (e.g. 'setup-sandbox', 'run-sandbox-command')"
          },
          "idempotencyKey": {
            "type": "string",
            "nullable": true,
            "description": "Idempotency key used to deduplicate trigger requests"
          },
          "version": {
            "type": "string",
            "description": "The worker version that executed the run"
          },
          "isTest": {
            "type": "boolean",
            "description": "Whether this is a test run"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the run was created (ISO 8601)"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the run was last updated (ISO 8601)"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When execution started (null if not yet started)"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the run finished (null if still running)"
          },
          "delayedUntil": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "If delayed, when the run becomes eligible to execute"
          },
          "ttl": {
            "description": "Time-to-live. If the run is not started within this duration, it expires.",
            "nullable": true
          },
          "expiredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the run expired (null if not expired)"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags associated with this run (max 10)"
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "JSON metadata attached to the run"
          },
          "costInCents": {
            "type": "number",
            "description": "Compute cost of the run in cents"
          },
          "baseCostInCents": {
            "type": "number",
            "description": "Base invocation cost in cents"
          },
          "durationMs": {
            "type": "number",
            "description": "Compute duration in milliseconds"
          },
          "env": {
            "type": "object",
            "description": "Environment the run executed in",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "user": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "depth": {
            "type": "integer",
            "description": "Nesting depth for child runs"
          },
          "batchId": {
            "type": "string",
            "nullable": true,
            "description": "Batch ID if triggered as part of a batch"
          },
          "triggerFunction": {
            "type": "string",
            "enum": [
              "trigger",
              "triggerAndWait",
              "batchTrigger",
              "batchTriggerAndWait"
            ],
            "description": "The function used to trigger this run"
          },
          "payload": {
            "description": "Input payload for the task. Only present when retrieving by runId.",
            "nullable": true
          },
          "output": {
            "description": "Task output data. Only present when retrieving by runId, and only populated when `status` is `COMPLETED`. Shape varies by `taskIdentifier`. The `create-content` task returns the schema below; other tasks return their own.",
            "nullable": true,
            "oneOf": [
              {
                "$ref": "#/components/schemas/CreateContentRunOutput"
              },
              {
                "type": "object",
                "description": "Generic task output (for tasks not yet schema'd)"
              }
            ]
          },
          "error": {
            "type": "object",
            "nullable": true,
            "description": "Error details if the run failed. Only present when retrieving by runId.",
            "properties": {
              "message": {
                "type": "string",
                "description": "Human-readable error message"
              },
              "name": {
                "type": "string",
                "description": "Error name or type"
              },
              "stackTrace": {
                "type": "string",
                "description": "Stack trace"
              }
            }
          },
          "attempts": {
            "type": "array",
            "description": "Attempt history. Only present when retrieving by runId.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Attempt ID, prefixed with `attempt_`"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "PENDING",
                    "EXECUTING",
                    "PAUSED",
                    "COMPLETED",
                    "FAILED",
                    "CANCELED"
                  ]
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "updatedAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "startedAt": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "completedAt": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "error": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "stackTrace": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "schedule": {
            "type": "object",
            "nullable": true,
            "description": "Schedule information if triggered by a schedule. Only present when retrieving by runId."
          },
          "relatedRuns": {
            "type": "object",
            "nullable": true,
            "description": "Related run references (root, parent, children). Only present when retrieving by runId."
          }
        }
      },
      "TasksResponse": {
        "type": "object",
        "required": [
          "status",
          "tasks"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            },
            "description": "Array of task objects"
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is error)"
          }
        }
      },
      "PlanLimitError": {
        "type": "object",
        "required": [
          "status",
          "error",
          "limit",
          "message",
          "plan",
          "task_limit",
          "min_cadence_minutes",
          "current_task_count",
          "billingUrl"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error` for this response shape"
          },
          "error": {
            "type": "string",
            "enum": [
              "plan_limit"
            ],
            "description": "Machine-readable code. Literally `plan_limit`."
          },
          "limit": {
            "type": "string",
            "enum": [
              "task_count",
              "min_cadence"
            ],
            "description": "Which entitlement blocked the request: `task_count` (the plan's enabled-task cap is reached) or `min_cadence` (the cron fires more often than the plan allows)."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation naming the plan and the limit, suitable to show to a user.",
            "example": "Free includes 1 task. Starter includes 3, Pro is unlimited."
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "pro"
            ],
            "description": "The account's current plan, resolved from its subscription."
          },
          "task_limit": {
            "type": "integer",
            "nullable": true,
            "minimum": 1,
            "description": "Maximum enabled tasks on the current plan. Null means uncapped.",
            "example": 1
          },
          "min_cadence_minutes": {
            "type": "integer",
            "minimum": 1,
            "description": "Shortest allowed gap between two consecutive runs of a task on the current plan, in minutes (Free 10080, Starter 1440, Pro 60).",
            "example": 10080
          },
          "current_task_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Enabled tasks the account has right now, excluding the one being created or updated.",
            "example": 1
          },
          "billingUrl": {
            "type": "string",
            "format": "uri",
            "description": "Static link to `/plan` in the Recoup app, where a human can upgrade. Constant; retrying creates nothing.",
            "example": "https://app.recoupable.dev/plan"
          }
        }
      },
      "UpdateTaskFailureResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error` for this response shape"
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message (for example `Task not found` on 404)"
          }
        }
      },
      "UpdateTaskValidationErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "missing_fields",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error` when validation fails"
          },
          "missing_fields": {
            "type": "array",
            "description": "JSON path segments to the first field that failed validation (from Zod), e.g. `[\"id\"]` or `[\"schedule\"]`",
            "items": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            }
          },
          "error": {
            "type": "string",
            "description": "Validation message for the first failing field"
          }
        }
      },
      "UpdateTaskRequest": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the task to update",
            "example": "aade2bce-55c7-468e-a606-c4e76fb2ea2a"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "description": "New descriptive title. If sent, must be a non-empty string.",
            "example": "Weekly Genre Pulse Check (Updated)"
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "description": "New instruction/prompt executed by the task. If sent, must be a non-empty string.",
            "example": "Execute this weekly genre analysis workflow and email a summary to the team."
          },
          "schedule": {
            "type": "string",
            "minLength": 1,
            "description": "New cron expression. If sent, must be non-empty and valid for your environment.",
            "example": "0 10 * * 4"
          },
          "timezone": {
            "type": "string",
            "description": "Optional IANA time zone the cron is interpreted in (DST-aware). If sent, updates the Trigger.dev schedule; a timezone-only change re-syncs the schedule. A cron-only update preserves the existing time zone.",
            "example": "America/New_York"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to update the task for. Only applicable when the authenticated account has access to multiple accounts via organization membership. If not provided, the task is updated for the API key's own account.",
            "example": "848cd58d-700f-4b38-ab4c-d9f52a1b2c3d"
          },
          "artist_account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account to associate with the task after update",
            "example": "1873859c-dd37-4e9a-9bac-80d35a1b2c3d"
          },
          "enabled": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the task is enabled (`true` / `false`), or `null` to clear an explicit override depending on server rules.",
            "example": true
          },
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "AI model identifier to use when the task runs. If sent, must be a non-empty string.",
            "example": "claude-sonnet-4-20250514"
          }
        }
      },
      "ArtistKnowledge": {
        "type": "object",
        "required": [
          "name",
          "url",
          "type"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name for the knowledge entry",
            "example": "Artist Knowledge Base Report"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public URL where the knowledge content is stored",
            "example": "https://arweave.net/abc123"
          },
          "type": {
            "type": "string",
            "description": "MIME type of the knowledge content",
            "example": "text/plain"
          }
        }
      },
      "UpdateArtistRequest": {
        "type": "object",
        "description": "All fields are optional. The request must include at least one field.",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "description": "Artist display name",
            "example": "The Weeknd"
          },
          "image": {
            "type": "string",
            "description": "Artist profile image URL. Pass an empty string to clear the existing image; otherwise must be a valid URL.",
            "example": "https://i.scdn.co/image/abc123"
          },
          "instruction": {
            "type": "string",
            "description": "Custom AI instruction shown to assistants when this artist is the active context",
            "example": "Always reference the artist's R&B catalog when discussing collaborations."
          },
          "label": {
            "type": "string",
            "description": "Record label name",
            "example": "XO / Republic Records"
          },
          "knowledges": {
            "type": "array",
            "description": "Knowledge base entries for this artist. Replaces the existing array when provided.",
            "items": {
              "$ref": "#/components/schemas/ArtistKnowledge"
            }
          },
          "profileUrls": {
            "type": "object",
            "description": "Map of uppercase platform identifier to social profile URL. Each entry replaces the existing social for that platform; platforms not included are preserved. Recognized keys: SPOTIFY, INSTAGRAM, TIKTOK, TWITTER, YOUTUBE, APPLE, FACEBOOK, THREADS, BANDSINTOWN. Keys are matched case-sensitively, so lowercase keys will create duplicate socials instead of replacing the existing entry.",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "SPOTIFY": "https://open.spotify.com/artist/1Xyo4u8uXC1ZmMpatF05PJ",
              "INSTAGRAM": "https://instagram.com/theweeknd",
              "TIKTOK": "https://tiktok.com/@theweeknd",
              "TWITTER": "https://x.com/theweeknd",
              "YOUTUBE": "https://youtube.com/@theweeknd",
              "APPLE": "https://music.apple.com/us/artist/the-weeknd/479756766",
              "FACEBOOK": "https://facebook.com/theweeknd",
              "THREADS": "https://threads.net/@theweeknd",
              "BANDSINTOWN": "https://www.bandsintown.com/a/1371750-the-weeknd"
            }
          },
          "pinned": {
            "type": "boolean",
            "description": "Pin or unpin the artist for the authenticated account. Affects only the caller's pin state, not other accounts."
          }
        }
      },
      "UpdateArtistResponse": {
        "type": "object",
        "required": [
          "artist"
        ],
        "properties": {
          "artist": {
            "$ref": "#/components/schemas/UpdatedArtist"
          }
        }
      },
      "UpdateArtistErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Path to the field that failed validation, when applicable"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "UpdatedArtist": {
        "type": "object",
        "required": [
          "account_id",
          "name",
          "account_socials",
          "pinned"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Artist display name"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Artist profile image URL"
          },
          "instruction": {
            "type": "string",
            "nullable": true,
            "description": "Custom AI instruction for this artist"
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Record label name"
          },
          "knowledges": {
            "type": "array",
            "nullable": true,
            "description": "Knowledge base entries for this artist",
            "items": {
              "$ref": "#/components/schemas/ArtistKnowledge"
            }
          },
          "account_socials": {
            "type": "array",
            "description": "Social profiles linked to the artist after the update",
            "items": {
              "$ref": "#/components/schemas/UpdatedArtistSocial"
            }
          },
          "pinned": {
            "type": "boolean",
            "description": "Whether the authenticated account has pinned this artist"
          }
        }
      },
      "UpdatedArtistSocial": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the underlying social record"
          },
          "username": {
            "type": "string",
            "nullable": true,
            "description": "Username on the platform"
          },
          "profile_url": {
            "type": "string",
            "nullable": true,
            "description": "Full profile URL on the platform"
          },
          "link": {
            "type": "string",
            "description": "Profile URL (mirror of profile_url for legacy clients)"
          },
          "type": {
            "type": "string",
            "description": "Uppercase platform identifier inferred from the profile URL: SPOTIFY, INSTAGRAM, TIKTOK, TWITTER, YOUTUBE, APPLE, FACEBOOK, THREADS, or NONE if the URL did not match a known platform."
          }
        }
      }
    }
  }
}