Recoup — Add Artist Bring the correct artist into the requested workspace, reusing an existing canonical profile when available. Use the authenticated account and organization already selected through recoup-platform-api-access. Confirm the intended artist and roster action before a live mutation; a name or local folder alone is not identity. Do not use an unknown or throwaway credential. The 8-step onboarding chain (drive from the RECOUP.md checklist) Drive the workflow from the existing checklist. Persist confirmed values and unfinished steps there, without credentials. A checkpoint can des…

\\n'}\"; payload=\"${response%

Recoup — Add Artist Bring the correct artist into the requested workspace, reusing an existing canonical profile when available. Use the authenticated account and organization already selected through recoup-platform-api-access. Confirm the intended artist and roster action before a live mutation; a name or local folder alone is not identity. Do not use an unknown or throwaway credential. The 8-step onboarding chain (drive from the RECOUP.md checklist) Drive the workflow from the existing checklist. Persist confirmed values and unfinished steps there, without credentials. A checkpoint can des…

\\n'*}\"\n case \"$status\" in\n 200) outcome=reused ;;\n 201) outcome=created ;;\n *) printf 'HTTP %s; stop and preserve the checkpoint: %s\\n' \"$status\" \"$payload\" >&2; return 1 ;;\n esac\n artist_id=$(printf '%s' \"$payload\" | jq -er '\n .artist | select(type == \"object\") |\n select(.id == null or .account_id == null or .id == .account_id) |\n (.account_id // .id) | select(type == \"string\" and length > 0)') || {\n echo 'Missing or conflicting returned artist ID. Reconcile; do not create another profile.' >&2\n return 1\n }\n jq -cn --argjson request \"$body\" --arg id \"$artist_id\" --argjson status \"$status\" --arg outcome \"$outcome\" \\\n '{spotify_artist_id:($request.spotify_artist_id // null), organization_id:$request.organization_id,\n artist_account_id:$id, create_http_status:$status, outcome:$outcome}'\n}\n\nonboard_spotify_artist() {\n local body\n body=$(jq -cen --arg name \"$1\" --arg spotify \"$2\" --arg org \"$3\" '\n select(($name | length) > 0 and ($spotify | test(\"^[A-Za-z0-9]{22}$\")) and\n ($org | test(\"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\"))) |\n {name:$name, spotify_artist_id:$spotify, organization_id:$org}') || {\n echo 'Use a verified exact Spotify artist ID and the requested organization UUID.' >&2\n return 1\n }\n roster_post_artist \"$body\"\n}\n\nonboard_manual_artist() {\n local body\n [ \"${3:-}\" = confirmed-manual ] || {\n echo 'Manual creation requires an explicit choice, never a fallback after errors.' >&2\n return 1\n }\n body=$(jq -cen --arg name \"$1\" --arg org \"$2\" '\n select(($name | length) > 0 and\n ($org | test(\"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\"))) |\n {name:$name, organization_id:$org}') || return 1\n roster_post_artist \"$body\"\n}\n\nreconcile_spotify_artist() {\n local spotify=\"$1\" org=\"$2\" saved_id=\"${3:-}\" response status payload matches artist_id\n local token=\"${RECOUP_API_KEY:-${RECOUP_ACCESS_TOKEN:-}}\"\n [ -n \"$token\" ] || return 1\n jq -en --arg spotify \"$spotify\" --arg org \"$org\" '\n ($spotify | test(\"^[A-Za-z0-9]{22}$\")) and\n ($org | test(\"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\"))' >/dev/null || return 1\n response=$(curl -sS --max-time 90 -X GET -H @- \\\n -w '\\n%{http_code}' \"https://api.recoupable.dev/api/artists?org_id=$org\" \\\n \u003c\u003c\u003c \"Authorization: Bearer $token\") || return 1\n status=\"${response##*

Recoup — Add Artist Bring the correct artist into the requested workspace, reusing an existing canonical profile when available. Use the authenticated account and organization already selected through recoup-platform-api-access. Confirm the intended artist and roster action before a live mutation; a name or local folder alone is not identity. Do not use an unknown or throwaway credential. The 8-step onboarding chain (drive from the RECOUP.md checklist) Drive the workflow from the existing checklist. Persist confirmed values and unfinished steps there, without credentials. A checkpoint can des…

\\n'}\"; payload=\"${response%

Recoup — Add Artist Bring the correct artist into the requested workspace, reusing an existing canonical profile when available. Use the authenticated account and organization already selected through recoup-platform-api-access. Confirm the intended artist and roster action before a live mutation; a name or local folder alone is not identity. Do not use an unknown or throwaway credential. The 8-step onboarding chain (drive from the RECOUP.md checklist) Drive the workflow from the existing checklist. Persist confirmed values and unfinished steps there, without credentials. A checkpoint can des…

\\n'*}\"\n [ \"$status\" = 200 ] || { printf 'Roster HTTP %s; stop.\\n' \"$status\" >&2; return 1; }\n matches=$(printf '%s' \"$payload\" | jq -ec --arg spotify \"$spotify\" '\n .artists | arrays | map(select(any(.account_socials[]?;\n (.profile_url // .link // \"\") | test(\"^(?:(?:https?://)?open[.]spotify[.]com/(?:intl-[a-z]{2}/)?artist/|spotify:artist:)\" + $spotify + \"(?:[/?#].*)?$\"))))') || return 1\n case \"$(printf '%s' \"$matches\" | jq 'length')\" in\n 0)\n jq -cn --arg spotify \"$spotify\" --arg org \"$org\" \\\n '{spotify_artist_id:$spotify, organization_id:$org, roster_http_status:200, reconciliation:\"not_found\"}'\n return 2 ;;\n 1) ;;\n *) echo 'Multiple profiles have that identity. Resolve the conflict; do not merge or create.' >&2; return 1 ;;\n esac\n artist_id=$(printf '%s' \"$matches\" | jq -er '.[0] |\n select(.id == null or .account_id == null or .id == .account_id) |\n (.account_id // .id) | select(type == \"string\" and length > 0)') || return 1\n [ -z \"$saved_id\" ] || [ \"$saved_id\" = \"$artist_id\" ] || {\n echo 'The saved artist ID conflicts with the current roster. Stop for review.' >&2; return 1;\n }\n jq -cn --arg spotify \"$spotify\" --arg org \"$org\" --arg id \"$artist_id\" \\\n '{spotify_artist_id:$spotify, organization_id:$org, artist_account_id:$id,\n roster_http_status:200, reconciliation:\"matched\"}'\n}\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4631,"content_sha256":"b53f7659b699948efccfd42555f1bbb46f2322f9490be08fb853db90de409a23"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Recoup — Add Artist","type":"text"}]},{"type":"paragraph","content":[{"text":"Bring the correct artist into the requested workspace, reusing an existing canonical profile when available. Use the authenticated account and organization already selected through recoup-platform-api-access. Confirm the intended artist and roster action before a live mutation; a name or local folder alone is not identity. Do not use an unknown or throwaway ","type":"text"},{"text":"agent+","type":"text","marks":[{"type":"code_inline"}]},{"text":" credential.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"The 8-step onboarding chain (drive from the RECOUP.md checklist)","type":"text"}]},{"type":"paragraph","content":[{"text":"Drive the workflow from the existing ","type":"text"},{"text":"RECOUP.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" checklist. Persist confirmed values and unfinished steps there, without credentials. A checkpoint can describe pending work; do not label an artist created or attached before an API result.","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Resolve identity before creation.","type":"text","marks":[{"type":"strong"}]},{"text":" Verify a supplied Spotify artist URL/ID, or use ","type":"text"},{"text":"GET /api/spotify/search","type":"text","marks":[{"type":"code_inline"}]},{"text":" and inspect the artist and catalog. Capture the exact 22-character, case-sensitive ","type":"text"},{"text":"spotify_artist_id","type":"text","marks":[{"type":"code_inline"}]},{"text":", verified URL and image if present. Ambiguous matches stop for selection. An unavailable match does not authorize creating by name; use the deliberate manual path below only when appropriate. On resume, retain the saved verified identity instead of searching for a new match.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Resolve/attach in the requested organization.","type":"text","marks":[{"type":"strong"}]},{"text":" Before the first write, save the authenticated account ID, requested ","type":"text"},{"text":"organization_id","type":"text","marks":[{"type":"code_inline"}]},{"text":", mode ","type":"text"},{"text":"spotify","type":"text","marks":[{"type":"code_inline"}]},{"text":", verified Spotify ID and pending state. Submit ","type":"text"},{"text":"POST /api/artists {name, spotify_artist_id, organization_id}","type":"text","marks":[{"type":"code_inline"}]},{"text":" with that exact identity and workspace. ","type":"text"},{"text":"200","type":"text","marks":[{"type":"code_inline"}]},{"text":" means an existing canonical artist was reused; ","type":"text"},{"text":"201","type":"text","marks":[{"type":"code_inline"}]},{"text":" means an artist was created. Save the actual HTTP status, outcome and returned artist account ID immediately. ","type":"text"},{"text":"artist.id","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"artist.account_id","type":"text","marks":[{"type":"code_inline"}]},{"text":" identify the same artist account, never an ","type":"text"},{"text":"account_info","type":"text","marks":[{"type":"code_inline"}]},{"text":" row. If both are present and disagree, stop for reconciliation. This operation establishes the Spotify association; do not create by name and then patch Spotify onto it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Continue on the returned artist ID.","type":"text","marks":[{"type":"strong"}]},{"text":" Reconcile the current scoped roster before resuming an interrupted run. If the exact artist is already visible, continue enrichment on that ID. Optional artwork/profile changes use ","type":"text"},{"text":"PATCH /api/artists/{id}","type":"text","marks":[{"type":"code_inline"}]},{"text":" only after identity is known; they are separate from canonical onboarding.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GET /api/research/lookup?spotifyId={verifiedSpotifyId}","type":"text","marks":[{"type":"code_inline"}]},{"text":" → capture the nonempty string ","type":"text"},{"text":"id","type":"text","marks":[{"type":"code_inline"}]},{"text":" as ","type":"text"},{"text":"researchArtistId","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the frontmatter. It is an opaque research ID, separate from the artist account UUID and Spotify ID. Re-resolve older saved research IDs instead of copying them. When an ID is available, read ","type":"text"},{"text":"GET /api/research/profile|career|playlists?id={researchArtistId}","type":"text","marks":[{"type":"code_inline"}]},{"text":". Profile coverage may be partial; career is an activity feed and playlists returns ","type":"text"},{"text":"placements","type":"text","marks":[{"type":"code_inline"}]},{"text":". A successful lookup without an ID skips these reads; record the gap. Run ","type":"text"},{"text":"POST /api/research/web","type":"text","marks":[{"type":"code_inline"}]},{"text":" with a JSON ","type":"text"},{"text":"query","type":"text","marks":[{"type":"code_inline"}]},{"text":" for biography and press. Persist returned evidence and coverage gaps under ","type":"text"},{"text":"## Research","type":"text","marks":[{"type":"code_inline"}]},{"text":"; empty data does not trigger a warm-up job or justify invented facts.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Spotify catalog (","type":"text"},{"text":"topTracks","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"albums","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"album","type":"text","marks":[{"type":"code_inline"}]},{"text":") → write ","type":"text"},{"text":"releases/{slug}/RELEASE.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" per album + ","type":"text"},{"text":"releases/top-tracks.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Web search for socials (ig/tiktok/twitter/youtube).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PATCH /api/artists/{id}","type":"text","marks":[{"type":"code_inline"}]},{"text":" with discovered ","type":"text"},{"text":"profileUrls","type":"text","marks":[{"type":"code_inline"}]},{"text":" (only platforms found).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Synthesize a ","type":"text"},{"text":"## Knowledge base","type":"text","marks":[{"type":"code_inline"}]},{"text":" section into ","type":"text"},{"text":"RECOUP.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Check HTTP status before using each response. On ","type":"text"},{"text":"402","type":"text","marks":[{"type":"code_inline"}]},{"text":", preserve the checkpoint and explain that the feature is unavailable with current account entitlements. Do not surface billing links or initiate purchases. Defer ","type":"text"},{"text":"429","type":"text","marks":[{"type":"code_inline"}]},{"text":" until ","type":"text"},{"text":"Retry-After","type":"text","marks":[{"type":"code_inline"}]},{"text":" permits a later attempt. Authorization, identity-conflict and persistence failures never switch to name-only creation. Do not automatically retry a mutation on a network failure or ","type":"text"},{"text":"5xx","type":"text","marks":[{"type":"code_inline"}]},{"text":": it may have committed. Keep the checkpoint and reconcile first. Read-only enrichment requests may retry network/","type":"text"},{"text":"5xx","type":"text","marks":[{"type":"code_inline"}]},{"text":" failures up to three attempts with a 90-second timeout per request. Keep failed steps unchecked. Uses recoup-platform-api-access for call shapes; connected MCP calls must preserve the same identity and workspace.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Request examples and a deliberate manual path","type":"text"}]},{"type":"paragraph","content":[{"text":"scripts/onboarding_examples.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" ships alongside this skill. The examples require Bash, jq and an already configured ","type":"text"},{"text":"RECOUP_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"RECOUP_ACCESS_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":"; they never extract a host's OAuth token. Prefer a connected tool when available. Sourcing defines functions and makes no request. From this skill's directory:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"source scripts/onboarding_examples.sh\n# These values must come from the verified identity and selected workspace.\nonboard_spotify_artist \"$ARTIST_NAME\" \"$SPOTIFY_ARTIST_ID\" \"$ORGANIZATION_ID\"","type":"text"}]},{"type":"paragraph","content":[{"text":"The one-request example prints the verified identity, organization, returned artist ID, ","type":"text"},{"text":"create_http_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"outcome","type":"text","marks":[{"type":"code_inline"}]},{"text":"; persist those actual values in ","type":"text"},{"text":"RECOUP.md","type":"text","marks":[{"type":"code_inline"}]},{"text":". It performs no enrichment and no automatic retry or fallback. Credentials pass to curl through stdin headers, not process arguments or files.","type":"text"}]},{"type":"paragraph","content":[{"text":"Manual/name-only onboarding is separate.","type":"text","marks":[{"type":"strong"}]},{"text":" If there is no verified Spotify identity and the user deliberately wants a manual artist, record mode ","type":"text"},{"text":"manual","type":"text","marks":[{"type":"code_inline"}]},{"text":" and that unresolved identity first. Submit only ","type":"text"},{"text":"{name, organization_id}","type":"text","marks":[{"type":"code_inline"}]},{"text":". This does not establish a shared identity, link another artist by matching name, or create a professional-roster record. The explicit REST example is:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"onboard_manual_artist \"$ARTIST_NAME\" \"$ORGANIZATION_ID\" confirmed-manual","type":"text"}]},{"type":"paragraph","content":[{"text":"The confirmation argument records the chosen example, not authorization or proof of ownership. Never use it after a failed canonical request. If a manual request has an uncertain outcome, inspect the roster and any returned ID; do not repeat creation because a name search is empty.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resume without duplicating the artist","type":"text"}]},{"type":"paragraph","content":[{"text":"Load the saved account, organization, mode, Spotify identity, returned artist ID when known, and create status/outcome before any new mutation. If the authenticated account or selected organization has changed, stop and re-establish the intended scope instead of moving the run. Read ","type":"text"},{"text":"GET /api/artists?org_id={savedOrganizationId}","type":"text","marks":[{"type":"code_inline"}]},{"text":"; reconcile by exact verified Spotify association and the saved artist account ID, not by display name. Stored profile URLs can omit the scheme; matching also accepts the API's locale URL and Spotify URI forms, while preserving the exact case-sensitive ID. Never lowercase an artist ID to force a match. The read-only example is:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"reconcile_spotify_artist \"$SPOTIFY_ARTIST_ID\" \"$ORGANIZATION_ID\" \"${SAVED_ARTIST_ACCOUNT_ID:-}\"","type":"text"}]},{"type":"paragraph","content":[{"text":"This performs one GET and never POSTs. A unique exact match returns its artist ID and ","type":"text"},{"text":"reconciliation: matched","type":"text","marks":[{"type":"code_inline"}]},{"text":". Preserve the original ","type":"text"},{"text":"create_http_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" and created/reused outcome; a later roster read cannot reconstruct a lost creation response. Resume unfinished enrichment on that ID. A saved-ID mismatch, multiple matches, denial, or incomplete roster response stops for review. Exit 2 means the successful roster read has no exact match, not proof the earlier write never happened. Check access and scope before a further authorized canonical request, using the same saved Spotify ID and organization. Never choose a new identity or fall back to manual creation to clear the error.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Guardrails","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The file is the state","type":"text","marks":[{"type":"strong"}]},{"text":" — tick + persist each step or a fresh turn redoes/skips work.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No placeholder data","type":"text","marks":[{"type":"strong"}]},{"text":" in artist files — real content or leave the section out.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never invent a roster/artist","type":"text","marks":[{"type":"strong"}]},{"text":" — confirm via recoup-platform-api-access first; an empty filesystem is not an empty roster.","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"recoup-roster-add-artist","author":"@skillopedia","source":{"stars":9,"repo_name":"skills","origin_url":"https://github.com/recoupable/skills/blob/HEAD/skills/recoup-roster-add-artist/SKILL.md","repo_owner":"recoupable","body_sha256":"3cf18c90347bb7347f29b9380e48c44347a82c03d0aab0ea0142a1fe57802f8c","cluster_key":"aa931e9264aa78ca2e7b2a09d13bab93536366811787118290d13bd69da3e7f5","clean_bundle":{"format":"clean-skill-bundle-v1","source":"recoupable/skills/skills/recoup-roster-add-artist/SKILL.md","text_bytes":21778,"source_commit":"fc7695cbc5188e7661bf0d3a7ebf314e17b672a0","skills_sh_rank":4475,"attachment_count":5,"text_attachments":5,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/recoup-roster-add-artist/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"business-sales-marketing","category_label":"Business"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"business-sales-marketing","import_tag":"skills-sh-top20k-2026-11","description":"Add or attach an artist to your roster and enrich them — verify Spotify identity, resolve the artist in the requested workspace, then research, catalog, socials and knowledge base. Supports deliberate manual onboarding when Spotify identity is unavailable. Use for \"create/add/onboard an artist\", \"set up [artist]\", \"we just signed [artist]\", or \"new signing\". To see your roster use recoup-roster-list-artists; to work inside an existing artist's files use recoup-roster-manage-artist."}},"renderedAt":1793672012327}

Recoup — Add Artist Bring the correct artist into the requested workspace, reusing an existing canonical profile when available. Use the authenticated account and organization already selected through recoup-platform-api-access. Confirm the intended artist and roster action before a live mutation; a name or local folder alone is not identity. Do not use an unknown or throwaway credential. The 8-step onboarding chain (drive from the RECOUP.md checklist) Drive the workflow from the existing checklist. Persist confirmed values and unfinished steps there, without credentials. A checkpoint can des…