Recoup

Get started with Recoup.

Sites

Sites

Create, generate, and publish fan experiences through the API or MCP.

On this page

Sites uses the same backend operations from the web app, HTTP API, and MCP. A new site is a private draft. Generation never publishes it automatically.

Authentication

Private HTTP endpoints accept exactly one of x-api-key: YOUR_API_KEY or Authorization: Bearer YOUR_PRIVY_ACCESS_TOKEN. MCP uses the existing authenticated https://api.recoupable.dev/mcp connection.

The caller is derived from authentication. Do not send an account_id. An optional organizationId selects a workspace the caller can access. Omit it for the authenticated account's workspace.

Endpoints and tools

HTTPMCP toolResult
GET /api/siteslist_sites{ sites: [...] }
POST /api/sitescreate_site{ site: ... }, HTTP 201
GET /api/sites/{id}get_site{ site: ... }
PATCH /api/sites/{id} action conceptspropose_site_conceptsShort pitches or missing context
PATCH /api/sites/{id} action generategenerate_siteGenerate from an optional customer-selected pitch; otherwise select a concept
PATCH /api/sites/{id} action generationget_site_generationStatus for the specified generation token
PATCH /api/sites/{id} action publishpublish_sitePublish the saved draft or the playable build selected by generationToken
PATCH /api/sites/{id} action unpublishunpublish_sitePublic snapshot removed, draft retained
PATCH /api/sites/{id} action deletedelete_site{ "deleted": true, "id": "..." }
GET /api/sites/{id}/signupsget_site_signups{ signups: [{ email, created_at }] }, up to 10,000
POST /api/sites/assetsupload_site_asset{ asset: { url, name, type } }

Listing accepts optional organizationId and artistId query parameters. All private reads and writes verify workspace access. Signup emails are never part of the public response.

Create and generate

Create a draft with a Spotify track, album, or playlist URL. Recoup retrieves its title and artwork. The brief is optional; without it, Recoup uses the available song and artist context to propose activities. Alternatively, supply both a name and a brief without a release URL.

bash
curl https://api.recoupable.dev/api/sites \
  -H "x-api-key: $RECOUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"releaseUrl":"https://open.spotify.com/track/TRACK_ID","brief":"Build a maze game"}'

Optional creation fields: name (120 characters), brief (6,000), organizationId, artistId, and assets (up to eight workspace-owned assets returned by the upload endpoint). An artist must be accessible to the caller and belong to the selected organization when one is used.

Read site.id and site.revision, then start automatic generation:

json
{"action":"generate","revision":0}

For Spotify tracks, the background workflow saves metadata, acquires and verifies full audio, saves Music Flamingo lyrics and musical analysis, collects artwork observations and public artist research, compiles a saved brief, selects a concept, and builds and reviews the site. No separate collector calls are required. Audio acquisition requires a Spotify preview for waveform verification and stops if no matching full recording is found. Completed accepted context is reused.

To choose a concept yourself before generation, optionally request pitches:

json
{"action":"concepts","revision":0}

Send this to PATCH /api/sites/{id}, or call MCP propose_site_concepts with id and revision. Optional instruction and contextBriefId apply to pitching too. This can use credits for context collection and a pitch call, but does not create images, site code or change the draft.

The response is { concepts: { status, candidates, reason }, revision }. Status is ready, needs-context, or no-good-concept. Metadata and cover details alone return needs-context; provide usable song analysis or sourced artist context rather than forcing an activity out of the artwork.

In the explicit-selection flow, show the short pitches to the customer and wait for their choice. Each candidate contains name, activity, fanMotivation, songOrArtistConnection, and friendHook. Pass the selected object unchanged as approvedConcept:

json
{
  "action": "generate",
  "revision": 0,
  "approvedConcept": {
    "name": "CUSTOMER_SELECTED_NAME",
    "activity": "SELECTED_ACTIVITY",
    "fanMotivation": "SELECTED_MOTIVATION",
    "songOrArtistConnection": "SELECTED_CONNECTION",
    "friendHook": "SELECTED_HOOK"
  }
}

