{
  "openapi": "3.1.0",
  "info": {
    "title": "Recoup API - Research",
    "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/chats": {
      "get": {
        "description": "Returns chats for the authenticated account. Personal Bearer tokens see only the caller's chats; org keys see chats across the org's member accounts; admin keys see all chats. Each row carries the chat id and the session id needed to build the chat URL.",
        "parameters": [
          {
            "name": "artist_account_id",
            "in": "query",
            "description": "Optional. Filter chats to only include those whose owning session's `artistId` matches the supplied artist account id. Combine with `account_id` to scope by both owner and artist.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "description": "Filter to a specific account. Only applicable when the authenticated account has access to multiple accounts via organization membership.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chats retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid query parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is not a member of the organization or account tried to filter by an account_id they don't have access to",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Create a new chat room. Optionally associate it with an artist and/or provide a client-generated chat ID. Pass accountId to create a chat on behalf of a specific account the API key has access to.",
        "requestBody": {
          "description": "Chat creation parameters",
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chat created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateChatResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - failed to create chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateChatErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - API key does not have access to the specified accountId",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateChatErrorResponse"
                }
              }
            }
          }
        }
      },
      "patch": {
        "description": "Update a chat room's topic (display name). The chatId is required; the topic field will be updated. Topic must be between 3 and 50 characters.",
        "requestBody": {
          "description": "Chat fields to update",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chat updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateChatResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters or validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateChatErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - API key does not have access to this chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found - chat room does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateChatErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Delete a chat room by ID. This operation also removes related room records (memory emails, memories) before deleting the room itself.",
        "requestBody": {
          "description": "Chat deletion parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chat deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteChatResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters or validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteChatErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - API key does not have access to this chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found - chat room does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteChatErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error - failed to delete chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteChatErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chats/{id}/artist": {
      "get": {
        "description": "Retrieve the artist associated with a specific chat room. Returns 404 if the chat does not exist or is not accessible by the authenticated caller.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the chat room.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat artist resolved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatArtistResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid chat id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatArtistErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found - chat does not exist or is not accessible",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatArtistErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chats/{id}/messages": {
      "get": {
        "description": "Retrieve all messages (memories) for a specific chat room in chronological order.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the chat room.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatMessagesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid chat id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatMessagesErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller does not have access to this chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found - chat does not exist or is not accessible",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatMessagesErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error - failed to retrieve messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetChatMessagesErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chats/{id}/messages/copy": {
      "post": {
        "description": "Copy all messages from the source chat (`id` path param) to a target chat (`targetChatId` in request body). By default, existing target messages are cleared before copy.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The source chat room UUID.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "Copy target options",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CopyChatMessagesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Messages copied successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CopyChatMessagesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid payload or chat identifiers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CopyChatMessagesErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller lacks access to source or target chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CopyChatMessagesErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found - source or target chat does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CopyChatMessagesErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error - failed to copy messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CopyChatMessagesErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chats/{id}/messages/trailing": {
      "delete": {
        "description": "Delete all messages in a chat from a given message onward (inclusive), identified by `from_message_id`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the chat room.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from_message_id",
            "in": "query",
            "description": "The message UUID from which trailing messages should be deleted (inclusive).",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Trailing messages deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTrailingChatMessagesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid or missing query/path parameters or message does not belong to chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTrailingChatMessagesErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTrailingChatMessagesErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - access denied to this chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTrailingChatMessagesErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found - chat or message does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTrailingChatMessagesErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error - failed to delete trailing messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteTrailingChatMessagesErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chats/compact": {
      "post": {
        "description": "Compact one or more chat conversations into summarized versions. This reduces the size of chat history while preserving key information. Optionally provide a prompt to control what information gets preserved in the compacted summary.",
        "requestBody": {
          "description": "Chat compaction parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompactChatsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chats compacted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompactChatsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found - one or more chat IDs do not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/runs": {
      "post": {
        "description": "Start an asynchronous, headless chat-generation run on the durable agent workflow — the same engine as interactive [`POST /api/chat`](/api-reference/chat/workflow).\n\n**Related endpoints**\n- [`POST /api/chat`](/api-reference/chat/workflow) — use that for **interactive, streaming** turns on an existing chat; use this for **headless/programmatic** runs (no browser or pre-provisioned session needed).\n- [`GET /api/chat/runs/{runId}`](/api-reference/chat/runs-status) — poll to learn **whether** the run finished (and if it succeeded).\n- [`GET /api/chat/{chatId}/stream`](/api-reference/chat/workflow-stream) — **watch the output live** by passing the returned `chatId`.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "description": "Chat generation request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatGenerateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Run accepted. A durable workflow run was started; `runId` identifies it. `chatId` / `sessionId` identify the persisted output — read the result via [`GET /api/chat/{chatId}/stream`](/api-reference/chat/workflow-stream) (resume the stream) or the chat's persisted messages. Poll [`GET /api/chat/runs/{runId}`](/api-reference/chat/runs-status) for status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatGenerateAcceptedResponse"
                }
              }
            },
            "headers": {
              "Location": {
                "description": "Relative URL of the run-status resource for the started run.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                },
                "example": "/api/chat/runs/wrun_01KVWZNM82NA7XKNEWWHG8VPHJ"
              }
            }
          },
          "400": {
            "description": "Bad request - missing required parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatGenerateErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/{chatId}/stop": {
      "post": {
        "description": "Stop the response currently being generated for a chat, freeing it to accept a new message. Idempotent — returns `stopped: false` when nothing is in progress. The chat must belong to the authenticated account.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "description": "UUID of the chat to stop.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stop request processed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopChatResponse"
                }
              }
            }
          },
          "400": {
            "description": "chatId is not a valid UUID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopChatErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The chat belongs to a different account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopChatErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Chat not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopChatErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/{chatId}/stream": {
      "get": {
        "summary": "Resume a chat response stream",
        "description": "Reconnects to an in-progress chat response — the resume counterpart to [`POST /api/chat`](/api-reference/chat/workflow), which never resumes. Returns the live Server-Sent Events stream when a response is still being generated, or `204 No Content` when there is nothing to resume. The chat must belong to the authenticated account.\n\n**Related endpoints**\n- [`POST /api/chat`](/api-reference/chat/workflow) — the interactive turn whose stream this resumes.\n- [`POST /api/chat/runs`](/api-reference/chat/runs) — start a **headless** run, then pass the returned `chatId` here to **watch its output live**.\n- [`GET /api/chat/runs/{runId}`](/api-reference/chat/runs-status) — check **whether** that run finished (status, not content).",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "description": "UUID of the chat to resume.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream of UI message parts for the in-progress response, compatible with the Vercel AI SDK. The `x-workflow-run-id` response header carries the run ID for this response.",
            "headers": {
              "x-workflow-run-id": {
                "description": "Durable workflow run ID of the resumed run.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events stream containing UI message parts"
                }
              }
            }
          },
          "204": {
            "description": "No active stream to resume for this chat."
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The authenticated account does not own the referenced chat.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Chat not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat": {
      "post": {
        "summary": "Stream sandbox-driven chat (Vercel Workflow)",
        "description": "Streams an agent loop running as a durable [Vercel Workflow](https://vercel.com/docs/workflow) against the session's sandbox. The agent uses sandbox-only tools (`bash`, `read`, `write`, `grep`, `glob`, `todo`, `task`, `ask_user_question`, `skill`, `fetch`) — no MCP or Composio. Requires a sandbox provisioned via [`POST /api/sandbox`](/api-reference/sandbox/create).\n\n**Related endpoints**\n- [`POST /api/chat/runs`](/api-reference/chat/runs) — the **headless** counterpart: it provisions its own session + sandbox and runs without a streaming client. Use that for tasks/cron/programmatic runs; use this for **interactive, streaming** turns on an existing chat.\n- [`GET /api/chat/{chatId}/stream`](/api-reference/chat/workflow-stream) — reconnect to an in-progress response.\n- [`GET /api/chat/runs/{runId}`](/api-reference/chat/runs-status) — check **whether** a headless run finished (status, not content).",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "description": "Chat workflow request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatWorkflowRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Server-Sent Events stream of UI message parts compatible with the Vercel AI SDK `createUIMessageStreamResponse`. The `x-workflow-run-id` response header carries the durable workflow run ID for resume/stop operations.",
            "headers": {
              "x-workflow-run-id": {
                "description": "Durable workflow run ID. Persist alongside the chat so a later `GET /api/chat/{chatId}/stream` request can resume this run after a disconnect.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events stream containing UI message parts"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — missing required fields, invalid body, or sandbox not initialized for the session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key's account does not own the referenced `sessionId` or `chatId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session or chat not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "A response is already streaming for this chat. Reconnect to it via `GET /api/chat/{chatId}/stream` instead of POSTing again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatStreamErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/research": {
      "get": {
        "description": "Unified music research search. This is the discovery primitive for the detail-lookup endpoints — use `type=artists` to find an `id` for [GET /api/research/albums](/api-reference/research/albums), `type=tracks` for [GET /api/research/track](/api-reference/research/track), `type=playlists` for [GET /api/research/playlist](/api-reference/research/playlist), etc. Pick the right `id` from the returned results and pass it to the appropriate detail endpoint.\n\nSearches the configured research data source. Use the `type` and `limit` parameters to narrow results before passing a returned `id` to a detail endpoint.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query. Can be a name (e.g. `Drake`, `Flowers`) or a streaming-platform URL (e.g. `https://open.spotify.com/artist/...`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Entity type to search: artists, tracks, or labels.",
            "schema": {
              "type": "string",
              "enum": [
                "artists",
                "tracks",
                "labels"
              ],
              "default": "artists"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Pagination offset. Defaults to 0 when omitted.",
            "schema": {
              "type": "string",
              "pattern": "^(0|[1-9][0-9]*)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (e.g., missing required query parameter `q`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/albums": {
      "get": {
        "description": "Get the album discography — albums, EPs, and singles with release dates — for a provider `artist_id`.\n\nFetches provider data for `/artist/:id/albums`. By default `is_primary=true`, so only albums where the artist is a main artist are returned — DJ compilations, soundtracks, and pure feature appearances are excluded. Pass `is_primary=false` to include them. Discover a provider `artist_id` via [GET /api/research](/api-reference/research/search) with `type=artists`.",
        "parameters": [
          {
            "name": "artist_id",
            "in": "query",
            "required": true,
            "description": "Provider artist ID. Obtain it from search or lookup results.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          },
          {
            "name": "is_primary",
            "in": "query",
            "required": false,
            "description": "When `true` (default), returns only albums where the artist is a main artist. Set to `false` to include feature appearances, DJ compilations, and soundtracks.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "true"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Number of albums to return. Defaults to the provider's default when omitted.",
            "schema": {
              "type": "string",
              "pattern": "^[1-9][0-9]*$"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Pagination offset. Defaults to 0 when omitted.",
            "schema": {
              "type": "string",
              "pattern": "^(0|[1-9][0-9]*)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist albums",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchAlbumsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — `artist_id` missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/audience": {
      "get": {
        "description": "Get audience demographics for an artist on a specific platform — age, gender, and country breakdown.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "Platform to get demographics for. Defaults to instagram.",
            "schema": {
              "type": "string",
              "enum": [
                "instagram",
                "tiktok",
                "youtube"
              ],
              "default": "instagram"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audience demographics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchAudienceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/career": {
      "get": {
        "description": "Get an artist's career timeline — key milestones, trajectory, and career stage.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Career timeline",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchCareerResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/deep": {
      "post": {
        "description": "Perform deep, comprehensive research on a topic. Browses multiple sources extensively and returns a cited report. Use for full artist deep dives, competitive analysis, and any research requiring synthesis across many sources.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResearchDeepRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deep research report with citations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchDeepResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/enrich": {
      "post": {
        "description": "Enrich an entity with structured data from web research. Provide a description of who or what to research and a JSON schema defining the fields to extract. Returns typed data with citations. **Important:** The `schema` object must include `\"type\": \"object\"` at the top level — requests without an explicit type will be rejected.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResearchEnrichRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Structured enrichment data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchEnrichResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/extract": {
      "post": {
        "description": "Extract clean markdown content from one or more public URLs. Handles JavaScript-heavy pages and PDFs. Returns focused excerpts aligned to an objective, or full page content.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResearchExtractRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted content from URLs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchExtractResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/insights": {
      "get": {
        "description": "Get AI-generated insights about an artist — automatically surfaced trends, milestones, and observations.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "AI-generated insights",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchInsightsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/lookup": {
      "get": {
        "description": "Look up an artist by a platform URL or ID — Spotify URL, Spotify ID, Apple Music URL, etc.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": false,
            "description": "Spotify artist URL. Required unless `spotifyId` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "spotifyId",
            "in": "query",
            "required": false,
            "description": "Spotify artist ID. Required unless `url` is provided.",
            "schema": {
              "type": "string",
              "example": "3TVXtAsR1Inumwj472S9r4"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist profile matching the platform URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchLookupResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/metrics": {
      "get": {
        "description": "Get platform-specific metrics for an artist over time — followers, listeners, views, engagement, and radio airplay. Supports 16 sources.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": true,
            "description": "Platform to get metrics for.",
            "schema": {
              "type": "string",
              "enum": [
                "spotify",
                "instagram",
                "tiktok",
                "twitter",
                "facebook",
                "youtube_channel",
                "youtube_artist",
                "soundcloud",
                "deezer",
                "twitch",
                "line",
                "melon",
                "wikipedia",
                "bandsintown",
                "radio",
                "sxm"
              ]
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Platform metrics over time",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchMetricsResponse"
                }
              }
            }
          },
          "202": {
            "description": "Metrics refresh is pending. Retry this endpoint shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchRefreshPendingResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/milestones": {
      "get": {
        "description": "Get an artist's activity feed — playlist adds, chart entries, and other notable events. Each milestone includes a date, summary, platform, track name, and star rating.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Milestone list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchMilestonesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/people": {
      "post": {
        "description": "Search for people in the music industry — artists, managers, A&R reps, producers. Returns multi-source profiles including LinkedIn data.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResearchPeopleRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "People search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchPeopleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/playlists": {
      "get": {
        "description": "Get an artist's playlist placements — editorial, algorithmic, and indie playlists across platforms.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "spotify",
                "applemusic",
                "deezer",
                "amazon",
                "youtube"
              ],
              "default": "spotify"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "current",
                "past"
              ],
              "default": "current"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Playlist placements",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchPlaylistsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/profile": {
      "get": {
        "description": "Get a full artist profile — bio, genres, social URLs, label, career stage, and basic metrics.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchProfileResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/similar": {
      "get": {
        "description": "Find similar or related artists for competitive analysis and collaboration discovery. Weight parameters are applied when the configured research source supports weighted matching; otherwise the endpoint returns the closest available related-artist set.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "audience",
            "in": "query",
            "required": false,
            "description": "Audience overlap weight.",
            "schema": {
              "type": "string",
              "enum": [
                "high",
                "medium",
                "low"
              ],
              "default": "medium"
            }
          },
          {
            "name": "genre",
            "in": "query",
            "required": false,
            "description": "Genre similarity weight.",
            "schema": {
              "type": "string",
              "enum": [
                "high",
                "medium",
                "low"
              ],
              "default": "medium"
            }
          },
          {
            "name": "mood",
            "in": "query",
            "required": false,
            "description": "Mood similarity weight.",
            "schema": {
              "type": "string",
              "enum": [
                "high",
                "medium",
                "low"
              ],
              "default": "medium"
            }
          },
          {
            "name": "musicality",
            "in": "query",
            "required": false,
            "description": "Musicality similarity weight.",
            "schema": {
              "type": "string",
              "enum": [
                "high",
                "medium",
                "low"
              ],
              "default": "medium"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Similar artists with overlap scores",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchSimilarResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/track": {
      "get": {
        "description": "Get full provider track metadata by track `id` — title, artists, albums, release date, genres, popularity, and platform IDs.\n\nThis endpoint is a thin proxy over the provider `/track/:id` detail lookup. Discover a provider track ID via [GET /api/research](/api-reference/research/search) with `type=tracks` — pick a result's `id`, then call this endpoint. Keeping the two concerns separate means predictable credit charging and no silent wrong-match behavior for ambiguous queries.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Provider track ID. Obtain it from search results or a track lookup.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "track_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Track metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchTrackResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — `id` missing or not a positive integer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No provider track exists with the supplied `id`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/tracks": {
      "get": {
        "description": "Get all tracks by an artist with popularity data.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artist tracks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchTracksResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/urls": {
      "get": {
        "description": "Get all social and streaming URLs for an artist — Spotify, Instagram, TikTok, YouTube, Twitter, SoundCloud, and more.",
        "parameters": [
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Provider artist ID returned by lookup or search. Required unless `artist` is provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "artist_123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Social and streaming URLs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchUrlsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/web": {
      "post": {
        "description": "Search the web for real-time information. Returns ranked results with titles, URLs, and content snippets. Use for narrative context, press coverage, and cultural research that structured data endpoints don't cover.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResearchWebRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Web search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchWebResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/track/playlists": {
      "get": {
        "description": "Returns playlists featuring a specific track. Accepts a provider track ID (`id`) or a track name + optional artist (`q`, `artist`) for a Spotify-powered lookup. When no filter flag is passed, defaults to editorial + indie + majorCurator + popularIndie = `true`. Costs 5 credits per request.\n\nDiscover a provider track `id` via [GET /api/research](/api-reference/research/search) with `type=tracks` — then pass the resulting `id` here for the most reliable match (avoids the ambiguity of name-based lookup).",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Provider track ID. Required if `q` is not provided.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "example": "track_123"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Track name to look up. Required if `id` is not provided. Combine with `artist` to narrow the match.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "artist",
            "in": "query",
            "description": "Artist name. Required unless `id` is provided.",
            "schema": {
              "type": "string"
            },
            "required": false
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Streaming platform to return playlists for. Defaults to `spotify`.",
            "schema": {
              "type": "string",
              "enum": [
                "spotify",
                "applemusic",
                "deezer",
                "amazon"
              ],
              "default": "spotify"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Return current or past playlist placements. Defaults to `current`.",
            "schema": {
              "type": "string",
              "enum": [
                "current",
                "past"
              ],
              "default": "current"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of placements to return.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Pagination offset.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "ISO date lower bound for placements.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "until",
            "in": "query",
            "description": "ISO date upper bound for placements.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort column.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "editorial",
            "in": "query",
            "description": "Include editorial playlists. When no filter flags are set, defaults to `true` along with `indie`, `majorCurator`, `popularIndie`.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "indie",
            "in": "query",
            "description": "Include indie playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "majorCurator",
            "in": "query",
            "description": "Include major-curator playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "popularIndie",
            "in": "query",
            "description": "Include popular indie playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "personalized",
            "in": "query",
            "description": "Include personalized playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "chart",
            "in": "query",
            "description": "Include chart playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "newMusicFriday",
            "in": "query",
            "description": "Include New Music Friday playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "thisIs",
            "in": "query",
            "description": "Include \"This Is\" artist playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "radio",
            "in": "query",
            "description": "Include algorithmic radio playlists.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "brand",
            "in": "query",
            "description": "Include brand playlists.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Playlist placements for the track.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "placements"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "example": "success"
                    },
                    "placements": {
                      "type": "array",
                      "description": "Playlist placement objects returned by the configured provider (shape varies per platform).",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — missing `id`/`q`, invalid `platform`, or invalid `status`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Track not found when resolving via `q` + `artist`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "501": {
            "$ref": "#/components/responses/ResearchDataSourceUnsupported"
          }
        }
      }
    },
    "/api/research/track/stats": {
      "get": {
        "description": "Get per-track, per-source current stats by ISRC or track id — absolute `streams_total`, playlist reach, and chart counts.",
        "parameters": [
          {
            "name": "isrc",
            "in": "query",
            "required": false,
            "description": "Track ISRC. Provide exactly one track identifier.",
            "schema": {
              "type": "string",
              "example": "USQY51771120"
            }
          },
          {
            "name": "songstats_track_id",
            "in": "query",
            "required": false,
            "description": "Songstats track id (from `GET /api/research/tracks`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "spotify_track_id",
            "in": "query",
            "required": false,
            "description": "Spotify track id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "apple_music_track_id",
            "in": "query",
            "required": false,
            "description": "Apple Music track id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": true,
            "description": "Comma-separated list of sources, or `all`. e.g. `spotify`, `apple_music`, `deezer`, `youtube`.",
            "schema": {
              "type": "string",
              "example": "spotify"
            }
          },
          {
            "name": "with_playlists",
            "in": "query",
            "required": false,
            "description": "Include the per-source `playlists[]` array in `data`.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "with_charts",
            "in": "query",
            "required": false,
            "description": "Include the per-source `charts[]` array in `data`.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "with_stations",
            "in": "query",
            "required": false,
            "description": "Include radio `stations[]` data where available.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "with_videos",
            "in": "query",
            "required": false,
            "description": "Include video data where available.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "with_links",
            "in": "query",
            "required": false,
            "description": "Include external links where available.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "only_current",
            "in": "query",
            "required": false,
            "description": "Return only current counters, omitting historical/total breakdowns where applicable.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max items in toggled arrays (e.g. `playlists[]`).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Pagination offset for toggled arrays.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-track current stats, one entry per requested source.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchTrackStatsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — missing identifier or `source`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient research credits — the body includes a `checkoutUrl` to top up.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchInsufficientCreditsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No track matched the supplied identifier.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Upstream provider error passthrough — e.g. 429 when the Songstats quota is exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/research/tracks/{id}/measurements": {
      "get": {
        "summary": "Track measurements",
        "description": "Time-series of a track's measured counts.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider-neutral track id — ISRC or Spotify track id."
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "spotify"
            },
            "description": "Platform to read. Currently `spotify`."
          },
          {
            "name": "metric",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "platform_displayed_play_count"
            },
            "description": "Metric to read."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Inclusive start date (ISO) for the series."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Inclusive end date (ISO) for the series."
          },
          {
            "name": "granularity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "daily"
              ],
              "default": "daily"
            },
            "description": "Series granularity. `daily` returns the per-day cumulative series."
          },
          {
            "name": "aggregate",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "run_rate"
              ]
            },
            "description": "Return a derived aggregate instead of the raw series. `run_rate` returns the trailing-window annualized run-rate (a projection of the series)."
          },
          {
            "name": "window",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "365d"
            },
            "description": "Trailing window for `aggregate=run_rate` (e.g. `365d`)."
          }
        ],
        "responses": {
          "200": {
            "description": "The measurement series (or the requested aggregate).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchMeasurementsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — bad identifier or query param.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchInsufficientCreditsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No measurements for this track yet — create a `historical` measurement-job to backfill it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/research/albums/{id}/measurements": {
      "get": {
        "summary": "Album measurements",
        "description": "Latest measured count per track on an album, from the measurement store. `{id}` is a Spotify album id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Spotify album id."
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "spotify"
            },
            "description": "Platform to read. Currently `spotify`."
          },
          {
            "name": "metric",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "platform_displayed_play_count"
            },
            "description": "Metric to read."
          },
          {
            "name": "latest",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            },
            "description": "Return the latest capture per track (the only supported mode today)."
          }
        ],
        "responses": {
          "200": {
            "description": "Latest per-track measured counts for the album.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchAlbumMeasurementsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchInsufficientCreditsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No capture exists for this album yet — create a `current` measurement-job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/research/measurement-jobs": {
      "post": {
        "summary": "Create a measurement job",
        "description": "One async ingest resource. `source:\"current\"` captures present counts via the snapshot pipeline. `source:\"historical\"` enqueues each resolved recording for Songstats deep backfill ranked by all-time streams (idempotent — songs already carrying `songstats` history are skipped; no track is fetched twice). Provide exactly one of `catalog_id` / `album_ids` / `isrcs` in `scope`. The returned `id` is a snapshot id you can pass to [Create catalog](/api-reference/songs/catalogs-create) to materialize the measured tracks into an account-owned catalog.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResearchMeasurementJobRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchMeasurementJobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — provide a `source` and exactly one of `scope.catalog_id`, `scope.album_ids`, `scope.isrcs`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — invalid or missing API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Per-organization monthly cap reached (current jobs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResearchErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/runs/{runId}": {
      "get": {
        "description": "Status of an asynchronous run started via [`POST /api/chat/runs`](/api-reference/chat/runs). Returns a point-in-time snapshot (**is it done?**) — not the generated content.\n\n**Related endpoints**\n- [`POST /api/chat/runs`](/api-reference/chat/runs) — starts the run this reports on.\n- [`GET /api/chat/{chatId}/stream`](/api-reference/chat/workflow-stream) — read the **content**: poll *this* to know **whether** a run finished; use the stream to **watch the output** as it's produced.\n- [`POST /api/chat`](/api-reference/chat/workflow) — the interactive, streaming counterpart to a headless run.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "description": "The durable workflow run id returned by [POST /api/chat/runs](/api-reference/chat/runs).",
            "schema": {
              "type": "string"
            },
            "example": "wrun_01KVWZNM82NA7XKNEWWHG8VPHJ"
          }
        ],
        "responses": {
          "200": {
            "description": "Run status snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatRunStatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "No run found for the given runId",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatGenerateErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "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": {
      "StopChatResponse": {
        "type": "object",
        "required": [
          "success",
          "stopped"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true when the request was processed.",
            "example": true
          },
          "stopped": {
            "type": "boolean",
            "description": "True when an in-progress response was cancelled; false when nothing was running.",
            "example": true
          }
        }
      },
      "StopChatErrorResponse": {
        "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"
          }
        }
      },
      "ChatGenerateErrorResponse": {
        "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"
          }
        }
      },
      "ChatGenerateRequest": {
        "type": "object",
        "description": "Request body for chat generation. Exactly one of 'prompt' or 'messages' must be provided.",
        "properties": {
          "prompt": {
            "type": "string",
            "description": "Single text prompt for the assistant. Required if 'messages' is not provided."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UIMessage"
            },
            "description": "Array of UIMessage objects for context. Required if 'prompt' is not provided."
          },
          "artistId": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the artist (optional)"
          },
          "model": {
            "type": "string",
            "description": "The AI model to use for text generation (optional)",
            "example": "openai/gpt-5-mini"
          }
        }
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "id",
          "room_id",
          "content",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the memory message"
          },
          "room_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the parent chat room"
          },
          "content": {
            "type": "object",
            "description": "Structured message payload stored for the memory"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of the memory update"
          }
        }
      },
      "ChatRoom": {
        "type": "object",
        "required": [
          "id",
          "title",
          "accountId",
          "sessionId",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Chat id."
          },
          "title": {
            "type": "string",
            "description": "Display title for the chat."
          },
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "Owning account for this chat."
          },
          "sessionId": {
            "type": "string",
            "format": "uuid",
            "description": "Session that owns this chat. Combine with `id` to build the chat URL: `/sessions/{sessionId}/chats/{id}`."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO timestamp of the last update."
          },
          "artistId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Artist account id this chat's session was created in the context of, or `null` when no artist was associated. Inherited from `sessions.artist_id` on the owning session."
          }
        }
      },
      "ChatStreamErrorResponse": {
        "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"
          }
        }
      },
      "ChatWorkflowRequest": {
        "type": "object",
        "description": "Request body for the sandbox-driven, Workflow-backed chat endpoint. Requires an existing session, chat, and provisioned sandbox. The Bearer key from the `Authorization` header is forwarded into the sandbox for skill-based callbacks to the Recoup API — no separate access-token field needed.",
        "required": [
          "messages",
          "chatId",
          "sessionId"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UIMessage"
            },
            "description": "Conversation history as an array of `UIMessage` objects, matching the [Vercel AI SDK](https://sdk.vercel.ai/) `UIMessage` shape. The last entry is typically the new user turn — the agent loop reads the entire array and persists the latest user message before the workflow starts."
          },
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of an existing chat under `sessionId`. The chat's `active_stream_id` column is used for compare-and-set race control across concurrent requests."
          },
          "sessionId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the session that owns both the chat and the sandbox. The session must have a non-null `sandbox_state` — call [POST /api/sandbox](/api-reference/sandbox/create) first if not."
          },
          "context": {
            "type": "object",
            "description": "Optional per-prompt context overrides forwarded into the agent loop.",
            "properties": {
              "contextLimit": {
                "type": "integer",
                "description": "Upper bound on the number of tokens / messages of conversation history the agent should consider for this prompt. Overrides the model's default for this call only."
              }
            }
          }
        }
      },
      "CompactChatsRequest": {
        "type": "object",
        "required": [
          "chatId"
        ],
        "properties": {
          "chatId": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Array of chat IDs to compact"
          },
          "prompt": {
            "type": "string",
            "description": "Optional prompt to control what information gets preserved in the compacted summary"
          }
        }
      },
      "CompactChatsResponse": {
        "type": "object",
        "required": [
          "chats"
        ],
        "properties": {
          "chats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompactedChat"
            },
            "description": "Array of compacted chat results"
          }
        }
      },
      "CompactedChat": {
        "type": "object",
        "required": [
          "chatId",
          "compacted"
        ],
        "properties": {
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the chat that was compacted"
          },
          "compacted": {
            "type": "string",
            "description": "The compacted summary text of the chat"
          }
        }
      },
      "CopyChatMessagesErrorResponse": {
        "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."
          }
        }
      },
      "CopyChatMessagesRequest": {
        "type": "object",
        "required": [
          "targetChatId"
        ],
        "properties": {
          "targetChatId": {
            "type": "string",
            "format": "uuid",
            "description": "Target chat room UUID to receive the copied messages."
          },
          "clearExisting": {
            "type": "boolean",
            "default": true,
            "description": "When true, existing messages in the target chat are deleted before copy."
          }
        }
      },
      "CopyChatMessagesResponse": {
        "type": "object",
        "required": [
          "status",
          "source_chat_id",
          "target_chat_id",
          "copied_count",
          "cleared_existing"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "source_chat_id": {
            "type": "string",
            "format": "uuid",
            "description": "Source chat room UUID."
          },
          "target_chat_id": {
            "type": "string",
            "format": "uuid",
            "description": "Target chat room UUID."
          },
          "copied_count": {
            "type": "integer",
            "description": "Number of messages copied from source to target."
          },
          "cleared_existing": {
            "type": "boolean",
            "description": "Whether existing target messages were deleted before copy."
          }
        }
      },
      "CreateChatErrorResponse": {
        "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"
          }
        }
      },
      "CreateChatRequest": {
        "type": "object",
        "properties": {
          "artistId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the artist account the chat is associated with"
          },
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID for the new chat (client-generated). If not provided, one will be generated automatically."
          },
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to create the chat for. Only applicable when the authenticated account has access to multiple accounts via organization membership. If not provided, the chat is created for the API key's own account."
          },
          "topic": {
            "type": "string",
            "description": "Topic name for the new chat room (e.g., 'Pulse Feb 2'). To edit the topic of an existing room, use [PATCH /api/chats](/api-reference/chat/update)."
          }
        }
      },
      "CreateChatResponse": {
        "type": "object",
        "required": [
          "status",
          "chat"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "chat": {
            "$ref": "#/components/schemas/ChatRoom",
            "description": "The created chat room object"
          }
        }
      },
      "DeleteChatErrorResponse": {
        "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."
          }
        }
      },
      "DeleteChatRequest": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier (UUID) of the chat room to delete."
          }
        }
      },
      "DeleteChatResponse": {
        "type": "object",
        "required": [
          "status",
          "id",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The UUID of the deleted chat room."
          },
          "message": {
            "type": "string",
            "description": "Success message describing the deletion result."
          }
        }
      },
      "DeleteTrailingChatMessagesErrorResponse": {
        "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."
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of missing or invalid parameter fields (when validation fails)."
          }
        }
      },
      "DeleteTrailingChatMessagesResponse": {
        "type": "object",
        "required": [
          "status",
          "chat_id",
          "from_message_id"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "chat_id": {
            "type": "string",
            "format": "uuid",
            "description": "The chat UUID where deletion was applied."
          },
          "from_message_id": {
            "type": "string",
            "format": "uuid",
            "description": "The message UUID used as the trailing deletion boundary."
          }
        }
      },
      "Error": {
        "required": [
          "error",
          "message"
        ],
        "type": "object",
        "properties": {
          "error": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "GetChatArtistErrorResponse": {
        "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"
          }
        }
      },
      "GetChatArtistResponse": {
        "type": "object",
        "required": [
          "status",
          "room_id",
          "artist_id",
          "artist_exists"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "room_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the chat room"
          },
          "artist_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "UUID of the artist account associated with the chat, or null when no artist is linked"
          },
          "artist_exists": {
            "type": "boolean",
            "description": "Whether an artist is linked to the chat room"
          }
        }
      },
      "GetChatMessagesErrorResponse": {
        "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"
          }
        }
      },
      "GetChatMessagesResponse": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            },
            "description": "Chronologically ordered list of messages for the chat"
          }
        }
      },
      "GetChatsErrorResponse": {
        "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"
          }
        }
      },
      "GetChatsResponse": {
        "type": "object",
        "required": [
          "status",
          "chats"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "chats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatRoom"
            },
            "description": "Array of chat objects"
          }
        }
      },
      "ResearchAlbumsResponse": {
        "type": "object",
        "description": "Artist catalog (albums view).",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "albums": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchCatalogItem"
            }
          }
        }
      },
      "ResearchAudienceResponse": {
        "type": "object",
        "description": "Audience demographics for the requested `platform`.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "result": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Data Retrieved."
          },
          "audience": {
            "type": "array",
            "description": "Per-platform audience demographic entries. May be empty when the provider has no data for the artist/platform.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "artist_info": {
            "$ref": "#/components/schemas/ResearchArtistRef"
          },
          "source_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ResearchCareerResponse": {
        "type": "object",
        "description": "Career activity feed.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "career": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchActivity"
            }
          }
        }
      },
      "ResearchDeepRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "description": "Request body for deep research. Performs comprehensive multi-source analysis.",
        "properties": {
          "query": {
            "type": "string",
            "description": "The research question — be specific and detailed for best results."
          }
        }
      },
      "ResearchDeepResponse": {
        "type": "object",
        "description": "Comprehensive research report with citations.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "example": "success"
          },
          "content": {
            "type": "string",
            "description": "The full research report as markdown."
          },
          "citations": {
            "type": "array",
            "description": "Source URLs cited in the report.",
            "items": {
              "type": "string",
              "format": "uri"
            }
          }
        }
      },
      "ResearchEnrichRequest": {
        "type": "object",
        "required": [
          "input",
          "schema"
        ],
        "properties": {
          "input": {
            "type": "string",
            "description": "What to research (e.g., \"Drake rapper from Dallas Texas\")."
          },
          "schema": {
            "type": "object",
            "description": "JSON schema defining the fields to extract. Must include `\"type\": \"object\"` at the top level.",
            "additionalProperties": true,
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "object"
                ],
                "description": "Must be \"object\""
              },
              "properties": {
                "type": "object",
                "description": "Field definitions to extract",
                "additionalProperties": true
              }
            },
            "required": [
              "type",
              "properties"
            ]
          },
          "processor": {
            "type": "string",
            "enum": [
              "base",
              "core",
              "ultra"
            ],
            "default": "base",
            "description": "Research depth: base (fast), core (balanced), ultra (comprehensive)."
          }
        }
      },
      "ResearchEnrichResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "output": {
            "type": "object",
            "description": "Structured data matching the provided schema.",
            "additionalProperties": true
          },
          "citations": {
            "type": "array",
            "description": "Source citations supporting the enriched output.",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "title": {
                  "type": "string"
                },
                "field": {
                  "type": "string",
                  "description": "Which output field this citation supports."
                }
              }
            }
          }
        }
      },
      "ResearchExtractRequest": {
        "type": "object",
        "required": [
          "urls"
        ],
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "maxItems": 10,
            "description": "URLs to extract content from (max 10).",
            "minItems": 1
          },
          "objective": {
            "type": "string",
            "description": "What information to focus on (optional, max 3000 chars)."
          },
          "full_content": {
            "type": "boolean",
            "default": false,
            "description": "Return full page content instead of focused excerpts."
          }
        }
      },
      "ResearchExtractResponse": {
        "type": "object",
        "required": [
          "status",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "example": "success"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchExtractResult"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "URLs that failed to extract. Only present when one or more URLs could not be processed."
          }
        }
      },
      "ResearchExtractResult": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "publish_date": {
            "type": "string",
            "nullable": true,
            "description": "Publish date in YYYY-MM-DD format."
          },
          "excerpts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Focused excerpts as markdown."
          },
          "full_content": {
            "type": "string",
            "nullable": true,
            "description": "Full page content as markdown."
          }
        }
      },
      "ResearchInsightsResponse": {
        "type": "object",
        "description": "Noteworthy activity highlights.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "insights": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchActivity"
            }
          }
        }
      },
      "ResearchLookupResponse": {
        "type": "object",
        "description": "Artist resolved from a Spotify URL or ID.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "result": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Data Retrieved."
          },
          "artist_info": {
            "$ref": "#/components/schemas/ResearchArtistInfo"
          }
        }
      },
      "ResearchMetricsResponse": {
        "type": "object",
        "description": "Current statistics for the artist on the requested `source`.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "result": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Data Retrieved."
          },
          "stats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchMetricStat"
            }
          },
          "artist_info": {
            "$ref": "#/components/schemas/ResearchArtistRef"
          },
          "source_ids": {
            "type": "array",
            "description": "Sources included in `stats`.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ResearchRefreshPendingResponse": {
        "type": "object",
        "required": [
          "status",
          "state",
          "message"
        ],
        "description": "Returned when metrics are being refreshed and no cached result is available yet.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending"
            ],
            "example": "pending"
          },
          "state": {
            "type": "string",
            "enum": [
              "refresh_pending"
            ],
            "example": "refresh_pending"
          },
          "message": {
            "type": "string",
            "example": "Research metrics refresh is pending. Retry this endpoint shortly."
          }
        }
      },
      "ResearchMilestonesResponse": {
        "type": "object",
        "description": "Milestone activity entries (same shape as career/insights).",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "milestones": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchActivity"
            }
          }
        }
      },
      "ResearchPeopleRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "string",
            "description": "Natural language search for people (e.g., \"A&R reps at Atlantic Records\")."
          },
          "num_results": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 10,
            "description": "Number of results to return."
          }
        }
      },
      "ResearchPeopleResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "example": "success"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchPeopleResult"
            }
          }
        }
      },
      "ResearchPeopleResult": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "Person name and role."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Profile URL (often LinkedIn)."
          },
          "id": {
            "type": "string",
            "description": "Unique result identifier from the search provider."
          },
          "publishedDate": {
            "type": "string",
            "nullable": true,
            "description": "Date the profile or page was published."
          },
          "author": {
            "type": "string",
            "nullable": true,
            "description": "Author of the page, if available."
          },
          "highlights": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Key excerpts from the profile."
          },
          "summary": {
            "type": "string",
            "nullable": true,
            "description": "Brief summary of the person."
          }
        }
      },
      "ResearchPlaylistPlacement": {
        "type": "object",
        "description": "A playlist the artist currently appears on.",
        "properties": {
          "playlist_id": {
            "type": "string"
          },
          "playlist_name": {
            "type": "string"
          },
          "external_url": {
            "type": "string",
            "description": "Link to the playlist on the platform."
          },
          "followers_total": {
            "type": "string",
            "description": "Human-readable follower count, e.g. \"34.3M\"."
          },
          "image_url": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": true
      },
      "ResearchPlaylistsResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "example": "success"
          },
          "placements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchPlaylistPlacement"
            }
          }
        }
      },
      "ResearchProfileResponse": {
        "type": "object",
        "description": "Full artist profile. For longitudinal platform metrics call `GET /api/research/metrics`.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "result": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Data Retrieved."
          },
          "artist_info": {
            "$ref": "#/components/schemas/ResearchArtistInfo"
          }
        }
      },
      "ResearchSearchResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "example": "success"
          },
          "results": {
            "type": "array",
            "description": "Matching artists with IDs, names, and basic metadata.",
            "items": {
              "$ref": "#/components/schemas/ResearchSearchResult"
            }
          }
        }
      },
      "ResearchSearchResult": {
        "type": "object",
        "description": "Search result. Additional type-specific identifier fields pass through. The set depends on `type` (artists | tracks | labels).",
        "properties": {
          "id": {
            "type": "string",
            "description": "Provider-neutral ID."
          },
          "name": {
            "type": "string"
          },
          "avatar": {
            "type": "string",
            "nullable": true
          },
          "site_url": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": true
      },
      "ResearchSimilarResponse": {
        "type": "object",
        "description": "Similar artists. Similarity is unweighted (no score/ranking fields).",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchArtistRef"
            }
          }
        }
      },
      "ResearchTrackResponse": {
        "type": "object",
        "description": "Track metadata and audio analysis.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "result": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Data Retrieved."
          },
          "track_info": {
            "$ref": "#/components/schemas/ResearchTrackInfo"
          },
          "audio_analysis": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchAudioAnalysisEntry"
            }
          }
        }
      },
      "ResearchTracksResponse": {
        "type": "object",
        "description": "Artist catalog (tracks view).",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "tracks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchCatalogItem"
            }
          }
        }
      },
      "ResearchUrlEntry": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string",
            "description": "Platform name (e.g., spotify, instagram)."
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ResearchUrlsResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "example": "success"
          },
          "urls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchUrlEntry"
            }
          }
        }
      },
      "ResearchWebRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "description": "Request body for web research.",
        "properties": {
          "query": {
            "type": "string",
            "description": "The search query — what you want to find on the web."
          },
          "max_results": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 10,
            "description": "Maximum number of results to return."
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO country code for regional results (e.g., 'US', 'GB')."
          }
        }
      },
      "ResearchWebResponse": {
        "type": "object",
        "description": "Web search results with titles, URLs, and content snippets.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "results": {
            "type": "array",
            "description": "Ranked web search results.",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "snippet": {
                  "type": "string",
                  "description": "Content snippet from the search result."
                },
                "date": {
                  "type": "string",
                  "nullable": true,
                  "description": "Publication date if available."
                },
                "last_updated": {
                  "type": "string",
                  "nullable": true,
                  "description": "Last updated date if available."
                }
              }
            }
          },
          "formatted": {
            "type": "string",
            "description": "Results formatted as markdown for easy reading."
          }
        }
      },
      "UIMessage": {
        "type": "object",
        "description": "A message in the chat conversation. See https://ai-sdk.dev/docs/reference/ai-sdk-core/ui-message for details.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the message"
          },
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant",
              "system"
            ],
            "description": "The role of the message sender"
          },
          "content": {
            "type": "string",
            "description": "The text content of the message"
          }
        }
      },
      "UpdateChatErrorResponse": {
        "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"
          }
        }
      },
      "UpdateChatRequest": {
        "type": "object",
        "required": [
          "chatId",
          "topic"
        ],
        "properties": {
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier (UUID) of the chat room to update"
          },
          "topic": {
            "type": "string",
            "minLength": 3,
            "maxLength": 50,
            "description": "The new display name for the chat room. Must be between 3 and 50 characters."
          }
        }
      },
      "UpdateChatResponse": {
        "type": "object",
        "required": [
          "status",
          "chat"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request",
            "example": "success"
          },
          "chat": {
            "$ref": "#/components/schemas/ChatRoom",
            "description": "The updated chat room object"
          }
        }
      },
      "ResearchErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "description": "Error response returned by all research endpoints for validation failures (400) and authentication errors (401).",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "example": "error"
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message describing what went wrong.",
            "example": "Missing required parameter: artist"
          }
        }
      },
      "ResearchArtistRef": {
        "type": "object",
        "description": "Provider-neutral artist reference.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Provider-neutral artist ID.",
            "example": "wjcgfd9i"
          },
          "name": {
            "type": "string",
            "example": "Drake"
          },
          "avatar": {
            "type": "string",
            "description": "Artist image URL.",
            "nullable": true
          },
          "site_url": {
            "type": "string",
            "description": "Artist page URL.",
            "nullable": true
          }
        },
        "additionalProperties": true
      },
      "ResearchArtistMini": {
        "type": "object",
        "description": "Minimal artist reference embedded in catalog/activity payloads.",
        "properties": {
          "name": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "ResearchLink": {
        "type": "object",
        "description": "External platform link.",
        "properties": {
          "source": {
            "type": "string",
            "description": "Platform name, e.g. `spotify`, `apple_music`."
          },
          "external_id": {
            "type": "string",
            "description": "Platform-specific ID."
          },
          "url": {
            "type": "string"
          },
          "isrc": {
            "type": "string",
            "description": "ISRC (tracks only)."
          }
        },
        "additionalProperties": true
      },
      "ResearchArtistInfo": {
        "type": "object",
        "description": "Full artist record — identity, bio, genres, platform links, and related artists.",
        "properties": {
          "name": {
            "type": "string"
          },
          "avatar": {
            "type": "string"
          },
          "site_url": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "description": "ISO country code.",
            "nullable": true
          },
          "bio": {
            "type": "string",
            "nullable": true
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchLink"
            }
          },
          "related_artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchArtistRef"
            }
          }
        },
        "additionalProperties": true
      },
      "ResearchActivityTrackInfo": {
        "type": "object",
        "description": "Track context attached to an activity entry.",
        "properties": {
          "title": {
            "type": "string"
          },
          "avatar": {
            "type": "string"
          },
          "release_date": {
            "type": "string",
            "nullable": true
          },
          "site_url": {
            "type": "string"
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchArtistMini"
            }
          }
        },
        "additionalProperties": true
      },
      "ResearchActivity": {
        "type": "object",
        "description": "Dated activity event. Shared by career, insights, and milestones.",
        "properties": {
          "source": {
            "type": "string",
            "description": "Platform, e.g. `spotify`."
          },
          "activity_text": {
            "type": "string",
            "description": "Human-readable activity description."
          },
          "activity_type": {
            "type": "string",
            "description": "e.g. `playlist`, `chart`."
          },
          "activity_date": {
            "type": "string",
            "example": "2026-06-04"
          },
          "activity_url": {
            "type": "string",
            "nullable": true
          },
          "activity_avatar": {
            "type": "string",
            "nullable": true
          },
          "activity_tier": {
            "type": "integer",
            "description": "Importance tier (lower = more significant)."
          },
          "track_info": {
            "$ref": "#/components/schemas/ResearchActivityTrackInfo"
          }
        },
        "additionalProperties": true
      },
      "ResearchCatalogItem": {
        "type": "object",
        "description": "Catalog entry. Unfiltered — high-catalog artists include remixes/features.",
        "properties": {
          "title": {
            "type": "string"
          },
          "avatar": {
            "type": "string"
          },
          "release_date": {
            "type": "string",
            "nullable": true
          },
          "site_url": {
            "type": "string"
          },
          "isrcs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchArtistMini"
            }
          }
        },
        "additionalProperties": true
      },
      "ResearchCollaborator": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "ResearchMetricStat": {
        "type": "object",
        "description": "Per-source current statistics.",
        "properties": {
          "source": {
            "type": "string",
            "description": "Platform the metrics belong to, e.g. `spotify`, `tiktok`."
          },
          "data": {
            "type": "object",
            "description": "Platform-specific metric fields. Keys vary by `source`.",
            "additionalProperties": {
              "type": "number"
            },
            "example": {
              "monthly_listeners_current": 99222567,
              "followers_total": 112035525,
              "popularity_current": 100,
              "streams_total": 137582496099,
              "playlists_current": 169220,
              "charts_current": 184
            }
          }
        },
        "additionalProperties": true
      },
      "ResearchAudioAnalysisEntry": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Audio feature name, e.g. `acousticness`, `tempo`."
          },
          "value": {
            "type": "string",
            "description": "Feature value (string-encoded)."
          }
        }
      },
      "ResearchTrackInfo": {
        "type": "object",
        "description": "Full track record.",
        "properties": {
          "songstats_track_id": {
            "type": "string",
            "description": "Songstats track id — pass to `GET /api/research/track/stats` or `GET /api/research/track`."
          },
          "title": {
            "type": "string"
          },
          "avatar": {
            "type": "string"
          },
          "site_url": {
            "type": "string"
          },
          "release_date": {
            "type": "string",
            "nullable": true
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchArtistMini"
            }
          },
          "is_remix": {
            "type": "boolean"
          },
          "collaborators": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchCollaborator"
            }
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "distributors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                }
              },
              "additionalProperties": true
            }
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchLink"
            }
          }
        },
        "additionalProperties": true
      },
      "ResearchTrackStatsResponse": {
        "type": "object",
        "description": "Per-track current statistics for the requested `source`(s). Mirrors `ResearchMetricsResponse`, scoped to a single recording. When every requested source is store-served, the response is the minimal envelope `{ result, stats }` — `track_info`, `source_ids`, and `message` appear only when Songstats served part of the request.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "result": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Data Retrieved."
          },
          "stats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchTrackStat"
            }
          },
          "track_info": {
            "$ref": "#/components/schemas/ResearchTrackInfo"
          },
          "source_ids": {
            "type": "array",
            "description": "All source identifiers Songstats tracks for this recording — a superset of the sources returned in `stats`.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ResearchTrackStat": {
        "type": "object",
        "description": "Per-source current statistics for a single track.",
        "properties": {
          "source": {
            "type": "string",
            "description": "Platform the stats belong to, e.g. `spotify`, `apple_music`, `deezer`, `youtube`."
          },
          "data": {
            "type": "object",
            "description": "Per-source counters for this track. `streams_total` is the absolute play count. Keys vary by `source`; optional `playlists[]` / `charts[]` / `reposters[]` arrays appear when the matching `with_*` toggle is set. Store-served (apify) entries carry only `streams_total` — a platform-displayed play count, not a royalty-bearing stream count.",
            "additionalProperties": true,
            "example": {
              "streams_total": 84213771,
              "popularity_current": 64,
              "playlists_current": 1200,
              "playlists_total": 4830,
              "playlist_reach_current": 18450201,
              "playlist_reach_total": 51200988,
              "charts_current": 3,
              "charts_total": 41,
              "shazams_total": 220145,
              "charted_cities_total": 87,
              "charted_countries_total": 23,
              "favorites_total": 0,
              "reposts_total": 0,
              "engagement_rate_total": 0,
              "playlists_editorial_current": 12,
              "playlists_editorial_total": 58
            }
          },
          "data_source": {
            "type": "string",
            "description": "Provenance of the numbers: `apify_spotify_playcount` (platform-displayed count from the measurement store), `songstats` (Songstats backfill), or `granted_analytics` (customer-granted data).",
            "example": "apify_spotify_playcount"
          },
          "captured_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this entry's values were captured into the measurement store. Present only on store-served entries (`data_source: \"apify_spotify_playcount\"`)."
          }
        },
        "additionalProperties": true
      },
      "ResearchInsufficientCreditsResponse": {
        "type": "object",
        "description": "Returned (402) when the account lacks research credits and auto-recharge did not cover the call.",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "insufficient_credits"
            ]
          },
          "remaining_credits": {
            "type": "integer",
            "example": 0
          },
          "required_credits": {
            "type": "integer",
            "example": 5
          },
          "checkoutUrl": {
            "type": "string",
            "description": "Stripe checkout link to top up credits."
          },
          "declineReason": {
            "type": "string",
            "description": "Card decline reason when auto-recharge was attempted and failed."
          }
        },
        "required": [
          "error",
          "remaining_credits",
          "required_credits",
          "checkoutUrl"
        ]
      },
      "ResearchMeasurementPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "example": "2026-06-12"
          },
          "value": {
            "type": "number",
            "description": "Cumulative metric value as of `date`.",
            "example": 297289495
          },
          "data_source": {
            "type": "string",
            "example": "songstats"
          }
        }
      },
      "ResearchMeasurementsResponse": {
        "type": "object",
        "description": "A track's measured series, or — when `aggregate` is set — the derived projection.",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "id": {
            "type": "string",
            "description": "The provider-neutral track id queried.",
            "example": "USQY51771120"
          },
          "platform": {
            "type": "string",
            "example": "spotify"
          },
          "metric": {
            "type": "string",
            "example": "platform_displayed_play_count"
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchMeasurementPoint"
            },
            "description": "Present unless `aggregate` is set."
          },
          "aggregate": {
            "type": "object",
            "nullable": true,
            "description": "Present when `aggregate=run_rate`.",
            "properties": {
              "kind": {
                "type": "string",
                "example": "run_rate"
              },
              "window_days": {
                "type": "integer",
                "example": 365
              },
              "delta": {
                "type": "number",
                "example": 42000000
              },
              "run_rate_annualized": {
                "type": "number",
                "example": 42000000
              }
            }
          }
        }
      },
      "ResearchAlbumMeasurementItem": {
        "type": "object",
        "properties": {
          "isrc": {
            "type": "string",
            "example": "USQY51771120"
          },
          "spotify_track_id": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "value": {
            "type": "number",
            "description": "Latest measured count.",
            "example": 297289495
          },
          "captured_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_source": {
            "type": "string",
            "example": "apify_spotify_playcount"
          }
        }
      },
      "ResearchAlbumMeasurementsResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "id": {
            "type": "string",
            "description": "Spotify album id.",
            "example": "70Zkfb99ladZ3q0JVg97co"
          },
          "platform": {
            "type": "string",
            "example": "spotify"
          },
          "metric": {
            "type": "string",
            "example": "platform_displayed_play_count"
          },
          "measurements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchAlbumMeasurementItem"
            }
          }
        }
      },
      "ResearchMeasurementJobScope": {
        "type": "object",
        "description": "Provide exactly one of `catalog_id`, `album_ids`, or `isrcs`.",
        "properties": {
          "catalog_id": {
            "type": "string",
            "format": "uuid"
          },
          "album_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isrcs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ResearchMeasurementJobRequest": {
        "type": "object",
        "required": [
          "scope",
          "source"
        ],
        "properties": {
          "scope": {
            "$ref": "#/components/schemas/ResearchMeasurementJobScope"
          },
          "source": {
            "type": "string",
            "enum": [
              "current",
              "historical"
            ],
            "description": "`current` = capture present counts (Apify snapshot). `historical` = enqueue Songstats deep backfill."
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": [
              "spotify"
            ],
            "example": [
              "spotify"
            ]
          }
        }
      },
      "ResearchMeasurementJobResponse": {
        "type": "object",
        "description": "For `current` jobs, `id` is pollable. For `historical` jobs, `enqueued`/`skipped` summarize the backfill enqueue (the queue drains via the daily worker).",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "source": {
            "type": "string",
            "example": "historical"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Job id for `current` jobs; null for `historical`."
          },
          "state": {
            "type": "string",
            "enum": [
              "queued"
            ],
            "nullable": true,
            "example": "queued"
          },
          "enqueued": {
            "type": "integer",
            "nullable": true,
            "description": "`historical`: recordings enqueued for backfill.",
            "example": 842
          },
          "skipped": {
            "type": "integer",
            "nullable": true,
            "description": "`historical`: recordings already backfilled (skipped).",
            "example": 3
          },
          "album_count": {
            "type": "integer",
            "nullable": true,
            "description": "`current`: albums the job will capture."
          },
          "estimated_cost_usd": {
            "type": "number",
            "nullable": true,
            "description": "`current`: scraper cost estimate before spend."
          }
        }
      },
      "ChatGenerateAcceptedResponse": {
        "type": "object",
        "required": [
          "runId",
          "chatId",
          "sessionId"
        ],
        "description": "Confirmation that an asynchronous chat-generation run has been started on the durable agent workflow.",
        "properties": {
          "runId": {
            "type": "string",
            "description": "Durable workflow run id for the started generation. Same identifier surfaced as the `x-workflow-run-id` header on interactive [POST /api/chat](/api-reference/chat/workflow).",
            "example": "wrun_01KVWZNM82NA7XKNEWWHG8VPHJ"
          },
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "Chat the run writes its assistant messages to. Use with [`GET /api/chat/{chatId}/stream`](/api-reference/chat/workflow-stream) to resume the stream, or to fetch the persisted messages.",
            "example": "24830c6c-76d8-43be-ae22-1dfd545421ab"
          },
          "sessionId": {
            "type": "string",
            "format": "uuid",
            "description": "Session (workspace + sandbox) provisioned for the run.",
            "example": "fa9c1516-35f0-4efd-a62c-01af26324e72"
          }
        }
      },
      "ChatRunStatusResponse": {
        "type": "object",
        "required": [
          "runId",
          "status"
        ],
        "description": "Point-in-time status of an asynchronous chat-generation run.",
        "properties": {
          "runId": {
            "type": "string",
            "description": "The durable workflow run id.",
            "example": "wrun_01KVWZNM82NA7XKNEWWHG8VPHJ"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed",
              "cancelled"
            ],
            "description": "Lifecycle state of the run. Read the produced content via the chat (`chatId` from the start response)."
          }
        }
      }
    },
    "responses": {
      "ResearchDataSourceUnsupported": {
        "description": "The configured research data source does not support this endpoint or data shape.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ResearchErrorResponse"
            },
            "example": {
              "status": "error",
              "error": "Request failed with status 501"
            }
          }
        }
      }
    }
  }
}