Skip to main content
Release dependency: these routes require the site fan connection API release and its database migration. The skill alone does not enable them. Base URL: https://api.recoupable.dev. Private calls use x-api-key: $RECOUP_API_KEY (or the platform’s existing bearer authentication). Customer credentials stay with the agent/server. Public connection entry and callback use browser-bound OAuth state, not the customer API key.

Customer and site

  • GET /api/accounts/id: verify the current credential.
  • POST /api/agents/signup with { "email": "customer@example.com" }: request email verification.
  • POST /api/agents/verify with { "email": "customer@example.com", "code": "123456" }: receive api_key; never echo it.
  • GET /api/artists: inspect the caller’s roster and choose the artist’s account ID.
  • GET /api/sites: inspect existing site records.
  • POST /api/sites with { "name": "Release experience", "artistId": "ARTIST_ACCOUNT_UUID", "releaseUrl": "https://open.spotify.com/track/TRACK_ID" }: register a site. Add organizationId only for a selected authorized workspace. Read the returned site.id. Omit owner/account ID fields; ownership comes from authentication.

Configure

GET /api/sites/{siteId}/fan-connection returns:
Existing config contains site_id, return_url, marketing_text, enabled and revision. Update with PUT /api/sites/{siteId}/fan-connection:
Use the real artist’s approved text, not the example unchanged. Return URL must be HTTPS, at most 2048 characters, without credentials or fragments. Text length is 20–1000 characters. Pass the last read revision, or 0 for initial configuration. The response has the same envelope with persisted config and the public connectUrl; always use that returned URL.
  • 400: invalid input or missing artist attribution.
  • 401/403: reconnect the customer or select an authorized workspace.
  • 402: no eligible paid subscription for the site’s owning account.
  • 404: missing site or API not deployed; inspect the actual response.
  • 409: another configuration update won; reload and reconcile before retry.
  • 503: provider, billing or storage unavailable; surface the dependency.
Spotify fan connection is included in an active paid Recoup subscription owned by the site’s workspace. No separate add-on is required. Existing POST /api/subscriptions/sessions accepts {plan: "starter" | "pro", successUrl, cancelUrl?} and returns the customer checkout URL. Let the customer complete payment; then retry activation. Fans do not buy this feature.

Public fan journey

GET /api/sites/public/{siteId}/spotify displays the site’s agreement. Its own form POST binds acceptance to that browser and configuration revision, then redirects to Spotify PKCE authorization. Do not handcraft this POST or bypass the page. Spotify returns to Recoup’s /api/sites/spotify/callback; Recoup then returns to the configured site URL with recoup_spotify=connected, cancelled or failed. Only a successful provider exchange with user-read-email and user-read-private creates records. Actual granted scopes and the exact accepted marketing text are retained separately from the successful connection. Email may be absent. No tokens or profile details are passed to the public site.

Read fans

GET /api/sites/{siteId}/fans?offset=0&limit=50 (private):
Each fan has id, site_id, spotify_id, display_name, email, first/last connection timestamps and nested site_fan_connections with site_fan_permissions and site_fan_marketing_consents. Paginate up to 100 fans per page. Records are private to authorized workspace members. Read access remains available after paid eligibility expires. Repeated connection updates the existing fan for that site and adds a connection history entry. The site’s owner and artist supply attribution. These records are separate from email-only signups and other artist audience data.

Recoup operator setup

Operators must apply 20260928010000_site_fan_connections.sql, configure SITES_SPOTIFY_CLIENT_ID, register an HTTPS callback ending /api/sites/spotify/callback, set SITES_FAN_SPOTIFY_REDIRECT_URI. This is Recoup service configuration, not work to impose on each artist. The existing browser playback callback remains separate.

Activity reporting

GET /api/sites/{siteId}/activity returns 30-day counts for visits, starts, completions, replays and shares to authorized workspace members. POST /api/sites/public/{siteId}/activity accepts {id: EVENT_UUID, visitId: VISIT_UUID, event: "visit" | "start" | "complete" | "replay" | "share"}. Reuse the event UUID when retrying. Use a site-scoped visit ID. Never send emails, profile data or arbitrary page contents to this endpoint. These are browser-reported interactions, not verified Spotify listening or a named fan activity history. Recoup-hosted generated sites use the trusted host bridge: window.recoup.track(event) reports approved events; window.recoup.join() reveals the trusted signup controls. External sites should implement the equivalent fixed-event transport and keep account credentials on the server.

Playback for Premium and Free listeners

The hosted player reads the connected Spotify profile’s product field. Premium listeners use the Spotify Web Playback SDK. Free (free or open) listeners use the site’s saved, verified recording through the same play/pause, seek and volume controls. If the profile cannot be read, its subscription remains unknown; available artist audio keeps playback usable. The player also handles a Spotify SDK account rejection by switching to the recording. GET /api/sites/public/{siteId} includes playbackAudioUrl, a fresh signed audio URL valid for one hour, or null when no matching verified recording is available. It is resolved from the published site’s workspace-owned context request and matching Spotify track; private research and analysis are excluded. Fetch a fresh public response when reloading rather than storing this URL permanently. Publish only audio you have permission to serve to fans. File playback is not a Spotify stream. Spotify playback subscription, Recoup’s paid fan-connection entitlement, and fan capture are separate concerns. A missing audio file must show an honest unavailable state while keeping the game playable.