MCP generate_site accepts the same fields plus id, without action. A customer-authored concept is also accepted. The optional approvedConcept preserves a caller-selected activity. When omitted, the engine selects from its proposed concepts after collecting context.

Generation defaults to background execution and returns a generation token; poll get_site_generation (HTTP action generation) for the result. Set background: false only for a synchronous client that supports a long timeout. If review rejects the concept, the draft is marked needs-review; the system does not silently substitute a new activity. Asset and implementation repairs retain their bounded pass.

Generation failures identify the stopped stage and leave the saved draft unchanged. A 409 means another edit changed the revision; read the site again before retrying.

Poll background generation

Send this JSON body to PATCH /api/sites/{id} using the token returned by the generation request. MCP get_site_generation takes id and token, without action. Polling uses token; publishing and cancellation use generationToken.

json
{"action":"generation","token":"TOKEN_FROM_GENERATION"}

The token is bound to its site and caller and expires. Invalid, expired or mismatched tokens return 404 during token verification; a missing or empty token is invalid input (400). A running job is not proof that a playable build or a published page exists; read the returned status and progress.

Generate from saved Context Engine evidence

Save a single-song creative_direction brief using the Context Engine save_brief action, then pass its snapshot ID when requesting pitches:

json
{
  "action": "concepts",
  "revision": 0,
  "contextBriefId": "SAVED_BRIEF_UUID"
}

Pass the same contextBriefId with the selected approvedConcept to generation. It is available on both MCP propose_site_concepts and generate_site. Without a saved brief, track generation automatically collects and saves context, reusing compatible accepted module results. The brief must belong to the site's workspace and match its Spotify track. Sites rejects unavailable or superseded snapshots before generation. Revisions reuse the draft's selected brief unless a new ID is supplied.

This path reuses accepted song summaries, artwork observations, public artist research and release/artist metadata instead of collecting them again. The creative director and generator receive attribution, coverage and missing-topic information. Supported evidence includes public Spotify metadata/artwork, YouTube-attributed audio analysis and saved public-search research. Private uploads, customer assertions and raw lyrics are excluded. Matching saved machine transcripts inform paraphrased themes but are not forwarded to the site. Partial evidence stays partial.

Generation still uses credits for creative direction, assets, implementation and review. It saves a private draft, never publishes automatically. Snapshot availability is checked again before saving and publishing. Later source withdrawal does not automatically retract an already published site. Internal evidence and source identifiers are excluded from the public site response.

Publish

After reviewing the intended draft or preview, publish through the same PATCH endpoint or MCP publish_site, using the site's current saved revision.

Publishing automatically prepares Spotify fan connection for an active paid workspace. The standard agreement uses the attributed artist's name; the customer does not need a separate setup step. Missing artist attribution or unavailable Spotify configuration stops paid publication with an actionable error. Publication requires an eligible paid workspace. The gate checks the site's owning workspace for an active subscription with a positive-priced item; a trial or Pro entitlement alone does not establish eligibility. Unpaid publication returns HTTP 402 before publishing. Successful paid setup reports fanConnection: "enabled".

For a custom hosted destination, HTTP callers may include returnUrl (an HTTPS URL without credentials or fragment) in the publish body. Otherwise an existing connection destination is preserved, or the configured Sites public origin is used. The Recoup editor supplies its actual published URL. The authenticated fan-connection API remains available for external integrations.

The public page is https://chat.recoupable.dev/s/{id}.

Publish the saved draft

Use the current revision from the saved site (3 in this example):

json
{"action":"publish","revision":3}

Omitting generationToken selects the saved draft, even when a background job has a newer preview. MCP publish_site takes id and revision, without action.

Publish a working preview

To publish a specific working preview from a background generation, pass its token as generationToken and the site's current saved revision:

json
{"action":"publish","revision":3,"generationToken":"TOKEN_FROM_GENERATION"}

