API reference
Sandbox
Calls that play review, the stores and payouts, for sandbox keys only.
Base address https://api.stillmoregroup.com/api/v1. Every answer is JSON; errors have one shape.
| Endpoint | What it does |
|---|---|
POST /sandbox/releases/{releaseId}/review | Play review |
POST /sandbox/releases/{releaseId}/deliver | Play delivery |
POST /sandbox/royalties | Play a royalty month |
POST /sandbox/payouts/{payoutId}/pay | Play paying a payout |
POST /sandbox/reset | Empty the sandbox |
Play review#
POST /sandbox/releases/{releaseId}/review · scope releases:read
Test keys only. Decides a submitted release as a reviewer would: APPROVE, REQUEST_CHANGES or REJECT, with the same status changes, review notes and events (release.approved, release.changes_requested, release.rejected).
Path parameters#
releaseIdstring- An id.
Body#
decisionstringRequired- One of
APPROVE,REQUEST_CHANGES,REJECT. notestring- What the label reads; needed to request changes or reject. 3 to 500 characters.
Headers#
Idempotency-Key- Any unique value, such as a UUID: a retry with the same key and body gets the first answer again for 24 hours.
Stillmore-Organization- A sub-label to act for, when the key may (see Sub-labels).
Example request#
curl -X POST "https://api.stillmoregroup.com/api/v1/sandbox/releases/{releaseId}/review" \
-H "Authorization: Bearer $STILLMORE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"decision": "APPROVE",
"note": "Sent from our catalogue system"
}'Response 200#
data holds the object below.
idstringlabelIdstringtitlestringversionTitlestring or nulltypestring- One of
SINGLE,EP,ALBUM. statusstring- Where the release is, review and delivery together. One of
DRAFT,LABEL_REVIEW,IN_REVIEW,CHANGES_REQUESTED,REJECTED,APPROVED,SENT_TO_STORES,LIVE,REFUSED_BY_STORES,TAKEDOWN_REQUESTED,TAKEN_DOWN,ARCHIVED. primaryArtistIdsstring[]featuredArtistIdsstring[]
Show 66 more fieldsHide 66 fields
primaryGenrestring or nullsecondaryGenrestring or nulllanguagestring or nullexplicitbooleanreleaseDatestring (date) or nulloriginalReleaseDatestring (date) or nullcopyrightobjectcopyright.objectcLine copyright.objectpLine territoriesobjectterritories.stringmode - One of
WORLDWIDE,CUSTOM. territories.string[]codes storesobjectstores.stringmode - One of
ALL,CUSTOM. stores.string[]codes upcstring or nullartworkobject or null- The cover; null until one is uploaded.
artwork.stringstatus - One of
PENDING_UPLOAD,UPLOADED,VERIFIED,FAILED,DELETED. artwork.string or nullfileName artwork.string or nullmimeType artwork.number or nullsizeBytes artwork.string or nulluploadedAt - ISO 8601 timestamp in UTC.
artwork.number or nullwidth artwork.number or nullheight trackCountinteger- From -9007199254740991 to 9007199254740991.
tracksobject[]- Included when one release is read, created or changed.
tracks[].stringid tracks[].stringreleaseId tracks[].integerposition - From -9007199254740991 to 9007199254740991.
tracks[].stringtitle tracks[].string or nullversionTitle tracks[].string[]primaryArtistIds tracks[].string[]featuredArtistIds tracks[].booleanexplicit tracks[].booleanisCover tracks[].string or nulllanguage tracks[].string or nullisrc tracks[].string or nulllyrics tracks[].objectcredits tracks[].object or nullaudio - The master; null until one is uploaded.
tracks[].string or nullexternalId - Your own id for it, when you set one.
tracks[].map of stringmetadata - Your own keys and values, as you sent them.
tracks[].stringcreatedAt - ISO 8601 timestamp in UTC.
tracks[].stringupdatedAt - ISO 8601 timestamp in UTC.
tracks[].stringrevision - Changes whenever the record does; send it back in If-Match to refuse an update when someone else changed it first.
reviewobject- Included when one release is read, created or changed.
review.string or nullsubmittedAt - ISO 8601 timestamp in UTC.
review.string or nulldecidedAt - ISO 8601 timestamp in UTC.
review.booleanautomatic - The account's automatic approval passed the release without a person.
review.string or nullnote - What review asked for when it sent the release back, or why it rejected it.
review.object[]issues review.object or nulldocumentsRequested deliveryobject- On its way to the stores, codes and store pages; included when one release is read, created or changed.
delivery.stringstatus - One of
NOT_SENT,SENT_TO_STORES,LIVE,REFUSED_BY_STORES,TAKEDOWN_REQUESTED,TAKEN_DOWN. delivery.string or nullsentAt delivery.string or nullliveAt delivery.string or nullrefusedReason delivery.object[]storeLinks delivery.object or nullsmartLink submittedAtstring or null- ISO 8601 timestamp in UTC.
approvedAtstring or null- ISO 8601 timestamp in UTC.
externalIdstring or null- Your own id for it, when you set one.
metadatamap of string- Your own keys and values, as you sent them.
createdAtstring- ISO 8601 timestamp in UTC.
updatedAtstring- ISO 8601 timestamp in UTC.
revisionstring- Changes whenever the record does; send it back in If-Match to refuse an update when someone else changed it first.
{
"data": {
"id": "6731c2a1b3c4d5e6f7a8b9c0",
"labelId": "6731c2a1b3c4d5e6f7a8b9c0",
"title": "Night Signals",
"versionTitle": "Radio Edit",
"type": "SINGLE",
"status": "DRAFT",
"primaryArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"featuredArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"primaryGenre": "Pop",
"secondaryGenre": "Pop",
"language": "en",
"explicit": false,
"releaseDate": "2027-01-15",
"originalReleaseDate": "2026-10-01",
"copyright": {
"cLine": {
"year": 2026,
"holder": "North Records"
},
"pLine": {
"year": 2026,
"holder": "North Records"
}
},
"territories": {
"mode": "WORLDWIDE",
"codes": [
"123456"
]
},
"stores": {
"mode": "ALL",
"codes": [
"123456"
]
},
"upc": "0612345678905",
"artwork": {
"status": "PENDING_UPLOAD",
"fileName": "cover.jpg",
"mimeType": "…",
"sizeBytes": 2483117,
"uploadedAt": "2026-11-01T09:30:00.000Z",
"width": 1,
"height": 1
},
"trackCount": 1200,
"tracks": [
{
"id": "6731c2a1b3c4d5e6f7a8b9c0",
"releaseId": "6731c2a1b3c4d5e6f7a8b9c0",
"position": 1,
"title": "Night Signals",
"versionTitle": "Radio Edit",
"primaryArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"featuredArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"explicit": false,
"isCover": false,
"language": "en",
"isrc": "GBXYZ2600001",
"lyrics": "…",
"credits": {
"songwriters": [],
"composers": [],
"producers": [],
"publishers": [],
"mixers": [],
"masteringEngineers": [],
"remixers": []
},
"audio": {
"status": "PENDING_UPLOAD",
"fileName": "cover.jpg",
"mimeType": "…",
"sizeBytes": 2483117,
"uploadedAt": "2026-11-01T09:30:00.000Z",
"durationSeconds": 214,
"sampleRate": 1,
"bitDepth": 1,
"channels": 1
},
"externalId": "6731c2a1b3c4d5e6f7a8b9c0",
"metadata": {
"catalogueId": "1042"
},
"createdAt": "2026-11-01T09:30:00.000Z",
"updatedAt": "2026-11-01T09:30:00.000Z",
"revision": "…"
}
],
"review": {
"submittedAt": "2026-11-01T09:30:00.000Z",
"decidedAt": "2026-11-01T09:30:00.000Z",
"automatic": false,
"note": "Sent from our catalogue system",
"issues": [
{
"category": "…",
"message": "Sent from our catalogue system"
}
],
"documentsRequested": {
"note": "Sent from our catalogue system",
"requestedAt": "2026-11-01T09:30:00.000Z",
"fulfilled": false
}
},
"delivery": {
"status": "NOT_SENT",
"sentAt": "2026-11-01T09:30:00.000Z",
"liveAt": "2026-11-01T09:30:00.000Z",
"refusedReason": "Sent from our catalogue system",
"storeLinks": [
{
"store": "Spotify",
"url": "https://example.com/webhooks/stillmore"
}
],
"smartLink": {
"url": "https://example.com/webhooks/stillmore",
"enabled": true
}
},
"submittedAt": "2026-11-01T09:30:00.000Z",
"approvedAt": "2026-11-01T09:30:00.000Z",
"externalId": "6731c2a1b3c4d5e6f7a8b9c0",
"metadata": {
"catalogueId": "1042"
},
"createdAt": "2026-11-01T09:30:00.000Z",
"updatedAt": "2026-11-01T09:30:00.000Z",
"revision": "…"
}
}Errors#
| Code | What it means |
|---|---|
SANDBOX_ONLY | Simulations run in the sandbox only; call them with a test key (sm_test_…). |
INVALID_RELEASE_STATUS | From the workspace rule behind this endpoint; message says what to change. |
Every endpoint can also give the errors every call can give, such as VALIDATION_ERROR, INSUFFICIENT_SCOPE and RATE_LIMITED.
Play delivery#
POST /sandbox/releases/{releaseId}/deliver · scope releases:read
Test keys only. Sends an approved release to make-believe stores: LIVE gives it a UPC, ISRCs and store links (release.sent, release.codes_assigned, release.live, release.store_links_updated); REFUSED_BY_STORES refuses it with your reason (release.provider_rejected).
Path parameters#
releaseIdstring- An id.
Body#
outcomestring- One of
LIVE,REFUSED_BY_STORES. Default"LIVE". reasonstring- For REFUSED_BY_STORES. 3 to 300 characters.
Headers#
Idempotency-Key- Any unique value, such as a UUID: a retry with the same key and body gets the first answer again for 24 hours.
Stillmore-Organization- A sub-label to act for, when the key may (see Sub-labels).
Example request#
curl -X POST "https://api.stillmoregroup.com/api/v1/sandbox/releases/{releaseId}/deliver" \
-H "Authorization: Bearer $STILLMORE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'Response 200#
data holds the object below.
idstringlabelIdstringtitlestringversionTitlestring or nulltypestring- One of
SINGLE,EP,ALBUM. statusstring- Where the release is, review and delivery together. One of
DRAFT,LABEL_REVIEW,IN_REVIEW,CHANGES_REQUESTED,REJECTED,APPROVED,SENT_TO_STORES,LIVE,REFUSED_BY_STORES,TAKEDOWN_REQUESTED,TAKEN_DOWN,ARCHIVED. primaryArtistIdsstring[]featuredArtistIdsstring[]
Show 66 more fieldsHide 66 fields
primaryGenrestring or nullsecondaryGenrestring or nulllanguagestring or nullexplicitbooleanreleaseDatestring (date) or nulloriginalReleaseDatestring (date) or nullcopyrightobjectcopyright.objectcLine copyright.objectpLine territoriesobjectterritories.stringmode - One of
WORLDWIDE,CUSTOM. territories.string[]codes storesobjectstores.stringmode - One of
ALL,CUSTOM. stores.string[]codes upcstring or nullartworkobject or null- The cover; null until one is uploaded.
artwork.stringstatus - One of
PENDING_UPLOAD,UPLOADED,VERIFIED,FAILED,DELETED. artwork.string or nullfileName artwork.string or nullmimeType artwork.number or nullsizeBytes artwork.string or nulluploadedAt - ISO 8601 timestamp in UTC.
artwork.number or nullwidth artwork.number or nullheight trackCountinteger- From -9007199254740991 to 9007199254740991.
tracksobject[]- Included when one release is read, created or changed.
tracks[].stringid tracks[].stringreleaseId tracks[].integerposition - From -9007199254740991 to 9007199254740991.
tracks[].stringtitle tracks[].string or nullversionTitle tracks[].string[]primaryArtistIds tracks[].string[]featuredArtistIds tracks[].booleanexplicit tracks[].booleanisCover tracks[].string or nulllanguage tracks[].string or nullisrc tracks[].string or nulllyrics tracks[].objectcredits tracks[].object or nullaudio - The master; null until one is uploaded.
tracks[].string or nullexternalId - Your own id for it, when you set one.
tracks[].map of stringmetadata - Your own keys and values, as you sent them.
tracks[].stringcreatedAt - ISO 8601 timestamp in UTC.
tracks[].stringupdatedAt - ISO 8601 timestamp in UTC.
tracks[].stringrevision - Changes whenever the record does; send it back in If-Match to refuse an update when someone else changed it first.
reviewobject- Included when one release is read, created or changed.
review.string or nullsubmittedAt - ISO 8601 timestamp in UTC.
review.string or nulldecidedAt - ISO 8601 timestamp in UTC.
review.booleanautomatic - The account's automatic approval passed the release without a person.
review.string or nullnote - What review asked for when it sent the release back, or why it rejected it.
review.object[]issues review.object or nulldocumentsRequested deliveryobject- On its way to the stores, codes and store pages; included when one release is read, created or changed.
delivery.stringstatus - One of
NOT_SENT,SENT_TO_STORES,LIVE,REFUSED_BY_STORES,TAKEDOWN_REQUESTED,TAKEN_DOWN. delivery.string or nullsentAt delivery.string or nullliveAt delivery.string or nullrefusedReason delivery.object[]storeLinks delivery.object or nullsmartLink submittedAtstring or null- ISO 8601 timestamp in UTC.
approvedAtstring or null- ISO 8601 timestamp in UTC.
externalIdstring or null- Your own id for it, when you set one.
metadatamap of string- Your own keys and values, as you sent them.
createdAtstring- ISO 8601 timestamp in UTC.
updatedAtstring- ISO 8601 timestamp in UTC.
revisionstring- Changes whenever the record does; send it back in If-Match to refuse an update when someone else changed it first.
{
"data": {
"id": "6731c2a1b3c4d5e6f7a8b9c0",
"labelId": "6731c2a1b3c4d5e6f7a8b9c0",
"title": "Night Signals",
"versionTitle": "Radio Edit",
"type": "SINGLE",
"status": "DRAFT",
"primaryArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"featuredArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"primaryGenre": "Pop",
"secondaryGenre": "Pop",
"language": "en",
"explicit": false,
"releaseDate": "2027-01-15",
"originalReleaseDate": "2026-10-01",
"copyright": {
"cLine": {
"year": 2026,
"holder": "North Records"
},
"pLine": {
"year": 2026,
"holder": "North Records"
}
},
"territories": {
"mode": "WORLDWIDE",
"codes": [
"123456"
]
},
"stores": {
"mode": "ALL",
"codes": [
"123456"
]
},
"upc": "0612345678905",
"artwork": {
"status": "PENDING_UPLOAD",
"fileName": "cover.jpg",
"mimeType": "…",
"sizeBytes": 2483117,
"uploadedAt": "2026-11-01T09:30:00.000Z",
"width": 1,
"height": 1
},
"trackCount": 1200,
"tracks": [
{
"id": "6731c2a1b3c4d5e6f7a8b9c0",
"releaseId": "6731c2a1b3c4d5e6f7a8b9c0",
"position": 1,
"title": "Night Signals",
"versionTitle": "Radio Edit",
"primaryArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"featuredArtistIds": [
"6731c2a1b3c4d5e6f7a8b9c0"
],
"explicit": false,
"isCover": false,
"language": "en",
"isrc": "GBXYZ2600001",
"lyrics": "…",
"credits": {
"songwriters": [],
"composers": [],
"producers": [],
"publishers": [],
"mixers": [],
"masteringEngineers": [],
"remixers": []
},
"audio": {
"status": "PENDING_UPLOAD",
"fileName": "cover.jpg",
"mimeType": "…",
"sizeBytes": 2483117,
"uploadedAt": "2026-11-01T09:30:00.000Z",
"durationSeconds": 214,
"sampleRate": 1,
"bitDepth": 1,
"channels": 1
},
"externalId": "6731c2a1b3c4d5e6f7a8b9c0",
"metadata": {
"catalogueId": "1042"
},
"createdAt": "2026-11-01T09:30:00.000Z",
"updatedAt": "2026-11-01T09:30:00.000Z",
"revision": "…"
}
],
"review": {
"submittedAt": "2026-11-01T09:30:00.000Z",
"decidedAt": "2026-11-01T09:30:00.000Z",
"automatic": false,
"note": "Sent from our catalogue system",
"issues": [
{
"category": "…",
"message": "Sent from our catalogue system"
}
],
"documentsRequested": {
"note": "Sent from our catalogue system",
"requestedAt": "2026-11-01T09:30:00.000Z",
"fulfilled": false
}
},
"delivery": {
"status": "NOT_SENT",
"sentAt": "2026-11-01T09:30:00.000Z",
"liveAt": "2026-11-01T09:30:00.000Z",
"refusedReason": "Sent from our catalogue system",
"storeLinks": [
{
"store": "Spotify",
"url": "https://example.com/webhooks/stillmore"
}
],
"smartLink": {
"url": "https://example.com/webhooks/stillmore",
"enabled": true
}
},
"submittedAt": "2026-11-01T09:30:00.000Z",
"approvedAt": "2026-11-01T09:30:00.000Z",
"externalId": "6731c2a1b3c4d5e6f7a8b9c0",
"metadata": {
"catalogueId": "1042"
},
"createdAt": "2026-11-01T09:30:00.000Z",
"updatedAt": "2026-11-01T09:30:00.000Z",
"revision": "…"
}
}Errors#
| Code | What it means |
|---|---|
SANDBOX_ONLY | Simulations run in the sandbox only; call them with a test key (sm_test_…). |
RELEASE_NOT_APPROVED | From the workspace rule behind this endpoint; message says what to change. |
Every endpoint can also give the errors every call can give, such as VALIDATION_ERROR, INSUFFICIENT_SCOPE and RATE_LIMITED.
Play a royalty month#
POST /sandbox/royalties · scope finance:read
Test keys only. Reports gross royalties for a month over the sandbox's live releases (store and territory lines), publishes the statement and credits the balance (statement.published).
Body#
monthstring- The reporting month; last month by default.
grossstring- USD reported for the month, spread over the sandbox's live releases. Default
"250.00".
Headers#
Idempotency-Key- Any unique value, such as a UUID: a retry with the same key and body gets the first answer again for 24 hours.
Stillmore-Organization- A sub-label to act for, when the key may (see Sub-labels).
Example request#
curl -X POST "https://api.stillmoregroup.com/api/v1/sandbox/royalties" \
-H "Authorization: Bearer $STILLMORE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'Response 201#
data holds the object below.
idstringstatusstring- VOID: Stillmore Group withdrew it after publishing and a corrected statement follows; its lines and CSV answer 410. One of
PUBLISHED,VOID. periodStartstringperiodEndstringcurrencystringgrossstring- What the stores reported for the period.
feesstring- A decimal amount in its currency, e.g. "1250.40".
netstring- What was credited to the balance.
revenueSharePercentstring or null- The organization's share applied, when an agreement set one.
versionnumberpublishedAtstring or null
{
"data": {
"id": "6731c2a1b3c4d5e6f7a8b9c0",
"status": "PUBLISHED",
"periodStart": "…",
"periodEnd": "…",
"currency": "USD",
"gross": "250.00",
"fees": "250.00",
"net": "250.00",
"revenueSharePercent": "…",
"version": 1,
"publishedAt": "2026-11-01T09:30:00.000Z"
}
}Errors#
| Code | What it means |
|---|---|
SANDBOX_ONLY | Simulations run in the sandbox only; call them with a test key (sm_test_…). |
NOTHING_LIVE | From the workspace rule behind this endpoint; message says what to change. |
STATEMENT_EXISTS | From the workspace rule behind this endpoint; message says what to change. |
Every endpoint can also give the errors every call can give, such as VALIDATION_ERROR, INSUFFICIENT_SCOPE and RATE_LIMITED.
Play paying a payout#
POST /sandbox/payouts/{payoutId}/pay · scope finance:read
Test keys only. Approves and pays a pending payout request (payout.approved, payout.paid), taking it off the balance.
Path parameters#
payoutIdstring- An id.
Headers#
Idempotency-Key- Any unique value, such as a UUID: a retry with the same key and body gets the first answer again for 24 hours.
Stillmore-Organization- A sub-label to act for, when the key may (see Sub-labels).
Example request#
curl -X POST "https://api.stillmoregroup.com/api/v1/sandbox/payouts/{payoutId}/pay" \
-H "Authorization: Bearer $STILLMORE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"Response 200#
data holds the object below.
idstringamountstring- A decimal amount in its currency, e.g. "1250.40".
currencystringstatusstring- One of
PENDING,APPROVED,REJECTED,PROCESSING,PAID,FAILED,CANCELLED. destinationobjectdestination.stringtype - One of
IBAN,BANK_ACCOUNT,PAYPAL,PAYONEER. destination.stringlabel destination.stringsummary - The account, masked: GB•• •••• 4821, •••• 0042, PayPal.
Show 7 more fieldsHide 7 fields
notestring or nullreviewNotestring or null- Stillmore Group's note when approving or refusing.
paymentReferencestring or nullpaidAtstring or nullcancelledAtstring or nullrequestedViastring- One of
APP,API. createdAtstring
{
"data": {
"id": "6731c2a1b3c4d5e6f7a8b9c0",
"amount": "250.00",
"currency": "USD",
"status": "PENDING",
"destination": {
"type": "IBAN",
"label": "…",
"summary": "…"
},
"note": "Sent from our catalogue system",
"reviewNote": "Sent from our catalogue system",
"paymentReference": "…",
"paidAt": "2026-11-01T09:30:00.000Z",
"cancelledAt": "2026-11-01T09:30:00.000Z",
"requestedVia": "APP",
"createdAt": "2026-11-01T09:30:00.000Z"
}
}Errors#
| Code | What it means |
|---|---|
SANDBOX_ONLY | Simulations run in the sandbox only; call them with a test key (sm_test_…). |
PAYOUT_NOT_FOUND | From the workspace rule behind this endpoint; message says what to change. |
PAYOUT_NOT_PENDING | From the workspace rule behind this endpoint; message says what to change. |
Every endpoint can also give the errors every call can give, such as VALIDATION_ERROR, INSUFFICIENT_SCOPE and RATE_LIMITED.
Empty the sandbox#
POST /sandbox/reset · any key
Test keys only. Deletes everything made in the sandbox: catalogue, files, smart links, licences, statements, ledger, payouts and events. Webhook endpoints and keys stay.
Body#
confirmtrueRequired- Send true: everything in the sandbox is deleted.
Headers#
Idempotency-Key- Any unique value, such as a UUID: a retry with the same key and body gets the first answer again for 24 hours.
Stillmore-Organization- A sub-label to act for, when the key may (see Sub-labels).
Example request#
curl -X POST "https://api.stillmoregroup.com/api/v1/sandbox/reset" \
-H "Authorization: Bearer $STILLMORE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"confirm": true
}'Response 200#
data holds the object below.
deletedmap of integer- How many records of each kind went.
{
"data": {
"deleted": {
"key": 1
}
}
}Errors#
| Code | What it means |
|---|---|
SANDBOX_ONLY | Simulations run in the sandbox only; call them with a test key (sm_test_…). |
Every endpoint can also give the errors every call can give, such as VALIDATION_ERROR, INSUFFICIENT_SCOPE and RATE_LIMITED.