ChapterTwo API
ChapterTwo is a music royalty management platform. This page is the canonical reference for the public tRPC API hosted at https://api.prod.chaptertwo.com. Every endpoint, the Cube analytics schema, and an agent-integration guide live on this single page so AI assistants and humans can cross-reference the entire surface area without navigation.
| Surface | Count |
|---|---|
| Total procedures | 142 |
| Queries (GET) | 67 |
| Mutations (POST) | 75 |
| Namespaces | 25 |
| Cube analytics views | 1 |
- —/llms.txt (alias /llm.txt) — the canonical reference. Agent-first ordering: a 5-line TL;DR, then setup, the three Cube rules, a sandbox Python helper, the agent guide with worked examples and explicit anti-patterns, then the
analytics_full_viewschema, and finally the per-procedure dump. - —/schemas.json — full Zod-derived JSON Schema for every procedure (regexes, enums, defaults). Use when you need strict input validation.
- —/cube-schema.json — full Cube view schema (measures, dimensions, time dimensions). Same data as
cube.metabut unauthenticated. - —
https://mcp.chaptertwo.com/api/mcp— for MCP-aware clients (Claude Desktop, Cursor).
Quick start
The fastest path is the CLI. It handles OAuth, organization scoping, token refresh, and output formatting so you can call any procedure with one command.
bash# 1. Install npm install -g @chapter-two-music/cli # 2. Authenticate (browser) c2 auth login # 3. Verify and call your first endpoint c2 whoami c2 rightsholder.list # 4. Use the access token from scripts TOKEN=$(c2 auth token) ORG=$(c2 whoami --format json | jq -r '.organizationId') curl -H "Authorization: Bearer $TOKEN" \ "https://api.prod.chaptertwo.com/trpc/health"
Or use the typed SDK from any Node/TypeScript project:
typescriptimport { createClient } from '@chapter-two-music/sdk'; const client = createClient({ apiUrl: 'https://api.prod.chaptertwo.com', token: process.env.C2_TOKEN!, // `c2 auth token` or OAuth access token orgId: process.env.C2_ORG_ID!, // your organization UUID }); const { data, pagination } = await client.query('rightsholder.list', { pagination: { page: 1, limit: 25 }, });
Authentication
All procedures except health require authentication. Two credential types are accepted: OAuth 2.0 access tokens (for users) and API keys (for service-to-service integrations).
OAuth 2.0 with PKCE
User sessions use OAuth 2.0 authorization-code flow with PKCE. The CLI and MCP clients run the flow automatically. For custom integrations, use the discovery endpoint to bind a client:
| Endpoint | URL |
|---|---|
| Discovery | https://mcp.chaptertwo.com/.well-known/oauth-authorization-server |
| Authorize | https://mcp.chaptertwo.com/api/oauth/authorize |
| Token | https://mcp.chaptertwo.com/api/oauth/token |
Send the access token as Authorization: Bearer <token> on every request.
API keys
For long-running automated workflows, request an API key by emailing support@chaptertwo.com. API keys carry an explicit scope of organization IDs and route patterns.
bashcurl -H "x-api-key: $C2_API_KEY" \ -H "x-organization-id: $ORG_ID" \ "https://api.prod.chaptertwo.com/trpc/health"
x-organization-id as a header AND repeat organizationId in the request body. With OAuth you only need the body field.Token lifecycle
| Token | Lifetime | Notes |
|---|---|---|
| OAuth access token | 1 hour | Bearer token. Refresh before expiry using the refresh token. |
| OAuth refresh token | 30 days | Single-use — every refresh rotates it. After 30 days re-authenticate. |
| API key | Until rotated | Long-lived. Rotate via apiKey.update / apiKey.delete. |
bash# Refresh the access token curl -X POST https://mcp.chaptertwo.com/api/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=refresh_token&refresh_token=$REFRESH_TOKEN"
Transport
Base URLs
| Service | URL |
|---|---|
| API (production) | https://api.prod.chaptertwo.com |
| MCP server | https://mcp.chaptertwo.com/api/mcp |
| OAuth discovery | https://mcp.chaptertwo.com/.well-known/oauth-authorization-server |
tRPC HTTP batch protocol
The API speaks tRPC over HTTP. Procedures are addressed at /trpc/<namespace>.<procedure>. There are two modes — always prefer batch mode ?batch=1 (the SDK uses this internally):
- —Batch (recommended):
?batch=1. Input is keyed by index{"0": ...}; the response is[{"result":{"data": ...}}]. - —Single: no
batchparam. Input is sent raw and the response is{"result":{"data": ...}}. - —Queries use
GETwith input URL-encoded in?input=…. - —Mutations use
POSTwith input as the JSON body.
bash# Query (GET) — note the URL-encoded JSON curl -H "Authorization: Bearer $TOKEN" \ "https://api.prod.chaptertwo.com/trpc/rightsholder.list?batch=1&input=%7B%220%22%3A%7B%22organizationId%22%3A%22$ORG%22%2C%22pagination%22%3A%7B%22page%22%3A1%2C%22limit%22%3A10%7D%7D%7D" # Mutation (POST) curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"0":{"organizationId":"'$ORG'","name":"New Artist"}}' \ "https://api.prod.chaptertwo.com/trpc/rightsholder.create?batch=1"
Organization scoping
Almost every procedure is scoped to a single organization. The server enforces that the authenticated principal belongs to the requested org via the organization-context-middleware. Always include organizationId (UUID) in the procedure input. The two exceptions — user.currentUser and exchangeRates.get — do not require it.
Pagination
List queries take a pagination object and return a uniform envelope. The default limit is 300 when no pagination params are provided, or 200 when only page is provided. Maximum is 2000.
typescript// Input { pagination: { page: 1, // 1-indexed limit: 25, // 1..2000 sortBy?: string; sortOrder?: 'asc' | 'desc'; } } // Output { data: T[], pagination: { page: number, limit: number, total: number, totalPages: number, hasNext: boolean, hasPrev: boolean, } }
upload.statements uses { items, totalFiles, pagination }; user.currentUser and exchangeRates.get return flat shapes.Errors
Every error follows the tRPC error envelope:
json[{ "error": { "message": "Rightsholder not found", "code": -32004, "data": { "code": "NOT_FOUND", "errorCode": "RIGHTSHOLDER_NOT_FOUND", "httpStatus": 404, "path": "rightsholder.getById" } } }]
| data.code | HTTP | Meaning |
|---|---|---|
| UNAUTHORIZED | 401 | Missing or invalid token / API key |
| FORBIDDEN | 403 | Authenticated but lacks the required role |
| NOT_FOUND | 404 | Resource does not exist or not visible to this org |
| BAD_REQUEST | 400 | Input failed Zod validation |
| CONFLICT | 409 | Unique-constraint violation (e.g. rightsholder name) |
| TIMEOUT | 408 | Cube query exceeded the long-poll window — retry via async |
| INTERNAL_SERVER_ERROR | 500 | Unexpected error — safe to retry once |
API reference
All 142 procedures, grouped by namespace. Each procedure shows its HTTP method, scope, summary, full path, and input schema. POST = mutation, GET = query. cli means the procedure is exposed by the public CLI; admin means it's gated to ChapterTwo admin tooling.
activity (3)
activity.byEntityGETList activity events for a single subject
GET /trpc/activity.byEntity?batch=1&input=…
organizationId* | string (uuid) | |
subjectType* | "rightsholder" | "file" | "upload" | "export" | "product" | "mapping" | "lens" | "chat" | "organization" | "user" | |
subjectId* | string | |
cursor | string | |
limit* | integer | (default 50) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
activity.feedGETList activity events for the organisation
GET /trpc/activity.feed?batch=1&input=…
activity.trackVisitPOSTRecord that the current user opened a subject
POST /trpc/activity.trackVisit?batch=1
organizationId* | string (uuid) | |
subjectType* | "rightsholder" | "file" | "upload" | "export" | "product" | "mapping" | "lens" | "chat" | "organization" | "user" | |
subjectId* | string | |
subjectLabel | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
clustering (6)
Track/product clustering and grouping inside a processing block.
clustering.getClusterGETcliGet a single cluster with its members and products
GET /trpc/clustering.getCluster?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
clusterId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
clustering.getClustersListGETcliGet a summary list of clusters for a block
GET /trpc/clustering.getClustersList?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
clustering.getTitleClusteringGETcliGet title clustering data for a block
GET /trpc/clustering.getTitleClustering?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
pagination | object | (default {"page":1,"limit":50}) |
pagination.page | integer | (default 1) |
pagination.limit | integer | (default 20) |
sortField* | "entries" | "titleDisplay" | "similarity" | "earnings" | (default "entries") |
sortDirection* | "asc" | "desc" | (default "desc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
clustering.getTopClustersGETcliGet top clusters for a block
GET /trpc/clustering.getTopClusters?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
wantedMusicalDetails | string[] | |
wantedClusterIds | string (uuid)[] | |
limit | integer | (default 300) |
threshold | number | (default 0.001) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
clustering.getTopMdsFromBlockGETcliGet top musical details with earnings from a block
GET /trpc/clustering.getTopMdsFromBlock?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
limit | integer | (default 300) |
minEarningsPercentage | number |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
clustering.updateClusteringPOSTcliApply clustering changes to reassign musical details between clusters
POST /trpc/clustering.updateClustering?batch=1
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
changes* | object[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube (8)
Analytics queries and SQL passthrough against the Cube semantic layer.
cube.getSessionGETcliGet or create an org-scoped Cube Cloud embed session
GET /trpc/cube.getSession?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.metaGETcliFetch Cube metadata (views, measures, dimensions)
GET /trpc/cube.meta?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.queryPOSTcliExecute a Cube REST API query scoped to the organization
POST /trpc/cube.query?batch=1
organizationId* | string (uuid) | |
query* | object | |
query.measures | string[] | |
query.dimensions | string[] | |
query.filters | object[] | |
query.timeDimensions | object[] | |
query.order | object | |
query.limit | integer | |
query.total* | boolean | (default false) |
usePreagg | boolean |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.queryAsyncPOSTcliSubmit a Cube query and return immediately with status
POST /trpc/cube.queryAsync?batch=1
organizationId* | string (uuid) | |
query* | object | |
query.measures | string[] | |
query.dimensions | string[] | |
query.filters | object[] | |
query.timeDimensions | object[] | |
query.order | object | |
query.limit | integer | |
query.total* | boolean | (default false) |
usePreagg | boolean | |
noCache | boolean |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.queryPollPOSTcliPoll for a previously submitted Cube query result
POST /trpc/cube.queryPoll?batch=1
organizationId* | string (uuid) | |
query* | object | |
query.measures | string[] | |
query.dimensions | string[] | |
query.filters | object[] | |
query.timeDimensions | object[] | |
query.order | object | |
query.limit | integer | |
query.total* | boolean | (default false) |
usePreagg | boolean |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.sqlQueryPOSTExecute a SQL query against the Cube SQL API
POST /trpc/cube.sqlQuery?batch=1
organizationId* | string (uuid) | |
sql* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.sqlQueryAsyncPOSTSubmit a Cube SQL query and return immediately with status
POST /trpc/cube.sqlQueryAsync?batch=1
organizationId* | string (uuid) | |
sql* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
cube.sqlQueryPollPOSTPoll for a previously submitted Cube SQL query result
POST /trpc/cube.sqlQueryPoll?batch=1
organizationId* | string (uuid) | |
sql* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
dataFreshness (4)
Inspect ETL freshness and ingestion progress.
dataFreshness.checkEarningsGETCheck earnings data freshness for selected rightsholders
GET /trpc/dataFreshness.checkEarnings?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
dataFreshness.checkProductsGETCheck products data freshness for selected rightsholders
GET /trpc/dataFreshness.checkProducts?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
dataFreshness.ingestionStatsGETFull aggregated preview stats from DynamoDB for selected rightsholders
GET /trpc/dataFreshness.ingestionStats?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
dataFreshness.isReadyGETReadiness check using DynamoDB status categories + RDS warehoused status
GET /trpc/dataFreshness.isReady?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
distributor (5)
Statement distributor formats (e.g. Kobalt, MLC, Spotify-direct).
distributor.createPOSTadminCreate a new distributor
POST /trpc/distributor.create?batch=1
organizationId* | string (uuid) | |
name* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
distributor.deleteByIdPOSTDelete a distributor by ID
POST /trpc/distributor.deleteById?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
distributor.getByIdGETadminGet a distributor by ID
GET /trpc/distributor.getById?batch=1&input=…
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
distributor.listGETadminList distributors with their mappings
GET /trpc/distributor.list?batch=1&input=…
organizationId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
distributor.updateByIdPOSTadminUpdate a distributor by ID
POST /trpc/distributor.updateById?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) | |
name | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
exchangeRates (1)
Foreign exchange rates used to convert statement currencies to USD.
exchangeRates.getGETcliGet exchange rates for a given date
GET /trpc/exchangeRates.get?batch=1&input=…
fetching (6)
Music enrichment / fetching queue status.
fetching.getFetchingCoverageByRightsholdersGETGET /trpc/fetching.getFetchingCoverageByRightsholders?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fetching.inQueueCountGETcliGet the number of musical details currently in the processing queue
GET /trpc/fetching.inQueueCount?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fetching.listGETadminList blocks with musical detail fetching status
GET /trpc/fetching.list?batch=1&input=…
organizationId | string (uuid) | |
search | string | |
pagination* | object | (default {"page":1,"limit":10}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 10) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fetching.refetchByBlockPOSTRe-fetch terminal musical details for a block
POST /trpc/fetching.refetchByBlock?batch=1
organizationId* | string (uuid) | |
blockId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fetching.reprocessSkippedByBlockPOSTReprocess skipped musical details for a block
POST /trpc/fetching.reprocessSkippedByBlock?batch=1
organizationId* | string (uuid) | |
blockId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fetching.stopFetchingByBlockPOSTStop fetching for a block by skipping pending items
POST /trpc/fetching.stopFetchingByBlock?batch=1
organizationId* | string (uuid) | |
blockId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
file (3)
Per-organization file management.
file.deleteFilesByIdsPOSTcliDelete specific files from an upload by their IDs
POST /trpc/file.deleteFilesByIds?batch=1
organizationId* | string (uuid) | |
fileIds* | string[] | |
uploadId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
file.getFilesDeleteInfoGETGet deletion impact info for a set of files (blocks and catalogs affected)
GET /trpc/file.getFilesDeleteInfo?batch=1&input=…
organizationId* | string (uuid) | |
fileIds* | string[] | |
uploadId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
file.listGETcliList files for the current organization with optional filters
GET /trpc/file.list?batch=1&input=…
organizationId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
filters | object | |
filters.fileId | string[] | |
filters.dateStart | string | |
filters.dateEnd | string | |
filters.rightsholderId | string (uuid) | |
filters.distributorId | string (uuid) | |
filters.customName | string | |
filters.name | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fileStorage (2)
Statement file storage — presigned downloads and CSV samples.
fileStorage.downloadStatementFilePOSTcliGenerate a presigned URL to download a statement file from S3
POST /trpc/fileStorage.downloadStatementFile?batch=1
organizationId* | string (uuid) | |
type* | "normalized" | "trimmed" | "original" | "single" | "removed" | "standardized" | "original-excel" | |
file* | string | |
fileName | string | |
originalExcelFile | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
fileStorage.getSampleGETcliGet a paginated sample of rows from a statement CSV file
GET /trpc/fileStorage.getSample?batch=1&input=…
organizationId* | string (uuid) | |
type* | "normalized" | "trimmed" | "original" | "single" | "removed" | "standardized" | |
file* | string | |
startRow* | integer | (default 0) |
endRow* | integer | (default 200) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
health (1)
Service health check.
healthGETGET /trpc/health?batch=1&input=…
insights (2)
Default FX-rate dates and rates for analytics.
insights.getDefaultFxDateGETGET /trpc/insights.getDefaultFxDate?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
insights.getDefaultFxRatesGETGET /trpc/insights.getDefaultFxRates?batch=1&input=…
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens (9)
Saved analytics lenses (charts/dashboards).
lens.createPOSTPOST /trpc/lens.create?batch=1
organizationId* | string (uuid) | |
name* | string | |
description | string | |
scope* | "personal" | "organization" | (default "organization") |
tags* | string[] | (default []) |
kind* | "query" | "pivot" | "funnel" | "flow" | "retention" | "drill_down" | "valuation" | |
viewName | unknown | |
payload* | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.deletePOSTPOST /trpc/lens.delete?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.duplicatePOSTPOST /trpc/lens.duplicate?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) | |
name | string | |
scope | "personal" | "organization" |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.getGETGET /trpc/lens.get?batch=1&input=…
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.listGETGET /trpc/lens.list?batch=1&input=…
organizationId* | string (uuid) | |
kind | "query" | "pivot" | "funnel" | "flow" | "retention" | "drill_down" | "valuation" | |
viewName | string | |
scope | "personal" | "organization" | |
tag | string | |
ownerUserId | string (uuid) | |
search | string | |
pinnedOnly | boolean | |
recentOnly | boolean | |
sort | "updated_desc" | "created_desc" | "name_asc" | "last_opened_desc" | |
cursor | unknown | |
limit* | integer | (default 50) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.listTagsGETGET /trpc/lens.listTags?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.recordOpenedPOSTPOST /trpc/lens.recordOpened?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.setPinnedPOSTPOST /trpc/lens.setPinned?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) | |
pinned* | boolean |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lens.updatePOSTPOST /trpc/lens.update?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) | |
name | string | |
description | unknown | |
scope | "personal" | "organization" | |
tags | string[] | |
payload | unknown | |
viewName | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType (8)
Sales-type lookup tables and AI resolvers (admin).
lookupSalesType.batchResolveWithAIPOSTadminBatch resolve multiple sales type strings using AI (max 10)
POST /trpc/lookupSalesType.batchResolveWithAI?batch=1
salesTypes* | string[] | |
saveToDb* | boolean | (default false) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.createPOSTadminCreate or upsert a sales type lookup entry
POST /trpc/lookupSalesType.create?batch=1
original* | string | |
cleansed* | string | |
status | string | |
reason | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.deletePOSTadminDelete a sales type lookup entry by id
POST /trpc/lookupSalesType.delete?batch=1
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.getSalesTypeLookupGETadminGet a single sales type lookup entry by id or original value
GET /trpc/lookupSalesType.getSalesTypeLookup?batch=1&input=…
id | string (uuid) | |
original | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.listGETadminList sales type lookup entries with optional search and status filter
GET /trpc/lookupSalesType.list?batch=1&input=…
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
search | string | |
status | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.missingSalesTypesGETadminList sales types that are present in data but have no lookup mapping
GET /trpc/lookupSalesType.missingSalesTypes?batch=1&input=…
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.resolveWithAIPOSTadminResolve a sales type string using AI classification
POST /trpc/lookupSalesType.resolveWithAI?batch=1
original* | string | |
saveToDb* | boolean | (default false) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSalesType.updatePOSTadminUpdate a sales type lookup entry by id
POST /trpc/lookupSalesType.update?batch=1
id* | string (uuid) | |
cleansed | string | |
primarySalesType | unknown | |
secondarySalesType | unknown | |
status | string | |
reason | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource (12)
Source lookup tables and AI resolvers (admin).
lookupSource.batchResolveWithAIPOSTadminBatch resolve multiple source strings using AI (max 10)
POST /trpc/lookupSource.batchResolveWithAI?batch=1
sources* | string[] | |
saveToDb* | boolean | (default false) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.createPOSTadminCreate or upsert a source lookup entry
POST /trpc/lookupSource.create?batch=1
original* | string | |
collectionSource | unknown | |
earningsSource | unknown | |
earningsCategory | unknown | |
earningsParentCompany | unknown | |
publisherSource | unknown | |
status* | string | |
reason | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.deletePOSTadminDelete a source lookup entry by id
POST /trpc/lookupSource.delete?batch=1
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.getSourceLookupGETadminGet a single source lookup entry by id or original value
GET /trpc/lookupSource.getSourceLookup?batch=1&input=…
id | string (uuid) | |
original | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.listGETadminList source lookup entries with optional search and status filter
GET /trpc/lookupSource.list?batch=1&input=…
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
search | string | |
status | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.missingSourcesGETadminList sources that are present in data but have no lookup mapping
GET /trpc/lookupSource.missingSources?batch=1&input=…
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.ragCreatePOSTadminAdd a new RAG source vector with auto-generated embedding
POST /trpc/lookupSource.ragCreate?batch=1
name* | string | |
category* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.ragDeletePOSTadminDelete a RAG source vector by id
POST /trpc/lookupSource.ragDelete?batch=1
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.ragListGETadminList all RAG source vectors used for AI resolution
GET /trpc/lookupSource.ragList?batch=1&input=…
lookupSource.ragUpdatePOSTadminUpdate a RAG source vector and regenerate its embedding
POST /trpc/lookupSource.ragUpdate?batch=1
id* | string (uuid) | |
name* | string | |
category* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
lookupSource.resolveWithAIPOSTadminResolve a source string using AI classification
POST /trpc/lookupSource.resolveWithAI?batch=1
lookupSource.updatePOSTadminUpdate a source lookup entry by id
POST /trpc/lookupSource.update?batch=1
id* | string (uuid) | |
collectionSource | unknown | |
earningsSource | unknown | |
earningsCategory | unknown | |
earningsParentCompany | unknown | |
publisherSource | unknown | |
status | string | |
reason | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping (11)
Distributor → standard column mappings (admin).
mapping.batchUpdatePOSTadminBatch update multiple mappings in a single transaction
POST /trpc/mapping.batchUpdate?batch=1
organizationId* | string (uuid) | |
updates* | unknown[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.createPOSTadminCreate a new column mapping
POST /trpc/mapping.create?batch=1
organizationId* | string (uuid) | |
name | string | |
headers* | string[] | |
fields | object | |
fields.stmtYear | object | |
fields.stmtPeriod | object | |
fields.stmtCadence | object | |
fields.currency | object | |
fields.royaltyEarnings | object | |
fields.grossRoyaltyEarnings | object | |
fields.salesPeriodStart | object | |
fields.salesPeriodEnd | object | |
fields.salesType | object | |
fields.subSalesType | object | |
fields.source | object | |
fields.subSource | object | |
fields.units | object | |
fields.country | object | |
fields.typeOfRight | object | |
fields.accountNumber | object | |
fields.accountName | object | |
fields.legalVendor | object | |
fields.territoryDeduction | object | |
fields.taxDeduction | object | |
fields.royaltyRate | object | |
fields.otherAdjustments | object | |
fields.rawTitle | object | |
fields.rawArtist | object | |
fields.rawReleaseTitle | object | |
fields.rawReleaseArtist | object | |
fields.composers | object | |
fields.isrc | object | |
fields.upc | object | |
fields.iswc | object | |
fields.otherIdentifier | object | |
fields.payor | object | |
headerHash* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.deleteByIdPOSTDelete a mapping by id (admin/owner only)
POST /trpc/mapping.deleteById?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.getByDistributorGETadminGet mappings associated with a specific distributor
GET /trpc/mapping.getByDistributor?batch=1&input=…
organizationId* | string (uuid) | |
distributorId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.getByIdGETadminGet a mapping by id (deprecated: use getMapping instead)
GET /trpc/mapping.getById?batch=1&input=…
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.getMappingGETadminGet a mapping by id or header hash
GET /trpc/mapping.getMapping?batch=1&input=…
organizationId* | string (uuid) | |
id | string (uuid) | |
headerHash | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.listGETadminList mappings for the current organization
GET /trpc/mapping.list?batch=1&input=…
organizationId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
ids | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.processSamplePOSTProcess a sample of rows using a field mapping to preview transformations
POST /trpc/mapping.processSample?batch=1
organizationId* | string (uuid) | |
statementId* | string | |
startRow* | integer | (default 0) |
endRow* | integer | (default 5) |
mappingId* | string (uuid) | |
mapping* | object | |
mapping.stmtYear* | object | |
mapping.stmtPeriod* | object | |
mapping.stmtCadence* | object | |
mapping.currency* | object | |
mapping.royaltyEarnings* | object | |
mapping.grossRoyaltyEarnings | object | |
mapping.salesPeriodStart | object | |
mapping.salesPeriodEnd | object | |
mapping.salesType | object | |
mapping.subSalesType | object | |
mapping.source | object | |
mapping.subSource | object | |
mapping.units | object | |
mapping.country | object | |
mapping.typeOfRight | object | |
mapping.accountNumber | object | |
mapping.accountName | object | |
mapping.legalVendor | object | |
mapping.territoryDeduction | object | |
mapping.taxDeduction | object | |
mapping.royaltyRate | object | |
mapping.otherAdjustments | object | |
mapping.rawTitle | object | |
mapping.rawArtist | object | |
mapping.rawReleaseTitle | object | |
mapping.rawReleaseArtist | object | |
mapping.composers | object | |
mapping.isrc | object | |
mapping.upc | object | |
mapping.iswc | object | |
mapping.otherIdentifier | object | |
mapping.payor | object | |
variables | object | |
fileName* | string | (default "") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.redrivePreviewGETadminPreview which files and organizations would be affected by a mapping redrive
GET /trpc/mapping.redrivePreview?batch=1&input=…
organizationId* | string (uuid) | |
mappingId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.revision.listByMappingGETadminGET /trpc/mapping.revision.listByMapping?batch=1&input=…
organizationId* | string (uuid) | |
mappingId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
mapping.updateByIdPOSTadminUpdate a mapping by id, optionally reassigning distributors
POST /trpc/mapping.updateById?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) | |
distributorId | string (uuid)[] | |
verify | boolean | |
redriveFileIds | string[] | |
name | string | |
headers | string[] | |
fields | object | |
fields.stmtYear | object | |
fields.stmtPeriod | object | |
fields.stmtCadence | object | |
fields.currency | object | |
fields.royaltyEarnings | object | |
fields.grossRoyaltyEarnings | object | |
fields.salesPeriodStart | object | |
fields.salesPeriodEnd | object | |
fields.salesType | object | |
fields.subSalesType | object | |
fields.source | object | |
fields.subSource | object | |
fields.units | object | |
fields.country | object | |
fields.typeOfRight | object | |
fields.accountNumber | object | |
fields.accountName | object | |
fields.legalVendor | object | |
fields.territoryDeduction | object | |
fields.taxDeduction | object | |
fields.royaltyRate | object | |
fields.otherAdjustments | object | |
fields.rawTitle | object | |
fields.rawArtist | object | |
fields.rawReleaseTitle | object | |
fields.rawReleaseArtist | object | |
fields.composers | object | |
fields.isrc | object | |
fields.upc | object | |
fields.iswc | object | |
fields.otherIdentifier | object | |
fields.payor | object | |
fileCategories | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
market (3)
Market data — quotes, treasury yields, history.
market.getHistoryGETGET /trpc/market.getHistory?batch=1&input=…
organizationId* | string (uuid) | |
ticker* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
market.getQuotesGETGET /trpc/market.getQuotes?batch=1&input=…
organizationId* | string (uuid) | |
symbols* | string[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
market.getTreasuryYieldsGETGET /trpc/market.getTreasuryYields?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails (7)
Musical-details lookup, override, and execution (admin).
musicalDetails.bulkOverridePOSTadminBulk override musical detail results
POST /trpc/musicalDetails.bulkOverride?batch=1
organizationId* | string (uuid) | |
blockId | string (uuid) | |
overrides* | object[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails.executeQueryGETadminExecute a filter query on musical details
GET /trpc/musicalDetails.executeQuery?batch=1&input=…
organizationId* | string (uuid) | |
query* | unknown | |
hasBlockRule* | boolean | (default false) |
hasRightsholderRule* | boolean | (default false) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails.getDistinctTargetProvidersGETadminGet distinct target providers for musical details
GET /trpc/musicalDetails.getDistinctTargetProviders?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails.getDistinctTargetTypesGETadminGet distinct target types for musical details
GET /trpc/musicalDetails.getDistinctTargetTypes?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails.getMusicalDetailsGETadminGet paginated musical details with filter
GET /trpc/musicalDetails.getMusicalDetails?batch=1&input=…
organizationId* | string (uuid) | |
query* | unknown | |
hasBlockRule* | boolean | (default false) |
hasRightsholderRule* | boolean | (default false) |
includeTrail* | boolean | (default false) |
pagination* | object | (default {"page":1,"pageSize":100}) |
pagination.page* | integer | (default 1) |
pagination.pageSize* | integer | (default 100) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails.ignoreTitlesPOSTadminMark musical detail titles as ignored
POST /trpc/musicalDetails.ignoreTitles?batch=1
organizationId* | string (uuid) | |
titles* | string[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicalDetails.updateResultsPOSTadminUpdate musical detail results with override values
POST /trpc/musicalDetails.updateResults?batch=1
organizationId* | string (uuid) | |
resultIds* | string (uuid)[] | |
overrideValues* | object | |
overrideValues.title* | string | |
overrideValues.artist* | string | |
overrideValues.releaseDate* | string | |
overrideValues.imageUrl | string | |
overrideValues.genres | object[] | |
overrideValues.albums | object[] | |
overrideValues.songWriters | object[] | |
overrideValues.fetchedComposers | object[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
musicEnrichment (1)
Track and rightsholder music-enrichment metadata (admin).
musicEnrichment.queryGETadminExecute a Chartmetric data query for music enrichment
GET /trpc/musicEnrichment.query?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization (16)
Organization administration — users, invitations, settings.
organization.addUserPOSTInvite a user to the organization
POST /trpc/organization.addUser?batch=1
organization.createPOSTCreate a new organization
POST /trpc/organization.create?batch=1
organization.deletePOSTSoft delete the organization
POST /trpc/organization.delete?batch=1
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.getByIdGETGet the current organization by ID
GET /trpc/organization.getById?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.getInvitationLinkPOSTGet the acceptance link for a pending invitation
POST /trpc/organization.getInvitationLink?batch=1
organizationId* | string (uuid) | |
invitationId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.listGETadminList organizations for the current user
GET /trpc/organization.list?batch=1&input=…
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.listInvitationsGETList pending invitations in the organization
GET /trpc/organization.listInvitations?batch=1&input=…
organizationId* | string (uuid) | |
status* | "pending" | "accepted" | "revoked" | "expired"[] | (default ["pending"]) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.listRightsholderMdPrioritiesGETadminList rightsholder MD fetch priorities for the organization
GET /trpc/organization.listRightsholderMdPriorities?batch=1&input=…
organizationId* | string (uuid) | |
search | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.listUsersGETadminList users in the organization
GET /trpc/organization.listUsers?batch=1&input=…
organizationId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.removeUserPOSTRemove a user from the organization
POST /trpc/organization.removeUser?batch=1
organizationId* | string (uuid) | |
userId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.resendInvitationPOSTResend a pending invitation by revoking and re-creating it
POST /trpc/organization.resendInvitation?batch=1
organizationId* | string (uuid) | |
invitationId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.revokeInvitationPOSTRevoke a pending invitation
POST /trpc/organization.revokeInvitation?batch=1
organizationId* | string (uuid) | |
invitationId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.setRightsholderMdPrioritiesPOSTReplace the full rightsholder MD fetch priority ordering
POST /trpc/organization.setRightsholderMdPriorities?batch=1
organizationId* | string (uuid) | |
rightsholderIds* | string (uuid)[] | (default []) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.updatePOSTUpdate organization details
POST /trpc/organization.update?batch=1
organizationId* | string (uuid) | |
name* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.updateMdFetchSettingsPOSTUpdate musical detail fetch settings for the organization
POST /trpc/organization.updateMdFetchSettings?batch=1
organizationId* | string (uuid) | |
mdFetchMultiplier | integer | |
mdFetchFocus | boolean |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
organization.updateRolePOSTUpdate a user
POST /trpc/organization.updateRole?batch=1
organizationId* | string (uuid) | |
userId* | string (uuid) | |
role* | "member" | "reader" | "writer" | "admin" | "c2_customer_support" | "owner" | "c2_superadmin" |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
payor (1)
payor.listGlobalGETList globally-known payors (for autocomplete)
GET /trpc/payor.listGlobal?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product (8)
Track and album product catalog.
product.getAllMusicalDetailsForProductGETcliGet all musical details for a product in a block
GET /trpc/product.getAllMusicalDetailsForProduct?batch=1&input=…
organizationId* | string (uuid) | |
productId* | string (uuid) | |
blockId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product.getByBlockGETGet products for a specific block
GET /trpc/product.getByBlock?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 20) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
searchQuery | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product.getByIdGETcliGet a product by ID
GET /trpc/product.getById?batch=1&input=…
organizationId* | string (uuid) | |
productId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product.getMusicalDetailsBatchGETcliGet musical details for multiple products in a block
GET /trpc/product.getMusicalDetailsBatch?batch=1&input=…
organizationId* | string (uuid) | |
productIds* | string (uuid)[] | |
blockId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product.getMusicalDetailsByBlockGETGet musical details for a product within a block
GET /trpc/product.getMusicalDetailsByBlock?batch=1&input=…
organizationId* | string (uuid) | |
blockId* | string (uuid) | |
productId* | string (uuid) | |
pagination | object | |
pagination.page | integer | (default 1) |
pagination.limit | integer | (default 5000) |
pagination.sortBy | string | |
pagination.sortOrder | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product.getProductsByIdsGETcliGet multiple products by their IDs
GET /trpc/product.getProductsByIds?batch=1&input=…
organizationId* | string (uuid) | |
productIds* | string (uuid)[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
product.updateByIdPOSTcliUpdate a product by ID
POST /trpc/product.updateById?batch=1
product.updateProductMusicalDetailsRelationshipsPOSTCreate, update, and delete product to musical detail relationships
POST /trpc/product.updateProductMusicalDetailsRelationships?batch=1
rightsholder (7)
Manage rightsholders (artists, labels, publishers).
rightsholder.batchUpdatePOSTcliBatch update multiple rightsholders
POST /trpc/rightsholder.batchUpdate?batch=1
organizationId* | string (uuid) | |
updates* | unknown[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
rightsholder.createPOSTcliCreate a new rightsholder
POST /trpc/rightsholder.create?batch=1
organizationId* | string (uuid) | |
name* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
rightsholder.deleteByIdPOSTcliDelete a rightsholder by ID
POST /trpc/rightsholder.deleteById?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
rightsholder.getByIdGETcliGet a rightsholder by ID
GET /trpc/rightsholder.getById?batch=1&input=…
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
rightsholder.listGETcliList rightsholders for the organization
GET /trpc/rightsholder.list?batch=1&input=…
organizationId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
rightsholder.trackOpenPOSTDEPRECATED: use activity.trackVisit
POST /trpc/rightsholder.trackOpen?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
rightsholder.updateByIdPOSTcliUpdate a rightsholder by ID
POST /trpc/rightsholder.updateById?batch=1
organizationId* | string (uuid) | |
id* | string (uuid) | |
name* | string | |
legalName | string | |
aliases | unknown | |
musoIds | unknown | |
spotifyIds | unknown | |
mlcPartyIds | unknown | |
ipiNameNumbers | unknown | |
isnis | unknown | |
isUmpg | unknown | |
isDeleted | boolean | |
lastUpdatedById | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
sourceGoldData (3)
Gold-data overrides for sources (admin).
sourceGoldData.deletePOSTDelete a source_gold_data row
POST /trpc/sourceGoldData.delete?batch=1
id* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
sourceGoldData.listGETList source_gold_data rows with optional search
GET /trpc/sourceGoldData.list?batch=1&input=…
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
search | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
sourceGoldData.upsertPOSTCreate or update a source_gold_data row
POST /trpc/sourceGoldData.upsert?batch=1
rawValue* | string | |
earningsSource | unknown | |
earningsCategory | unknown | |
parentCompany | unknown | |
collectionSource | unknown | |
publisherSource | unknown | |
notes | unknown | |
id | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload (10)
Upload royalty statements and execute upload actions.
upload.abortedPOSTSignal that a direct upload was aborted client-side
POST /trpc/upload.aborted?batch=1
organizationId* | string (uuid) | |
statementId* | string | |
reason | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.callbackPOSTSubmit a mapping callback for statement processing
POST /trpc/upload.callback?batch=1
organizationId* | string (uuid) | |
type* | string | |
mapping_id* | string | |
file_id | string | |
upload_id | string | |
action | "skip" | "bad" | |
distributor_id | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.heartbeatPOSTExtend the TTL on in-progress direct uploads
POST /trpc/upload.heartbeat?batch=1
organizationId* | string (uuid) | |
statementIds* | string[] |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.listGETcliList uploads for the organization
GET /trpc/upload.list?batch=1&input=…
organizationId* | string (uuid) | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
filter | object | |
filter.search | string | |
filter.date_start | string | |
filter.date_end | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.resolveAllFlagsPOSTResolve all unresolved flags on a statement
POST /trpc/upload.resolveAllFlags?batch=1
organizationId* | string (uuid) | |
uploadId* | string | |
statementId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.resolveFlagPOSTResolve a single flag on a statement
POST /trpc/upload.resolveFlag?batch=1
organizationId* | string (uuid) | |
uploadId* | string | |
statementId* | string | |
flagId* | string |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.setFlagShownPOSTSet the shown/seen state of a flag on a statement
POST /trpc/upload.setFlagShown?batch=1
organizationId* | string (uuid) | |
uploadId* | string | |
statementId* | string | |
flagId* | string | |
shown* | boolean |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.statementsPOSTcliList statements for an organization or upload
POST /trpc/upload.statements?batch=1
organizationId* | string (uuid) | |
rightsholderId | string (uuid) | |
customName | string | |
pagination* | object | (default {"page":1,"limit":300,"sortBy":"name","sortOrder":"asc"}) |
pagination.page* | integer | (default 1) |
pagination.limit* | integer | (default 200) |
pagination.sortBy | string | |
pagination.sortOrder* | "asc" | "desc" | (default "asc") |
filter | object | |
filter.statementStatus | string[] | |
filter.statementIds | string[] | |
filter.includeDeleted | boolean | (default false) |
filter.fileName | string | Case-insensitive substring match on the uploaded file name |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.uploadActionPOSTcliExecute an action on an upload or statement
POST /trpc/upload.uploadAction?batch=1
organizationId* | string (uuid) | |
action* | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
upload.uploadFilePOSTcliGenerate a presigned URL and initiate a file upload
POST /trpc/upload.uploadFile?batch=1
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
user (5)
Authenticated user profile and data-access roles.
user.currentUserGETcliGet the currently authenticated user
GET /trpc/user.currentUser?batch=1&input=…
user.preferencesGETGet the current user\u2019s per-organisation preferences
GET /trpc/user.preferences?batch=1&input=…
organizationId* | string (uuid) |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
user.setDataAccessRolePOSTSet the data access role for a user
POST /trpc/user.setDataAccessRole?batch=1
organizationId* | string (uuid) | |
userId* | string (uuid) | |
role* | unknown |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
user.setRightsholderSortPOSTPersist the home-page rightsholder sort selection
POST /trpc/user.setRightsholderSort?batch=1
organizationId* | string (uuid) | |
sort* | "last_updated" | "alphabetical" | "recently_created" |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
user.updateOrganizationUserStatusPOSTUpdate a user
POST /trpc/user.updateOrganizationUserStatus?batch=1
organizationId* | string (uuid) | |
userId* | string (uuid) | |
status* | "active" | "disabled" |
* required. Full Zod-derived JSON Schema for every endpoint is in /schemas.json.
Cube analytics
All royalty analytics flow through the Cube semantic layer. The cube.* procedures wrap two Cube APIs: the JSON /v1/load REST API and the SQL /v1/cubesql API. Both are org-scoped server-side via a self-signed JWT — clients cannot escape their tenant.
analytics_full_view is the single canonical view for earnings questions. Every measure, dimension, and time dimension you need — totals, top tracks, by-source and by-territory breakdowns, currency exposure, and trailing-twelve-month windows — lives on this one view. The schema is published below.
- —Always include a filter on
analytics_full_view.rightsholder_id. The view is multi-tenant within an org and unfiltered queries are slow. - —Always prefer
analytics_full_view.royalty_earnings_usdfor monetary measures. Only useroyalty_earningswhen the question is explicitly about original currency. - —Never include
organization_idin filters. Cube'squeryRewriteinjects it from the authenticated JWT and filters that target it are stripped server-side.
cube.query input shape
cube.query, cube.queryAsync, and cube.queryPoll all accept the same payload:
typescript{ organizationId: string; // UUID, required query: { measures?: string[]; // e.g. ["analytics_full_view.royalty_earnings_usd"] dimensions?: string[]; // e.g. ["analytics_full_view.product_title"] timeDimensions?: Array<{ dimension: string; // e.g. "analytics_full_view.sales_period_start" granularity?: 'day' | 'week' | 'month' | 'quarter' | 'year'; dateRange?: string | [string, string]; // "Last 12 months" or ["2024-01-01", "2024-12-31"] }>; filters?: Array<{ member: string; operator: 'equals' | 'notEquals' | 'contains' | 'notContains' | 'gt' | 'gte' | 'lt' | 'lte' | 'set' | 'notSet' | 'inDateRange' | 'notInDateRange' | 'beforeDate' | 'afterDate'; values?: string[]; }>; order?: Record<string, 'asc' | 'desc'>; limit?: number; // 1..50000 total?: boolean; // include total row count (default false) } }
The response contains a row per result tuple, keyed by fully-qualified member path:
json{ "query": { /* echoed */ }, "lastRefreshTime": "2026-05-04T17:00:00Z", "annotation": { "measures": {...}, "dimensions": {...}, "timeDimensions": {...} }, "data": [ { "analytics_full_view.royalty_earnings_usd": "12345.67", "analytics_full_view.product_title": "Song Name" } ] }
SQL passthrough — cube.sqlQuery
For ad-hoc analysis, send raw SQL against the Cube SQL API. Org-scoping is still enforced via the JWT. Response is positional rows + a column schema:
json{ "schema": [ { "name": "royalty_earnings_usd", "column_type": "Double" }, { "name": "product_title", "column_type": "String" } ], "rows": [ ["12345.67", "Song Name"] ] }
Async polling pattern
Cube queries can take longer than a Lambda invocation can wait. Use the queryAsync / queryPoll pair to avoid timeouts:
- —Submit the query with
cube.queryAsync(orcube.sqlQueryAsync). - —Inspect the response —
{ status: "completed", data }means done,{ status: "processing" }means keep polling. - —Re-send the SAME query (Cube is idempotent on identical query bodies) via
cube.queryPolluntil status flips tocompleted.
Discovering schema dynamically
Use cube.meta at runtime to fetch the live cube definitions, or consume the typed views from @chapter-two-music/cube. The authoritative schema for analytics_full_view is below.
analytics_full_view
The canonical view for royalty analytics. Every measure and dimension you need to answer earnings questions — totals, breakdowns by source / sales type / territory / track / artist / distributor, currency exposure, and trailing-twelve-month windows — lives here. Always filter by `analytics_full_view.rightsholder_id` and prefer `analytics_full_view.royalty_earnings_usd` for monetary measures.
Measures
| Field | Type | Description |
|---|---|---|
analytics_full_view.statement_count | number | Number of distinct statements |
analytics_full_view.statement_row_count | number | Number of statement records |
analytics_full_view.royalty_earnings | number | Net royalty earnings in the statement |
analytics_full_view.gross_royalty_earnings | number | Gross royalty earnings in the statement |
analytics_full_view.units | number | Number of sales, streams or other quantity units reported on the royalty row level. Extracted from statement file. |
analytics_full_view.royalty_earnings_usd | number | Net royalty earnings converted to USD at the FX rate as of the date the rightsholder |
analytics_full_view.gross_royalty_earnings_usd | number | Gross royalty earnings converted to USD at the FX rate as of the date the rightsholder |
analytics_full_view.product_dollar_age | number | Earnings-weighted average product age. Each product\ |
analytics_full_view.fetching_percentage | number | Percentage of royalty earnings (in USD) attributed to tracks for which resolution is complete (but not necessarily successful). |
analytics_full_view.product_age_avg | number | Average product age in years, excluding missing release dates. Returns 0 when no products have known ages. |
analytics_full_view.product_age_percentage | number | Percentage of distinct products for which the product age is defined. |
analytics_full_view.product_dollar_age_percentage | number | Percentage of LTM earnings that are part of the dollar age calculation, i.e. earnings attributed to a product with a known age. This calculation only includes products with positive earnings. |
Dimensions
| Field | Type | Description |
|---|---|---|
analytics_full_view.stmt_year | number | Calendar year the statement is associated with. Extracted from statement file, derived by Baltazar or provided by user |
analytics_full_view.stmt_period | number | Period number within the year (1-12 for monthly, 1-4 for quarterly, 1-2 for half-yearly, 1 for yearly). Extracted from statement file, derived by Baltazar or user-provided. |
analytics_full_view.stmt_cadence | string | Reporting frequency (M = monthly, Q = quarterly, H = half-yearly, Y = yearly). Extracted from statement file, derived by Baltazar or user-provided. |
analytics_full_view.currency | string | Earnings Currency. ISO 3-letter code. Extracted from statement file, derived by Baltazar or user-provided. |
analytics_full_view.territory_raw | string | Territory string. Extracted from statement file. |
analytics_full_view.primary_rights_type | string | High-level rights category. Publishing, Master or Neighboring. Derived by Baltazar |
analytics_full_view.secondary_rights_type | string | Additional detail on the rights type for the royalties, mapped to a set of standard values |
analytics_full_view.rightsholder_id | string | Generated identifier for the rightsholder receiving the royalties (e.g. artist, label, publisher) |
analytics_full_view.custom_name | string | File level label set during ingestion. User-provided. |
analytics_full_view.file_name | string | Name of the uploaded file. Extracted from statement file. |
analytics_full_view.account_number | string | Account identifier. Extracted from statement file. |
analytics_full_view.account_name | string | Account identifier. Extracted from statement file. |
analytics_full_view.legal_vendor | string | Name of the legal entity or internal business unit that collected the earnings and issued the statement |
analytics_full_view.payor | string | The paying entity associated with the royalty row |
analytics_full_view.track_title_raw | string | Track/Work string. Extracted from statement file. |
analytics_full_view.track_artist_raw | string | Artist string. Extracted from statement file. |
analytics_full_view.track_title | string | Track title resolved via third-party enrichment, prior to product clustering. Derived by Baltazar. |
analytics_full_view.track_artist | string | Artist name resolved via third-party enrichment, prior to product clustering. Derived by Baltazar. |
analytics_full_view.release_title_raw | string | Release-level title string. Extracted from statement file. |
analytics_full_view.release_artist_raw | string | Release-level artist string. Extracted from statement file. |
analytics_full_view.isrc | string | Standard ISRC - unique identifier for a sound recording. Extracted from statement file. |
analytics_full_view.iswc | string | Standard ISWC - unique identifier for a musical composition. Extracted from statement file. |
analytics_full_view.upc | string | Standard UPC - unique identifier for a release (album or single). Extracted from statement file. |
analytics_full_view.other_identifier | string | Additional product or track identifier supplied on the statement (outside ISRC, ISWC or UPC). Extracted from statement file. |
analytics_full_view.composers_raw | string | Composer name list from the statement |
analytics_full_view.other_adjustments | string | Other earnings adjustments reported in the statement |
analytics_full_view.royalty_rate | number | Per-unit or percentage rate paid on the royalty line. Extracted from statement file. |
analytics_full_view.tax_deduction | number | Percentage deducted from earnings for taxes |
analytics_full_view.territory_deduction | number | Territory-specific percentage deducted from earnings |
analytics_full_view.stmt_period_full | string | Combined period identifier (e.g. 2023M12, 2024Q03). Derived by Baltazar. |
analytics_full_view.stmt_period_month | string | Statement period harmonized into its latest calendar month (e.g. M2 → M2, Q3 → M9, H1 → M6, Y1 → M12). Extracted from statement file, derived by Baltazar or user-provided. |
analytics_full_view.sales_type_raw | string | Sales type as it appears in the statement |
analytics_full_view.sales_subtype_raw | string | Additional detail on sales type as it appears in the statement |
analytics_full_view.source_raw | string | Royalty source as it appears in the statement |
analytics_full_view.subsource_raw | string | Additional detail on royalty source as it appears in the statement |
analytics_full_view.full_sales_type_raw | string | Sales type string. Extracted from statement file. |
analytics_full_view.full_source_raw | string | Source string. Extracted from statement file. |
analytics_full_view.release_title | string | Release-level title resolved via third-party enrichment, prior to product clustering. Derived by Baltazar. |
analytics_full_view.release_artist | string | Release-level artist resolved via third-party enrichment, prior to product clustering. Derived by Baltazar. |
analytics_full_view.product_age | number | Age - enriched and clustered (measured from product release date to the end of the most recent available statement period). Derived by Baltazar. |
analytics_full_view.product_age_bucket | string | Product age rounded down to an integer. Products whose age is 8 years or greater are assigned the value |
analytics_full_view.product_title | string | Title - enriched and clustered. Derived by Baltazar. |
analytics_full_view.product_artist | string | Artist - enriched and clustered. Derived by Baltazar. |
analytics_full_view.primary_sales_type | string | Top-level sales type (e.g. Streaming) - normalized and categorized. Derived by Baltazar. |
analytics_full_view.secondary_sales_type | string | Sub-level sales type (e.g. Interactive Streaming) - normalized and categorized. Derived by Baltazar. |
analytics_full_view.source | string | Normalized source name derived from (in order of priority) the collection source, publisher source, or earnings source. |
analytics_full_view.earnings_collection_source | string | Collection source (e.g. MLC) - normalized and categorized. Derived by Baltazar. |
analytics_full_view.earnings_source | string | Earnings source (e.g. Spotify) - normalized and categorized. Derived by Baltazar. |
analytics_full_view.earnings_category | string | Category classification of the earnings source (e.g., streaming, download, broadcast). Null if earnings source is unknown. |
analytics_full_view.earnings_parent_company | string | Corporate owner of the earnings source. Only populated when ownership is explicitly verified. For example, YouTube → Google, Instagram/Facebook → Meta, TikTok → ByteDance, etc. |
analytics_full_view.publisher_source | string | Canonical English name of the music publisher or publishing administrator when not acting as a collection source. Includes major publishers (w.g. Warner Chappell, Sony Music Publishing, Universal Music Publishing, BMG), independents (e.g. Kobalt, Downtown, Concord, peermusic), and administrators (e.g. Songtrust, CD Baby Pro, TuneCore Publishing). |
analytics_full_view.territory | string | Earnings territory (e.g. US) - normalized and categorized. Derived by Baltazar. |
analytics_full_view.rightsholder_name | string | Rightsholder or project name. User-provided. |
analytics_full_view.distributor_name | string | The overarching statement format owner. Derived by Baltazar. |
analytics_full_view.tm | string | Trailing 12-month earnings window per distributor, then combined. 00.TM = current, 01.LTM = last twelve months, 02.PTM = previous twelve months, etc. Derived by Baltazar. |
analytics_full_view.fx_rate | number | Foreign exchange rate used to convert the statement's original currency to USD. Derived by Baltazar. |
Time dimensions
- —
analytics_full_view.sales_period_start - —
analytics_full_view.sales_period_end - —
analytics_full_view.release_date - —
analytics_full_view.release_release_date - —
analytics_full_view.product_release_date
Agent integration guide
How to wire ChapterTwo into an autonomous agent (Claude, Cursor, custom GPT, etc.) so it can answer royalty questions, run analytics, and trigger workflows safely. The same patterns power the in-product Data Agent that ships with ChapterTwo (Claude Haiku 4.5 with Gemini fallback) — see packages/agents/DATA-AGENT.md for the full internal architecture.
Earnings recipe
Almost every analytics question reduces to the same five-step recipe. Follow it in order; do not skip steps.
- —Verify auth. Call
user.currentUser. If multi-org, ask the user which organization to scope to and cache its UUID. - —Resolve rightsholder(s). Call
rightsholder.list. Every Cube query MUST filter byanalytics_full_view.rightsholder_id— never run an unscoped earnings query. - —Discover the schema. If the question references a field you're unsure about, call
cube.metafirst. Do not guess field names. - —Build the query. Default measure is
analytics_full_view.royalty_earnings_usd. Always setlimit(10 for “top N”, 20 for breakdowns) andorder. - —Execute. Use
cube.queryfor sub-30s queries. For anything larger or unknown, use thequeryAsync/queryPollpair.
typescriptimport { createClient } from '@chapter-two-music/sdk'; const client = createClient({ apiUrl: 'https://api.prod.chaptertwo.com', token: process.env.C2_TOKEN!, orgId: process.env.C2_ORG_ID!, }); // 1. Boot — confirm principal + org const me = await client.query('user.currentUser'); // 2. Resolve rightsholder const { data: rightsholders } = await client.query('rightsholder.list', { pagination: { page: 1, limit: 50 }, }); const rh = rightsholders[0]; // or prompt the user to pick // 3 + 4 + 5. Top 10 tracks by USD earnings, last 12 months const earnings = await client.mutate('cube.query', { query: { measures: ['analytics_full_view.royalty_earnings_usd'], dimensions: ['analytics_full_view.product_title', 'analytics_full_view.product_artist'], filters: [ { member: 'analytics_full_view.rightsholder_id', operator: 'equals', values: [rh.id] }, ], timeDimensions: [{ dimension: 'analytics_full_view.sales_period_start', dateRange: 'Last 12 months', }], order: { 'analytics_full_view.royalty_earnings_usd': 'desc' }, limit: 10, }, });
Endpoint discovery
The SDK ships with a static catalogue of every procedure plus its Zod-derived JSON schema. Use it to build dynamic tool manifests for your agent without burning tokens on this page:
typescriptimport { ENDPOINTS, // string[] of all procedure names listEndpoints, // (namespace) => string[] isValidEndpoint, getEndpointMeta, // returns { type, inputSchema, description? } } from '@chapter-two-music/sdk'; // Build agent tool definitions from the catalogue const tools = ENDPOINTS.map((endpoint) => { const meta = getEndpointMeta(endpoint); return { name: endpoint.replace('.', '_'), description: meta?.description ?? endpoint, parameters: meta?.inputSchema, }; });
Cube polling loop
typescriptasync function runCube(query: CubeQuery, maxAttempts = 30) { let res = await client.mutate('cube.queryAsync', { query }); for (let i = 0; res.status === 'processing' && i < maxAttempts; i++) { await new Promise((r) => setTimeout(r, 2_000)); res = await client.mutate('cube.queryPoll', { query }); } if (res.status !== 'completed') throw new Error('Cube query timed out'); return res.data; }
Common query patterns
Substitute the actual rightsholder UUID(s). Strip the analytics_full_view. prefix from response keys before presenting data to the user.
| Question | measures + dimensions |
|---|---|
| Total earnings (USD) | m: royalty_earnings_usd |
| By calendar year | m: royalty_earnings_usd, d: stmt_year |
| Top platforms | m: royalty_earnings_usd, d: earnings_source |
| By territory | m: royalty_earnings_usd, d: territory |
| Top tracks | m: royalty_earnings_usd, d: product_title |
| Streaming vs download | m: royalty_earnings_usd, d: primary_sales_type |
| Master vs publishing | m: royalty_earnings_usd, d: primary_rights_type |
| TM windows (LTM, PTM) | m: royalty_earnings_usd, d: tm |
| Catalogue maturity | m: product_dollar_age |
Best practices
- —Always pass
organizationIdin the body. The header alone is not sufficient for OAuth requests. Cache the org UUID once at startup. - —Always include a rightsholder filter on Cube queries. An unscoped query against
analytics_full_viewwill be slow or rejected. Resolve viarightsholder.listfirst. - —Default to USD. Use
analytics_full_view.royalty_earnings_usdunless the user explicitly asks for the original currency or per-currency breakdown. - —Prefer normalized dimensions over raw. Use
earnings_sourcenotsource_raw;primary_sales_typenotfull_sales_type_raw;territorynotterritory_raw. Raw fields are unmapped strings from the source statement. - —Bound every query. Cube queries with no time range scan large volumes. Either set a
timeDimensionsdateRangeonsales_period_startor filter bystmt_year. - —Always set limit + order. Default to
limit: 10for “top N”,limit: 20for breakdowns,limit: 24for monthly time series. Sort by{ "<measure>": "desc" }. - —Treat limited breakdown sums as subtotals. If a dimension query returns exactly the limit, the sum is a top-N subtotal. For a true total, run a second query with no dimensions.
- —Surface errors verbatim. Errors carry a stable
data.codeanddata.errorCode. Branch on those, not on the human-readable message. - —Treat mutations as deliberate. Endpoints like
rightsholder.deleteByIdrequire admin role and have no soft-delete. Confirm with the user before invoking destructive mutations. - —Refresh tokens proactively. Access tokens last 1 hour. If your agent runs longer, refresh ~5 minutes before expiry, not after a 401.
MCP server
For Claude Desktop, Cursor, and other MCP-aware clients, point the client at https://mcp.chaptertwo.com/api/mcp. Auth is auto-negotiated via OAuth + PKCE. The MCP server exposes a curated subset of tools and a generic c2_api_call escape hatch.
json{ "mcpServers": { "chaptertwo": { "url": "https://mcp.chaptertwo.com/api/mcp" } } }
| Tool | Description |
|---|---|
| c2_health | Check API health |
| c2_whoami | Current user + organization |
| c2_get_permissions | Permissions and capabilities for the principal |
| c2_list_endpoints | Catalogue of all procedures, optionally filtered by namespace |
| c2_search_endpoints | Keyword search across procedures |
| c2_describe_endpoint | Schema + metadata for one procedure |
| c2_api_call | Call any procedure (auto-detects query vs mutation) |
| c2_query_analytics | Wrapper around cube.query with sane defaults |
| c2_cube_meta | Live Cube schema (measures, dimensions, descriptions) |
c2_query_analytics which uses the async polling loop.