Copyright Review
Copyright Review is the fast triage surface for pending community submissions. It loads every pending row from the shared copyright_reports table and splits it into two tabs by report_type: copyright submissions (candidates to block) and recommendation submissions (candidates to mark safe). Unlike Copyright Reports, it carries no status filter and no history — only what still needs a decision.
Where to find it
Admin sidebar → Copyright Review (/copyright-review).
Quick start
- Open Admin → Copyright Review.
- Pick a tab: Blocked Candidates or Safe Candidates. Each tab header carries its own count.
- For each candidate, read the song, artist, confirmation count, and category.
- Click Approve to accept the submission and move the track into the blocked/safe catalog.
- Click Dismiss to reject the candidate without changing catalog state.
Detailed walkthrough
Tabs
The page renders a tab bar, not side-by-side columns. Only one list is visible at a time:
- Blocked Candidates (N) — pending rows with
report_type = copyright. Approving marks the track copyright-blocked globally. - Safe Candidates (N) — pending rows with
report_type = recommendation. Approving marks the track copyright-safe globally.
The split is by report_type, not by which vote count is larger. Both tabs are populated from a single status=pending fetch and filtered client-side.
Candidate cards
Each candidate renders as a card showing:
- Song title, with an external-link icon to
open.spotify.com/track/…when a Spotify track ID is present. - Artist.
\{confirmation_count\} copyright votes— one confirmation counter per row, whichever tab it sits in.- The submission category, when set.
There is no separate "safe votes" tally on a row.
Row actions
- Approve — calls
POST /api/copyright/reports/\{reportId\}/approveand refetches, dropping the row from the queue. - Dismiss — calls
POST /api/copyright/reports/\{reportId\}/dismissand refetches.
Both proxy routes execute the same GraphQL mutations as Copyright Reports / Copyright Recommendations; the Review page is a pending-only view over the same dataset.
Empty state
When the active tab has no candidates: "No candidates to review."
Common scenarios
- High-traffic triage session — work through both tabs, approving clear majorities and dismissing ambiguous ones.
- Policy change — review the Safe Candidates tab to validate community consensus before approving a new genre's picks.
- Suspected brigading — if a single track in Blocked Candidates has an absurd confirmation count coming from fresh accounts, dismiss and investigate via Abuse Reports.
- Cross-check with external copyright sources — before approving a Blocked candidate, confirm the track is actually copyrighted (Spotify's own "Content ID Block" metadata is a decent heuristic).
Permissions
| Action | Permission |
|---|---|
| View candidates | copyright:read |
| Approve / dismiss | copyright:moderate |
copyright:moderate is a distinct permission from copyright:edit: it governs the global, cross-account blocked/safe lists, while copyright:edit only touches an account's own lists. Dashboard entry requires admin:access. System admins implicitly hold both.
API
This surface is GraphQL-first. There are no /v1/copyright/reports* REST endpoints — the paths below are the admin app's own Next.js proxy routes, which execute the GraphQL operations in the middle column.
| UI action | GraphQL | Admin proxy route |
|---|---|---|
| List pending candidates | adminReports(status: "pending") | GET /api/copyright/reports?status=pending |
| Approve a candidate | approveReport(id, addToPlaylistId) | POST /api/copyright/reports/\{id\}/approve |
| Dismiss a candidate | dismissReport(id) | POST /api/copyright/reports/\{id\}/dismiss |
Related backend surfaces, callable directly:
| Purpose | GraphQL | REST |
|---|---|---|
| User-side vote submission | confirmCopyrightReport | POST /v1/copyright/vote |
| Raw vote candidates | voteCandidates | GET /v1/copyright/vote-candidates |
| Approve a raw vote candidate | — | POST /v1/copyright/vote-candidates/\{track_id\}/approve |
| Dismiss a raw vote candidate | — | POST /v1/copyright/vote-candidates/\{track_id\}/dismiss |
The vote-candidates REST endpoints are gated on copyright:moderate as well.
Tips & gotchas
- Approve is not reversible from the UI. Double-check the confirmation count and confirm the correct tab (Blocked vs Safe) before clicking.
- The page is community-moderation at its rawest — brigading and sockpuppet voting are possible. Spot-check confirmation counts via the underlying reports query.
- Approving a Safe candidate here is exactly the same mutation as approving a recommendation in Copyright Recommendations — but without the optional playlist dropdown, so the track lands in the safe catalog and no curated playlist.
- This page surfaces only
pendingcandidates. For historical review use the Copyright Reports page with a status filter.
Related
- Copyright Reports — granular per-report view
- Copyright Recommendations — positive-polarity per-report view
- Curated Playlists — where approved safe tracks live
- Abuse Reports — meta-reports against the voting system