MCP publish_site uses the same fields plus id, without action. The token must match the site and caller. The selected build must contain a completed playable build step for the current site/workspace/revision; the whole generation job need not already be completed. A preview that is not ready returns 400, a stale build returns 409, and invalid or expired token access returns 404. A temporary saved-build failure may return 503. Without generationToken, publication uses the saved draft; it does not automatically select the newest background preview.

Entitlement required

An ineligible workspace receives HTTP 402 with an error body, for example:

json
{"error":"Site publishing is unavailable with this workspace's current entitlements. The draft is preserved."}

Keep the draft and explain that publishing is unavailable. Plugin workflows do not offer plans, link to checkout or start a purchase. Resume only after access is independently available and the user requests publication again. This response does not publish the site or create a checkout session.

Unpublish

Send PATCH /api/sites/{id} with the current revision to remove the public snapshot while retaining the draft. MCP unpublish_site takes id and revision.

json
{"action":"unpublish","revision":3}

Agents should publish, unpublish or delete only when explicitly instructed.

Delete and cancel generation

Delete only when explicitly instructed. Send PATCH /api/sites/{id} with action: "delete" and the current revision in its JSON request body (Content-Type: application/json). Include generationToken when cancelling a known in-progress generation. The token must belong to this site and caller; cancellation is attempted before deletion. Omitting it does not request cancellation of every running job.

For example, use the current saved revision and the token returned by a known generation:

json
{"action":"delete","revision":3,"generationToken":"TOKEN_FROM_GENERATION"}

Omit generationToken when no known generation needs cancellation.

Deletion checks workspace access and revision, then returns { "deleted": true, "id": "..." }. A stale revision returns 409; reload before retrying. Unpublish with action: "unpublish" when the draft should remain available.

Uploads

HTTP uploads are multipart forms with a file field and optional organizationId query parameter. Supported formats: JPEG, PNG, WebP, MP3, and WAV, up to 4 MB per file. Images are re-encoded to WebP. Audio is checked for its file signature.

MCP's upload_site_asset accepts name, contentType, base64 file bytes, and optional organizationId. It uses the same validation and storage path as HTTP.

Public endpoints

  • GET /api/sites/public/{id} returns { snapshot: ... } for a published site only. Drafts, account identifiers, and signup data are excluded.
  • POST /api/sites/public/{id}/signup accepts {"email":"fan@example.com","consent":"yes"}. An optional website honeypot must be empty. Duplicate submissions succeed without disclosing list membership. Unpublished sites reject signups.

Public pages and the browser music player render in the chat app. The API provides snapshots, generation, uploads, signup persistence and the fan-connection integration routes listed below.

Route coverage

The lifecycle and public snapshot/signup operations above are documented here in prose; they are not a complete Sites OpenAPI specification. The following existing routes are accounted for as integration surfaces, outside the copyable lifecycle examples. Listing them does not establish live authorization or add an MCP tool.

Authenticated integration routes

HTTPPurpose
GET /api/sites/{id}/fan-connectionRead fan-connection configuration
PUT /api/sites/{id}/fan-connectionConfigure the fan connection through the authenticated integration
GET /api/sites/{id}/fansFan reporting
GET /api/sites/{id}/activityActivity reporting

Browser integration routes

HTTPPurpose
GET /api/sites/apple/configBrowser music configuration
POST /api/sites/public/{id}/activityPublished-site activity collection
GET /api/sites/public/{id}/spotifyShow the fan-connection acceptance page
POST /api/sites/public/{id}/spotifyBegin Spotify authorization after acceptance

These browser flows are not replacements for a private lifecycle request or proof of a completed fan connection. Email signup and Spotify connection are distinct.

Provider callback

GET /api/sites/spotify/callback completes the provider redirect flow. It is intentionally excluded from agent lifecycle examples; do not invoke it as a standalone operation. OPTIONS preflight handlers are also excluded from the operation tables because they serve browser CORS negotiation.

Errors

  • 402: publication requires an eligible paid workspace.

400 invalid input; 401 missing/invalid authentication; 403 workspace or artist access denied; 404 missing/unpublished site; 409 stale revision; 422 Spotify metadata unavailable; 503 temporary operation failure.