{
  "openapi": "3.1.0",
  "info": {
    "title": "Recoup API - Release Players",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.recoupable.dev"
    }
  ],
  "paths": {
    "/api/players": {
      "get": {
        "summary": "List release players",
        "description": "List up to 100 players in the authenticated workspace. MCP: list_release_player.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid",
              "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PlayerList fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a release player",
        "description": "Register one reusable listening page plus provider embeds. Returns player, listenUrl, spotifyEmbedUrl, appleEmbedUrl. MCP: create_release_player. No site generation is required. This feature requires its API/app/database releases.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [],
        "responses": {
          "201": {
            "description": "PlayerResource fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerResource"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "402": {
            "description": "Publishing requires an active paid workspace",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artistId",
                  "name"
                ],
                "properties": {
                  "organizationId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
                  },
                  "artistId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Artist account belonging to this workspace."
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "spotifyUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Spotify track, album, or playlist URL. Share parameters are removed."
                  },
                  "appleUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Apple Music song or album URL, optionally an album ?i= song selection."
                  },
                  "artwork": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Optional public HTTPS artwork."
                  },
                  "allowedOrigins": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "description": "Exact HTTPS origins authorized to embed this player; no path, query, credentials, or fragment."
                  },
                  "enabled": {
                    "type": "boolean",
                    "default": false,
                    "description": "Publishing requires an active paid workspace. Disabled players are unavailable publicly."
                  },
                  "freePlayback": {
                    "type": "string",
                    "enum": [
                      "spotify",
                      "audio"
                    ],
                    "default": "spotify",
                    "description": "Release-owner choice for verified Spotify Free accounts. spotify opens the configured Spotify release; audio plays a workspace-owned uploaded file. Premium continues using Spotify streaming."
                  },
                  "audioUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "MP3 or WAV URL returned by POST /api/sites/assets for the same workspace. Requires an existing audio object; arbitrary or other-workspace URLs are rejected. Required when freePlayback is audio. Uploads currently have a 4 MB limit."
                  }
                },
                "description": "At least one of spotifyUrl or appleUrl is required. Disabled by default.",
                "anyOf": [
                  {
                    "required": [
                      "spotifyUrl"
                    ],
                    "properties": {
                      "spotifyUrl": {
                        "type": "string",
                        "format": "uri"
                      }
                    }
                  },
                  {
                    "required": [
                      "appleUrl"
                    ],
                    "properties": {
                      "appleUrl": {
                        "type": "string",
                        "format": "uri"
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/players/{id}": {
      "get": {
        "summary": "Read a release player",
        "description": "Read settings, revision and share/embed links. MCP: get_release_player.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid",
              "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PlayerResource fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerResource"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a release player",
        "description": "Edit branding, DSP destinations, embed origins, or enabled. Requires current revision; stale edits return 409. Artist/owner cannot change. Any update invalidates existing listening sessions. MCP: update_release_player.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PlayerResource fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerResource"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "402": {
            "description": "Publishing requires an active paid workspace",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "409": {
            "description": "The player revision changed. Read the latest settings before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "organizationId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "spotifyUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Spotify track, album, or playlist URL. Share parameters are removed."
                  },
                  "appleUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Apple Music song or album URL, optionally an album ?i= song selection."
                  },
                  "artwork": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Optional public HTTPS artwork."
                  },
                  "allowedOrigins": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "description": "Exact HTTPS origins authorized to embed this player; no path, query, credentials, or fragment."
                  },
                  "enabled": {
                    "type": "boolean",
                    "default": false,
                    "description": "Publishing requires an active paid workspace. Disabled players are unavailable publicly."
                  },
                  "freePlayback": {
                    "type": "string",
                    "enum": [
                      "spotify",
                      "audio"
                    ],
                    "description": "Release-owner choice for verified Spotify Free accounts. spotify opens the configured Spotify release; audio plays a workspace-owned uploaded file. Premium continues using Spotify streaming."
                  },
                  "audioUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "MP3 or WAV URL returned by POST /api/sites/assets for the same workspace. Requires an existing audio object; arbitrary or other-workspace URLs are rejected. Required when freePlayback is audio. Uploads currently have a 4 MB limit."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/players/{id}/activity": {
      "get": {
        "summary": "Read player listening activity",
        "description": "Private 30-day summary plus up to 100 events, newest first. Returns measurement=browser_reported_playback, dspStreams=null, periodDays, offset, limit and report (sessions, connectedFans, reportedListeningMs, playEvents, campaigns, activity). Activity has session_id, provider, track_id, event, position_ms, listened_ms, received_at, fan_id and display_name. Anonymous Apple sessions have no fan identity. These are reported SDK observations, not DSP stream counts, cross-device monitoring, or causal uplift. MCP: get_release_player_activity.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid",
              "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PlayerReportResponse fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerReportResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        }
      }
    },
    "/api/players/{id}/fans": {
      "get": {
        "summary": "Read artist player fans",
        "description": "Artist-level Spotify fan relationships across releases in the authorized workspace. Each row keeps its stable relationship id and adds contact_id, the workspace-owned contact shared across artist relationships. Email/display_name come from the latest available confirmed workspace contact profile. scope=artist_in_workspace; marketingConsent=false. No provider tokens or cross-workspace identity links. MCP: get_release_player_fans.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid",
              "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PlayerFansResponse fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerFansResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        }
      }
    },
    "/api/players/public/{id}": {
      "get": {
        "summary": "Read public player metadata without creating a session",
        "description": "Internal transport for the trusted Recoup browser player. Without provider, returns only public release settings. With provider, validates parent and creates or resumes a signed, provider-bound listening session. No owner, fan profile or email is exposed. The embedding website should use returned embed URLs rather than calling this API or handling credentials.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PublicPlayerConfig fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPlayerConfig"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        }
      }
    },
    "/api/players/events": {
      "post": {
        "summary": "Record reported listening",
        "description": "Internal trusted Recoup player transport. Requires its Origin plus signed flow; browser-facing same-origin app proxy forwards to API. Provider and fan attribution come from the stored session. Event UUID makes retries idempotent, listenedMs is capped at 30 seconds and checked against elapsed server time. Recording failures do not stop music.",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "ListeningReceipt fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListeningReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "flow",
                  "event"
                ],
                "properties": {
                  "flow": {
                    "type": "string",
                    "maxLength": 2048
                  },
                  "event": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "id",
                      "provider",
                      "event"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "provider": {
                        "type": "string",
                        "enum": [
                          "spotify",
                          "apple_music"
                        ]
                      },
                      "event": {
                        "type": "string",
                        "enum": [
                          "connected",
                          "playing",
                          "paused",
                          "stopped",
                          "track_changed",
                          "skip",
                          "heartbeat",
                          "disconnected"
                        ]
                      },
                      "trackId": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "positionMs": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 86400000,
                        "default": 0
                      },
                      "listenedMs": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 30000,
                        "default": 0
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/players/spotify/session": {
      "post": {
        "summary": "Exchange player Spotify authorization",
        "description": "Internal trusted Recoup player endpoint, not an external website integration. Requires a fresh Spotify listening session, PKCE code/verifier, API-configured OAuth app and required scopes. Captures Spotify-confirmed available profile/email for the registered artist/workspace. Returns provider credentials only to the trusted Recoup browser, plus player_session_id and fanCapture; no email/fan ID in the response. Tokens are not persisted in fan tables. A failed capture reports fanCapture=false while valid playback credentials remain usable. Authorization is not email marketing consent.",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "SpotifyPlayerSession fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpotifyPlayerSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "verifier",
                  "flow"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048
                  },
                  "verifier": {
                    "type": "string",
                    "minLength": 43,
                    "maxLength": 128
                  },
                  "flow": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/players/public/{id}/session": {
      "post": {
        "summary": "Acquire or resume a registered player session",
        "description": "Internal transport for the trusted Recoup browser player. Without provider, returns only public release settings. With provider, validates parent and creates or resumes a signed, provider-bound listening session. No owner, fan profile or email is exposed. The embedding website should use returned embed URLs rather than calling this API or handling credentials.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PublicPlayerConfig fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPlayerConfig"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "404": {
            "description": "Player not available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        },
        "operationId": "acquirePlayerSession",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "spotify",
                      "apple_music"
                    ]
                  },
                  "parent": {
                    "type": "string",
                    "minLength": 1
                  },
                  "flow": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "medium": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "campaign": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "content": {
                    "type": "string",
                    "maxLength": 100
                  }
                },
                "required": [
                  "provider",
                  "parent"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/api/players/fans": {
      "get": {
        "summary": "Read workspace player fans",
        "description": "One contact per workspace and Spotify provider identity, with all artist relationships for that workspace. Authentication and current workspace access are required. Never merged by email/name or linked across organizations. Sign-in does not grant email marketing consent. MCP: get_workspace_player_fans.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid",
              "description": "Authorized workspace; omit for the authenticated account. Never send account_id."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PlayerFansResponse fields returned by the release player service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspacePlayerFansResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid authentication or failed provider authorization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "403": {
            "description": "Workspace, origin, or session access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "429": {
            "description": "Request rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          },
          "503": {
            "description": "Feature/configuration temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "PlayerError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "ReleasePlayer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "owner_id": {
            "type": "string",
            "format": "uuid"
          },
          "artist_id": {
            "type": "string",
            "format": "uuid"
          },
          "created_by": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "spotify_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "apple_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "artwork": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "allowed_origins": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "free_playback": {
            "type": "string",
            "enum": [
              "spotify",
              "audio"
            ],
            "default": "spotify",
            "description": "Release-owner choice for verified Spotify Free accounts. spotify opens the configured Spotify release; audio plays a workspace-owned uploaded file. Premium continues using Spotify streaming."
          },
          "audio_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "MP3 or WAV URL returned by POST /api/sites/assets for the same workspace. Requires an existing audio object; arbitrary or other-workspace URLs are rejected. Required when freePlayback is audio. Uploads currently have a 4 MB limit."
          }
        },
        "required": [
          "id",
          "owner_id",
          "artist_id",
          "name",
          "spotify_url",
          "apple_url",
          "allowed_origins",
          "enabled",
          "revision",
          "free_playback",
          "audio_url"
        ]
      },
      "PlayerResource": {
        "type": "object",
        "properties": {
          "player": {
            "$ref": "#/components/schemas/ReleasePlayer"
          },
          "listenUrl": {
            "type": "string",
            "format": "uri"
          },
          "spotifyEmbedUrl": {
            "type": "string",
            "format": "uri",
            "description": "Stable provider route. This URL is returned even when that provider is unconfigured; inspect the corresponding destination in player before displaying it."
          },
          "appleEmbedUrl": {
            "type": "string",
            "format": "uri",
            "description": "Stable provider route. This URL is returned even when that provider is unconfigured; inspect the corresponding destination in player before displaying it."
          }
        },
        "required": [
          "player",
          "listenUrl",
          "spotifyEmbedUrl",
          "appleEmbedUrl"
        ]
      },
      "PlayerList": {
        "type": "object",
        "properties": {
          "players": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleasePlayer"
            }
          },
          "offset": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "nextOffset": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Use as offset for the next page; null marks the end."
          }
        },
        "required": [
          "players"
        ]
      },
      "PlayerFan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string",
            "const": "spotify"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "first_connected_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_connected_at": {
            "type": "string",
            "format": "date-time"
          },
          "contact_id": {
            "type": "string",
            "format": "uuid",
            "description": "Workspace-owned contact shared across this fan’s artist relationships."
          }
        },
        "required": [
          "id",
          "provider",
          "first_connected_at",
          "last_connected_at",
          "contact_id"
        ]
      },
      "PlayerFansResponse": {
        "type": "object",
        "properties": {
          "fans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlayerFan"
            }
          },
          "scope": {
            "type": "string",
            "const": "artist_in_workspace"
          },
          "marketingConsent": {
            "type": "boolean",
            "const": false
          },
          "offset": {
            "type": "integer",
            "format": "int64"
          },
          "limit": {
            "type": "integer",
            "format": "int64"
          }
        },
        "required": [
          "fans",
          "scope",
          "marketingConsent",
          "offset",
          "limit"
        ]
      },
      "PlayerActivity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "event": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "spotify",
              "apple_music"
            ]
          },
          "track_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "position_ms": {
            "type": "integer",
            "format": "int64"
          },
          "listened_ms": {
            "type": "integer",
            "format": "int64"
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          },
          "fan_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "required": [
          "id",
          "session_id",
          "event",
          "provider",
          "listened_ms",
          "received_at"
        ],
        "description": "Raw database reporting row; snake_case keys are preserved intentionally."
      },
      "PlayerReportResponse": {
        "type": "object",
        "properties": {
          "measurement": {
            "type": "string",
            "const": "browser_reported_playback"
          },
          "dspStreams": {
            "type": "null"
          },
          "periodDays": {
            "type": "integer",
            "const": 30
          },
          "offset": {
            "type": "integer",
            "format": "int64"
          },
          "limit": {
            "type": "integer",
            "const": 100
          },
          "report": {
            "type": "object",
            "properties": {
              "sessions": {
                "type": "integer",
                "format": "int64"
              },
              "connectedFans": {
                "type": "integer",
                "format": "int64"
              },
              "reportedListeningMs": {
                "type": "integer",
                "format": "int64"
              },
              "playEvents": {
                "type": "integer",
                "format": "int64"
              },
              "campaigns": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "string"
                    },
                    "source": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "campaign": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "sessions": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "listened_ms": {
                      "type": "integer",
                      "format": "int64"
                    }
                  },
                  "required": []
                }
              },
              "activity": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PlayerActivity"
                }
              }
            },
            "required": [
              "sessions",
              "connectedFans",
              "reportedListeningMs",
              "playEvents",
              "campaigns",
              "activity"
            ]
          }
        },
        "required": [
          "measurement",
          "dspStreams",
          "periodDays",
          "offset",
          "limit",
          "report"
        ],
        "description": "Reporting envelope uses camelCase. Nested activity and campaign rows intentionally expose raw database snake_case fields."
      },
      "PublicPlayerConfig": {
        "type": "object",
        "properties": {
          "playerId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "artwork": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "spotifyUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "appleUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "provider": {
            "type": "string",
            "enum": [
              "spotify",
              "apple_music"
            ]
          },
          "release": {
            "type": "string",
            "format": "uri"
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "flow": {
            "type": "string",
            "description": "Short-lived signed player capability; only the trusted Recoup player should handle this."
          },
          "spotify": {
            "type": "object",
            "properties": {
              "configured": {
                "type": "boolean"
              },
              "clientId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "redirectUri": {
                "type": "string",
                "format": "uri"
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "configured",
              "clientId",
              "redirectUri",
              "scopes"
            ]
          },
          "freePlayback": {
            "type": "string",
            "enum": [
              "spotify",
              "audio"
            ],
            "default": "spotify",
            "description": "Release-owner choice for verified Spotify Free accounts. spotify opens the configured Spotify release; audio plays a workspace-owned uploaded file. Premium continues using Spotify streaming."
          },
          "audioUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Included only in provider-session responses. Configured uploaded audio is provided for Spotify sessions in audio mode; otherwise null. Not included in chooser metadata."
          }
        },
        "required": [
          "playerId",
          "name",
          "spotifyUrl",
          "appleUrl",
          "revision",
          "freePlayback"
        ]
      },
      "ListeningReceipt": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "recorded": {
            "type": "boolean",
            "description": "False for an already-recorded retry."
          }
        },
        "required": [
          "success",
          "recorded"
        ]
      },
      "SpotifyPlayerSession": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Private provider access token, delivered only to the trusted Recoup browser. Never embed in an artist website."
          },
          "refresh_token": {
            "type": "string",
            "description": "Optional private provider refresh token; same trusted-browser boundary."
          },
          "expires_in": {
            "type": "number"
          },
          "token_type": {
            "type": "string"
          },
          "scope": {
            "type": "string"
          },
          "player_session_id": {
            "type": "string",
            "format": "uuid"
          },
          "fanCapture": {
            "type": "boolean",
            "description": "True only after the verified fan identity was persisted."
          }
        },
        "required": [
          "access_token",
          "expires_in",
          "scope",
          "player_session_id",
          "fanCapture"
        ]
      },
      "WorkspacePlayerFansResponse": {
        "type": "object",
        "required": [
          "fans",
          "scope",
          "marketingConsent",
          "offset",
          "limit",
          "nextOffset"
        ],
        "properties": {
          "fans": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "provider",
                "email",
                "display_name",
                "first_connected_at",
                "last_connected_at",
                "artists"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "Contact ID within this workspace, not an artist relationship ID."
                },
                "provider": {
                  "type": "string",
                  "const": "spotify"
                },
                "email": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "display_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "first_connected_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "last_connected_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "artists": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "artist_id",
                      "first_connected_at",
                      "last_connected_at"
                    ],
                    "properties": {
                      "artist_id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "first_connected_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "last_connected_at": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          },
          "scope": {
            "type": "string",
            "const": "workspace"
          },
          "marketingConsent": {
            "type": "boolean",
            "const": false
          },
          "offset": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "nextOffset": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      }
    }
  }
}