Catalog & Distribution API
Use the InterSpace Distribution REST API to manage artists, build and submit releases, upload audio and artwork, and read royalties, analytics and wallet data for your account.
Introduction
This is the same API that powers the InterSpace Distribution dashboard and mobile apps. Every request is made as one account (the account that owns the key or token), and every response is scoped to that account's artists, releases, royalties and wallet. Labels use it to automate catalog ingestion and pull earnings data into their own systems.
Base URL
Each resource is a single script under the base URL, for
example https://cms.interspacemusic.com/api/releases.php.
The operation is selected with an action
parameter (in the query string for reads, and in the query
string or request body for writes, as noted per endpoint) or
with the HTTP method. There are no nested resource paths:
use query parameters such as ?id=123 instead.
There is no separate sandbox. Releases you create stay as private drafts until you explicitly submit them, so you can safely build and test an integration by creating drafts and deleting test data before submission. Questions: support@interspacemusic.com.
Authentication
Every endpoint (except signup, login and password recovery)
requires a bearer token in the Authorization
header. There are two ways to get one:
| Credential | How to get it | Lifetime |
|---|---|---|
| API key (Label plan) | Issued to your account by InterSpace support on request. Email support@interspacemusic.com from the account's email address. | Long-lived, until revoked. Ask support to revoke and reissue if it is ever exposed. |
| Login token | POST /api/mobile_auth.php?action=login with the account's email and password (see Session endpoints), or the token returned by signup. |
30 days. Log in again when you receive a 401. |
Authorization header
Authorization: Bearer <token>
Quick check
curl -s "https://cms.interspacemusic.com/api/mobile_auth.php?action=me" \
-H "Authorization: Bearer $INTERSPACE_TOKEN"
A missing token returns
401 {"ok":false,"error":"Authentication required.","code":"UNAUTHENTICATED"};
an expired or revoked one returns
401 ... "Token invalid or expired." with the same code.
Accounts with two-factor authentication enabled cannot use
the password login endpoint and should request an API key.
Requests & Responses
Request bodies
Endpoints accept one of two body formats, noted on each
endpoint below. Sending the wrong one means the server sees
empty fields and returns a 422.
- Form:
multipart/form-data(required when a file is attached) orapplication/x-www-form-urlencoded. List and object values (credits, platforms, country lists) are sent as JSON-encoded strings inside a form field. - JSON:
Content-Type: application/jsonwith a JSON object body.
Response envelope
Successful responses:
{
"ok": true,
"data": { ... }, // object or array
"total": 42, // list endpoints: pagination fields sit next to "data"
"page": 1,
"limit": 20,
"total_pages": 3
}
Errors use a non-2xx HTTP status and a machine-readable code:
{ "ok": false, "error": "Release not found.", "code": "NOT_FOUND" }
| HTTP | Meaning | Typical codes |
|---|---|---|
| 400 | Unknown action or malformed request | BAD_ACTION, INVALID_JSON |
| 401 | Missing, expired or revoked token | UNAUTHENTICATED |
| 402 | Plan limit reached or payment required | PLAN_RELEASE_LIMIT, PAYMENT_REQUIRED |
| 403 | Not allowed for this account or plan | PLAN_LOCKED, ARTIST_LIMIT_REACHED, KYC_REQUIRED |
| 404 | Not found, or not owned by your account | NOT_FOUND |
| 405 | Wrong HTTP method for this endpoint | METHOD_NOT_ALLOWED |
| 409 | Conflicts with the current state (for example the release is no longer a draft) | NOT_DRAFT, ALREADY_SUBMITTED, ALREADY_PENDING |
| 413 / 415 | File too large / unsupported file type | FILE_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE |
| 422 | Validation failed; error explains which field | VALIDATION and field-specific codes |
| 429 | Rate limited | RATE_LIMITED |
| 5xx | Server or storage error; safe to retry idempotent reads | DB_ERROR, STORAGE_ERROR |
Pagination
List endpoints take page (1-based, default 1)
and limit (default 20, maximum 100 unless noted).
IDs are integers. Dates are YYYY-MM-DD;
timestamps are YYYY-MM-DD HH:MM:SS. Money
values are in USD unless a currency field says
otherwise.
Account Creation: Agent / API Signup
AI agents and other automation acting on behalf of an artist can create a real InterSpace Distribution account directly, without a human filling out the web signup form, and receive a bearer token that works with every endpoint in this reference.
https://cms.interspacemusic.com/api/mobile_auth.php?action=signup
What it does: Creates a new artist account on the Free plan and returns a bearer token (valid for 30 days), ready to use immediately. No API key or pre-registration is required.
When to use
- An agent is onboarding an artist who doesn't yet have an InterSpace Distribution account
- You want a token to call the endpoints below (artists, releases, uploads, analytics) on that artist's behalf
Body (JSON)
first_name,email,password(min 8 chars): required-
agent_name: required. Identifies your agent or integration (e.g."Claude","my-artist-bot") so InterSpace can attribute the signup. Calls without it are rejected with422 AGENT_NAME_REQUIRED. Can also be sent as anX-Agent-Nameheader. last_name: optional
Request example
curl -s -X POST "https://cms.interspacemusic.com/api/mobile_auth.php?action=signup" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Ada",
"last_name": "Okafor",
"email": "ada@example.com",
"password": "correct horse battery staple",
"agent_name": "Claude"
}'
Success response (200)
{
"ok": true,
"data": {
"token": "e45f67c4...",
"expires_in": 7776000,
"user": {
"id": 5432,
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Okafor",
"plan": "FREE_PLAN",
"display_name": "Ada Okafor",
"avatar_url": null,
"location": null,
"username": null,
"subscription_status": "active",
"period_end": null,
"billing_cycle": "free",
"currency": "USD",
"is_paid": false
}
}
}
Treat a 401 response, not expires_in,
as the signal to log in again: login tokens are valid for 30 days.
Error examples
// Email already registered (409)
{ "ok": false, "error": "An account with that email already exists.", "code": "EMAIL_TAKEN" }
// Missing/invalid fields (422)
{ "ok": false, "error": "Name, email, and password are required.", "code": "VALIDATION" }
// Rate limited: 3 signups per hour per network (429)
{ "ok": false, "error": "Too many signups from your network. Please try again later.", "code": "RATE_LIMITED" }
The new account starts unverified. The artist receives a
6-digit code by email; submit it with
POST ?action=verify_otp_email, or call
POST ?action=resend_verification to send a new
one. An unverified account cannot use the password login
endpoint (403 EMAIL_NOT_VERIFIED).
MCP server (for MCP-native agents)
If your agent speaks the Model Context Protocol directly rather than raw REST, connect to InterSpace Distribution's MCP server instead. It wraps the same signup and login endpoints as typed tools, with no API key required.
https://mcp.interspacemusic.com/mcp
(Streamable HTTP transport)
Tools exposed: signup_artist (agent_name
required), login_artist,
get_current_user, and
resend_verification. No tool can set or
change an account's type or plan; those fields are not
accepted as input. See GET
https://mcp.interspacemusic.com/ for a live
summary, or GET
https://mcp.interspacemusic.com/health for a
liveness check.
Session & Account
/api/mobile_auth.php. All actions go in the query string; bodies are JSON.
| Method | Action | Auth | Body | Returns |
|---|---|---|---|---|
| POST | ?action=login | None | email, password, optional device (label for the token) | { token, expires_in, user } |
| POST | ?action=signup | None | See Account Creation | { token, expires_in, user } |
| GET | ?action=me | Bearer | None | The user object |
| POST | ?action=logout | Bearer | None | data: null; revokes the login token used |
| POST | ?action=verify_otp_email | Bearer | otp (6 digits) | { user }, marks the email verified |
| POST | ?action=resend_verification | Bearer | None | message |
| POST | ?action=change_password | Bearer | current_password, new_password (min 8) | message |
| POST | ?action=forgot_password | None | email | Always a generic message; emails a 6-digit sign-in code if the account exists (max 5 per hour) |
| POST | ?action=verify_otp_signin | None | email, otp | { token, expires_in, user } |
Login example
curl -s -X POST "https://cms.interspacemusic.com/api/mobile_auth.php?action=login" \
-H "Content-Type: application/json" \
-d '{"email":"label@example.com","password":"...","device":"catalog-sync"}'
Login errors: 422 VALIDATION, 401 INVALID_CREDENTIALS, 403 REQUIRES_2FA, 403 EMAIL_NOT_VERIFIED.
Artists
/api/artists.php. Artists are the primary
artist profiles on your account. Create the artist first:
a release needs a primary_artist_id. The number
of artists depends on your plan (Label plan: unlimited).
/api/artists.php?q=&page=1&limit=20{
"ok": true,
"data": [
{ "id": 812, "artist_name": "Ada Okafor", "artist_photo": "https://...", "platforms": ["spotify", "apple"] }
],
"total": 1, "page": 1, "limit": 20, "total_pages": 1
}
/api/artists.php?id=812Returns id, artist_name, artist_photo, platforms (object of platform to profile URL), albums[] with album_count, and tracks[] with track_count.
/api/artists.php (form) with action=quick_create or action=createartist_name(required, max 255, must be unique on your account)artist_photo(optional file: JPG, PNG or WebP, max 8 MB; a generated avatar is used otherwise)action=createalso accepts parallel arraysplatform_name[]andplatform_url[]for store profile links
curl -s -X POST "https://cms.interspacemusic.com/api/artists.php" \
-H "Authorization: Bearer $INTERSPACE_TOKEN" \
-F action=quick_create \
-F artist_name="Ada Okafor" \
-F artist_photo=@./ada.jpg
{ "ok": true, "data": { "id": 812, "artist_name": "Ada Okafor", "artist_photo": "https://...", "manager_id": 77 } }
Errors: 422 DUPLICATE, 403 ARTIST_LIMIT_REACHED, 422 INVALID_PHOTO.
Other artist actions (POST, form)
| action | Fields | Returns |
|---|---|---|
update_photo | artist_id, artist_photo (file) | { artist_photo } |
save_platform_link | artist_id, platform (e.g. spotify, apple, youtube, audiomack), url (must be on that store's domain; empty to clear) | { platform, url } |
Releases
/api/releases.php. A release (Single, EP or
Album) moves through these statuses:
DRAFT (editable, private) →
PENDING (submitted, under review) →
APPROVED (live / delivered) or
REJECTED (the rejection reason is returned in
rejection_reason; a rejected release can be
edited again once review reopens it).
All writes are POST /api/releases.php with a
form body whose action field selects the
operation. Typical flow:
create draft →
add tracks →
set schedule, territories and stores →
submit.
/api/releases.php?q=&status=&page=1&limit=20q searches titles; status filters (DRAFT, PENDING, APPROVED, REJECTED). Newest first.
{
"ok": true,
"data": [
{
"id": 9041,
"release_title": "No Balance",
"album_cover_photo": "https://...",
"status": "APPROVED",
"date_of_release": "2026-10-10",
"album_slug": "...",
"upc_code": "1234567890123",
"created_at": "2026-09-20 11:04:12",
"genre_name": "Afrobeats",
"track_count": 1
}
],
"total": 1, "page": 1, "limit": 20, "total_pages": 1
}
/api/releases.php?id=9041
Returns the full release: title, type, version, label,
status, rejection_reason, upc_code,
genres, copyright lines (p_copyright_*,
c_copyright_*), date_of_release,
schedule_type, territory_mode,
territory_countries, itunes_price_tier,
distribution_platforms (comma-separated store
names), primary_artist_name,
smart_link, collaborators[]
(royalty split invitations), tracks[] (each with
credits, ISRC, other_collaborators and
splits), update_request_pending,
takedown_request_pending, and the documents
fields described under Review documents.
/api/releases.php (multipart) with action=save_draft_step1Required fields
title,release_type(Single|EP|Album)metadata_language,primary_genreprimary_artist_id(an artist on your account)p_copyright_year,p_copyright_owner(sound recording, ℗)c_copyright_year,c_copyright_owner(composition, ©)cover_artfile (JPG or PNG, square, at least 3000×3000 px, max 10 MB), orexisting_cover_urlwhen editing
Optional fields
release_id_edit: ID of an existing draft to update instead of creating a new onetitle_version,record_label,secondary_genre,upc(digits; leave empty to have one assigned)featured_artist,additional_artistsfeatured_artist_dsp,additional_artists_dsp: JSON object mapping artist name to store profile URLs, e.g.{"Kofi":{"spotify":"https://open.spotify.com/artist/..."}}performer_credits,other_credits: JSON arrayspreviously_released(yes|no) andprevious_release_date(YYYY-MM-DD)country_of_recording(ISO 3166-1 alpha-2)cover_art_ai_use(none|assisted|generated)
curl -s -X POST "https://cms.interspacemusic.com/api/releases.php" \
-H "Authorization: Bearer $INTERSPACE_TOKEN" \
-F action=save_draft_step1 \
-F title="No Balance" \
-F release_type=Single \
-F metadata_language=English \
-F primary_artist_id=812 \
-F primary_genre=Afrobeats \
-F p_copyright_year=2026 -F p_copyright_owner="Example Records" \
-F c_copyright_year=2026 -F c_copyright_owner="Example Publishing" \
-F cover_art=@./cover-3000.jpg
{ "ok": true, "data": { "id": 9041, "slug": "...", "status": "DRAFT", "title": "No Balance" } }
Errors: 422 VALIDATION, 422 COVER_NOT_SQUARE, 422 COVER_TOO_SMALL, 402 PLAN_RELEASE_LIMIT, 409 NOT_DRAFT (release is locked).
Other reads
| Request | Returns |
|---|---|
GET ?available_dsps=1 | { plan, platforms: [{ id, platform_name, platform_photo, sort_order, is_locked }] }. Use platform_name values when selecting stores; is_locked stores are not available on your plan. |
GET ?tracks=1&album_id=&q=&page= | Paginated tracks across your catalog, each with album_title, isrc_code, status. |
GET ?track_id=123 | A single track's full metadata. |
GET ?catalog=1&exclude_album=&q= | Up to 200 of your existing tracks (with credits and audio) for reuse on another release. |
Tracks
Tracks belong to a draft release and are managed through
POST /api/releases.php (form). Limits per
release: Single 3 tracks, EP 6, Album 30.
/api/releases.php with action=add_trackAudio (one of)
track_audiofile in the same request (MP3, WAV, FLAC, AAC/M4A, max 200 MB). WAV or FLAC masters are recommended.s3_keyreturned byget_presigned_urlafter a direct upload (best for large files, see below)existing_audio_urlof audio already on your account (for example from?catalog=1)
Required fields
album_id,track_titleprimary_genre,languagetrack_composer,track_producer: JSON arrays of full legal names, e.g.["Ada Okafor"]track_lyricist: JSON array, required unlesslyrics_type=instrumentaltrack_properties: JSON array with at least one ofremix,samples,compilation,alternate,special_genre,non_musical,ai(AI-generated content), or["none"]when none apply
Optional fields
track_version,isrc_code(leave empty to have one assigned),iswc_codeexplicit_content(1|0)lyrics_type(contains_lyricsdefault |instrumental),language_of_lyrics,lyricssecondary_genre,preview_start_timetrack_origin(original_workdefault |public_domain|cover_song)track_feat_artists,track_additional_artists(JSON arrays),track_feat_artists_dsp,track_additional_artists_dsp(JSON maps, as on releases)performer_credits,other_credits(JSON arrays)
curl -s -X POST "https://cms.interspacemusic.com/api/releases.php" \
-H "Authorization: Bearer $INTERSPACE_TOKEN" \
-F action=add_track -F album_id=9041 \
-F track_title="No Balance" -F primary_genre=Afrobeats -F language=English \
-F track_composer='["Ada Okafor"]' -F track_lyricist='["Ada Okafor"]' \
-F track_producer='["Kofi Mensah"]' -F track_properties='["none"]' \
-F track_audio=@./no-balance.wav
{ "ok": true, "data": { "id": 55102, "track_title": "No Balance", "track_audio": "https://...", "isrc_code": null, "sort_order": 1, ... } }
Errors: 422 TRACK_LIMIT, 422 INVALID_AUDIO, 422 AUDIO_TOO_LARGE, 409 NOT_DRAFT.
Direct upload for large audio
POST action=get_presigned_urlwithalbum_id,filename,mime_type. Returns{ presigned_url, s3_key, s3_url }; the URL is valid for 15 minutes.PUTthe raw file bytes topresigned_urlwith the sameContent-Type(no Authorization header).- Call
add_track(orupdate_track) withs3_keyinstead of a file.
Other track actions (POST, form)
| action | Fields | Returns |
|---|---|---|
update_track | track_id plus the same fields as add_track (audio optional; omit to keep the current file) | The updated track |
delete_track | track_id | { deleted: true } |
reorder_tracks | album_id, orders: JSON [{"id":55102,"sort_order":1}, ...] | { reordered: n } |
Distribution & Submission
Before a draft can be submitted it needs at least one track
with audio, a release date and at least one store. All
actions are POST /api/releases.php (form).
/api/releases.php with action=save_distributionalbum_idschedule_type:scheduled(withrelease_dateYYYY-MM-DD, at least 5 days out on paid plans, 16 on Free) orasap(rush release; a rush fee may apply)territory_mode:worldwide(default) |include|exclude, withterritory_countriesas a JSON array of ISO country codes for include/excludeitunes_price_tier:back|mid(default) |front|premiumplatforms: JSON array of store names fromGET ?available_dsps=1, e.g.["Spotify","Apple Music"]
{ "ok": true, "data": {
"date_of_release": "2026-10-24", "schedule_type": "scheduled", "asap_fee_pending": false,
"territory_mode": "worldwide", "territory_countries": [], "itunes_price_tier": "mid",
"platforms": ["Spotify", "Apple Music"]
} }
save_schedule (album_id, schedule_type, release_date) and
save_dsps (album_id, platforms) update just one part.
Stores not on your plan return 403 PLAN_LOCKED or are dropped (422 NO_VALID_DSPS if none remain).
/api/releases.php with action=submit_release, album_id{ "ok": true, "data": { "id": 9041, "status": "PENDING", "title": "No Balance", "payment_ref": null } }
Errors: 409 ALREADY_SUBMITTED, 422 NO_TRACKS,
422 NO_AUDIO (lists the tracks missing a master),
422 NO_DSPS, 422 NO_SCHEDULE,
402 PLAN_RELEASE_LIMIT, 403 ACCOUNT_UNDER_REVIEW.
Once submitted the release is locked; poll
GET ?id= and watch status and
rejection_reason.
Changes after approval (POST, form)
| action | Fields | Notes |
|---|---|---|
request_update | album_id, reason (max 255), optional notes | Asks our team to reopen an APPROVED release for edits. Returns { request_id, request_type, status: "pending" }. |
request_takedown | album_id, reason, optional notes | Requests removal of a live release from stores. 409 ALREADY_PENDING if one is open. |
Review documents
During review we may ask for supporting documents (licenses,
split sheets, proof of ownership). The release stays
PENDING and GET ?id= returns
docs_open: true, docs_requested_items,
docs_request_note, docs_deadline and
the documents[] already uploaded.
action=upload_document(multipart):album_id,document_type(master_use,mechanical,sample_clearance,cover_sync,distribution,split_sheet,proof_ownership,trademark,other),documentfile (JPG, PNG, WebP or PDF, max 10 MB), optionaldescription.action=submit_documents:album_id. Sends the uploaded set back to review.
File Uploads
Release artwork, track audio and artist photos can be sent
directly with the create/update calls above. Use the generic
upload endpoint when you want a hosted URL first (for
example to pass as existing_cover_url or
existing_audio_url).
/api/upload.php (multipart)| Field | Description |
|---|---|
file | Required. Images: JPG, PNG, WebP (max 10 MB). Audio: MP3, WAV, FLAC (max 100 MB; use the direct upload flow for larger masters). |
folder | Optional grouping: artists, avatars, releases, tracks. Default uploads. |
curl -s -X POST "https://cms.interspacemusic.com/api/upload.php" \
-H "Authorization: Bearer $INTERSPACE_TOKEN" \
-F folder=releases \
-F file=@./cover-3000.jpg
{ "ok": true, "data": { "url": "https://...", "mime": "image/jpeg", "size": 2483312 } }
Errors: 400 UPLOAD_ERROR, 413 FILE_TOO_LARGE, 415 UNSUPPORTED_MEDIA_TYPE, 502 STORAGE_ERROR.
Royalties
GET /api/royalties.php?action=.... Earnings
credited to your account from store reports, net of
commission and after any royalty splits. period
accepts all (default), 3m,
6m, 1y or a single month
YYYY-MM (statement period).
| action | Params | Returns |
|---|---|---|
summary (default) | period | { total_earned, total_streams, commission, country_count, dsp_count, available_balance } |
trend | period | Array of { statement_period, label, revenue, streams } per month |
breakdown | by = release | track | country | dsp, period | Rows of { label, code?, release?, track_count?, streams, revenue, gross, share_pct, rate_per_stream }, plus top-level total_revenue. code is the UPC (release) or ISRC (track). |
statements | none | Array of statement months: { statement_period, label, distributed_at, earned, gross, streams, dsp_count, country_count, line_count } |
history | page, limit (10 to 100) | Paginated ledger entries { id, amount, transaction_date, transaction_note, transaction_type, created_at } |
curl -s "https://cms.interspacemusic.com/api/royalties.php?action=breakdown&by=track&period=2026-08" \
-H "Authorization: Bearer $INTERSPACE_TOKEN"
{ "ok": true,
"data": [ { "label": "No Balance", "release": "No Balance", "code": "QZXXX2600001",
"streams": 18231, "revenue": 41.2733, "gross": 51.5916,
"share_pct": 63.4, "rate_per_stream": 0.0022639 } ],
"total_revenue": 65.1002 }
Analytics
GET /api/analytics.php?action=...&period=.
Streams and revenue aggregated from the same royalty data,
shaped for dashboards. period: 1m,
3m, 6m, 1y,
all (default) or YYYY-MM.
| action | Returns |
|---|---|
summary (default) | { total_streams, total_revenue, country_count, dsp_count, track_count, rate_per_stream, streams_change, revenue_change } (changes are percent versus the previous equal period) |
trend | Monthly { statement_period, label, short, revenue, streams } |
platforms | Per store { label, streams, revenue, share_pct, photo_url } plus total_revenue |
tracks | Top tracks by streams { title, release, isrc, streams, revenue, share_pct }; limit up to 20 (default 10) |
territories | Top countries { country, streams, revenue, share_pct }; limit up to 20 (default 8) |
albums | Top releases { title, cover_url, streams, revenue, share_pct }; limit up to 20 (default 8) |
Royalty Splits
/api/royalty_splits.php?action=.... Share a
percentage of an approved release's (or a single track's)
earnings with collaborators. Requires a paid plan. Write
actions take a JSON body; the action goes in the query string.
| Method | action | Body / params | Returns |
|---|---|---|---|
| GET | list | page, limit (max 50) | { splits: [...], stats: { total_count, pending_count, accepted_count, revoked_count, total_pct_given } } |
| GET | collab_list | none | Splits where you are the collaborator, with earned_amount, available and payout_history |
| POST | invite | first_name, last_name, email, split_percent (0.01 to 99.99), album_id or track_id, optional role_label, notes | { invite_id, uuid }. The collaborator is emailed an invitation. |
| POST | resend | uuid | message (pending invites only) |
| POST | revoke | uuid | Cancels a pending invite |
| POST | force_revoke | uuid, optional reason | Ends an accepted split; future earnings on that scope return to the release owner |
curl -s -X POST "https://cms.interspacemusic.com/api/royalty_splits.php?action=invite" \
-H "Authorization: Bearer $INTERSPACE_TOKEN" -H "Content-Type: application/json" \
-d '{"first_name":"Kofi","last_name":"Mensah","email":"kofi@example.com","split_percent":25,"album_id":9041,"role_label":"Producer"}'
Errors: 403 UPGRADE_REQUIRED, 422 EXCEEDS_100 (all splits on a scope cannot exceed 100%), 422 ALBUM_NOT_APPROVED, 409 DUPLICATE, 422 SELF_INVITE.
Wallet & Payouts
/api/wallet.php?action=.... Balances, ledger,
payout methods and withdrawals. Write actions take a form
body. Withdrawals require approved identity verification
(see KYC).
| Method | action | Fields | Returns |
|---|---|---|---|
| GET | overview | none | { balance, frozen, available, min_payout, total_earned, total_withdrawn, this_month, pending_withdrawal, recent_txns[], chart[], payout_methods_count, kyc: { status, approved } } |
| GET | txns | page, limit (max 50), type = credit | debit | Paginated ledger; pagination fields total, page, limit, pages |
| GET | payout_methods | none | { bank: [], wire: [], paypal: [] } |
| GET | withdrawals | page, limit | Paginated withdrawal requests with withdrawal_status, withdrawal_code, processed_at |
| POST | add_bank | bank_name, account_name, account_number, optional country, is_default | { id } (max 3 local accounts) |
| POST | add_wire | bank_name, account_name, account_number, swift_code, optional routing_no, bank_address, country, is_default | { id } (max 2) |
| POST | add_paypal | paypal_email, optional is_default | { id } (max 1) |
| POST | set_default | type = bank | wire | paypal, method_id | message |
| POST | delete_payout_method | payment_method = BANK | WIRE | PAYPAL, method_id | message; 422 METHOD_IN_USE while a withdrawal is pending |
| POST | withdraw | payment_method = BANK | WIRE | PAYPAL, and method_id of a saved method (or inline account_name, account_number, bank_name, swift_code / paypal_email), optional note | { code, status: "PENDING", amount, net_amount, fee_amount } |
A withdrawal always requests the full available balance
(balance minus any amount frozen by an open dispute); a 1%
processing fee is deducted. Errors: 422 BELOW_THRESHOLD
(under min_payout), 403 KYC_REQUIRED,
422 PENDING_EXISTS (one pending request at a time).
Notifications
/api/notifications.php. In-app notifications for release reviews, payouts, disputes and account events.
| Method | Body / params | Returns |
|---|---|---|
| GET | unread_only=1, page, limit | Array of { id, type, title, body, url, is_read, date_fmt, created_at } with pagination fields and unread_count |
| PATCH | JSON {"id":5} or {"all":true} | Marks as read; updated count |
| DELETE | JSON {"id":5} or {"all":true} (deletes read notifications only) | deleted count |
Release Campaigns
/api/campaigns.php. Marketing plans attached to
a release: goals, audience, channels, a checklist of assets
and a generated milestone timeline. Writes are POST with a
form body and an action field.
| Request | Fields | Returns |
|---|---|---|
GET | status (draft | active | paused | completed | archived), album_id, page, limit | Campaign list; pagination in meta: { total, page, limit } |
GET ?id= | Campaign with release, assets[], milestones[], progress | |
create_or_update_step | step 1 to 6 and campaign_id (0 or omitted with step 1 to create; then album_id and optional name). Step 2: goal_primary (streams, saves, followers, press, playlist_pitch, tour, multi), goals[], start_date, end_date. Step 3: territories[], segments[], languages[]. Step 4: channels[<type>]=1 for presave, smartlink, promo_card, nmf_email, social_post, press_blurb, video_teaser, discovery, qr_code, custom. Step 5: due_dates[<asset_id>]. Step 6: notes. | The campaign |
launch | campaign_id | Activates a draft and generates milestones and copy; milestones_generated |
set_status | campaign_id, status (active | paused | completed | archived) | { id, status } |
asset_toggle | asset_id, status (pending | in_progress | done | skipped) | { id, status, completed_at } |
milestone_toggle | milestone_id, status (pending | done | skipped) | { id, status, plan_health_score } |
regenerate_plan, regenerate_pitch, regenerate_ai_copy | campaign_id | Rebuilt milestones, playlist pitch text, or promotional copy |
toggle_reminders | campaign_id, mute (1 | 0) | { mute_reminders } |
delete | campaign_id | Deletes a draft campaign |
Toolbox Services
/api/toolbox.php. Promotion and rights
services. The action goes in the JSON body (or query string).
This endpoint uses its own response shape:
{ "ok": true|false, "message": "...", ...fields },
and returns validation failures as ok: false
with HTTP 200, so always check ok. Paid services
are purchased in the dashboard.
| action | Fields | Returns |
|---|---|---|
get_submissions | none | submissions[]: your last 30 service requests with status and delivery links |
get_pitches | none | pitches[]: your last 30 editorial pitches |
create_smartlink | album_id | { slug, url, existing }: a public smart link page for the release |
submit_service | service_key (ytclaim for a YouTube Official Artist Channel request; mvd with an active video subscription), album_id, email, notes; for ytclaim: yt_ownership_confirmed, yt_has_existing_channel, yt_channel_url, yt_channel_email, channel_name | { submission_id } |
submit_pitch (multipart) | album_id, genre, audience, why (min 20 chars), email, optional moods, about, bio, milestones, instagram, tiktok, twitter, epk, release_date, nmf_optin, questionnaire (JSON) | { pitch_id, nmf, blog }: pitch to InterSpace Daily and/or New Music Friday |
Disputes
/api/disputes.php. Rights claims or conflicts
raised against your releases. While a dispute is open, the
related earnings may be frozen (see frozen in
the wallet overview).
| Request | Fields | Returns |
|---|---|---|
GET ?action=list | Your disputes: { id, dispute_type, priority, status, claimant_name, dsp, revenue_frozen, response_deadline, takedown_applied, created_at, release_title, upc_code } | |
GET ?id= | One dispute with messages[] and its timeline | |
POST (form) action=counter or respond | id, counter_notice (min 20 chars), optional counter_evidence (one URL per line) | { status: "artist_responded" } |
POST (form) action=message | id, message | { message_id } |
Identity Verification (KYC)
/api/kyc.php. Identity verification is required
before the first withdrawal. It is not required to submit or
distribute releases.
| Method | Fields | Returns |
|---|---|---|
| GET | { status, submission }; status is not_submitted, pending, approved or rejected (with reject_reason) | |
GET ?action=docs | All of your submissions | |
| POST (multipart) | doc_type (nin, passport, drivers_licence, national_id, voters_card), doc_number, full_name, dob, country, address, optional nationality, issue_date, expiry_date; files front_file, selfie_file, and back_file for national ID, driver's licence and voter's card (JPG, PNG, WebP, HEIC or PDF, max 10 MB each) | { status: "pending", kyc_id } |
| PATCH (multipart) | Same as POST | Resubmits after a rejection |
Account holders must be at least 16. Errors include 409 ALREADY_APPROVED, 409 ALREADY_PENDING, 422 EXPIRED_DOC, 422 UNDERAGE.
Subscription
/api/subscriptions.php. Read your plan and billing history. Plans are purchased and changed in the dashboard.
| Request | Returns |
|---|---|
GET | Current plan: { plan, status, billing_cycle, currency, amount_paid, started_at, period_start, period_end, is_paid, is_period_expired } |
GET ?action=history | Billing records { id, invoice_ref, plan, billing_cycle, status, currency, amount_paid, amount_display, started_at, period_end } |
GET ?action=plans | Available plans with annual and monthly prices in your currency and is_current |
POST ?action=cancel | Cancels auto-renewal; access continues until period_end |
Music Videos
GET /api/videos.php. Read access to your music
video distributions. Creating, uploading and submitting
videos is currently done in the dashboard.
| Request | Returns |
|---|---|
GET ?status=&q=&page=&limit= | List of { id, video_title, video_type, primary_artist_name, thumbnail_url, video_url, video_duration_seconds, status, release_date, created_at, submitted_at }; pagination in meta (limit max 50) |
GET ?id= | One video with full metadata |
GET ?check_access=1 | Whether your account can currently create video distributions |
Notes & Best Practices
- Create the artist first, then the draft release, then tracks. Reuse artist IDs instead of creating duplicates.
- Send WAV or FLAC masters. Use the direct upload flow for files over 100 MB.
- Cover art must be a square JPG or PNG of at least 3000×3000 px; check this before uploading to avoid a
422. - Leave
upcandisrc_codeempty if you do not own codes; they are assigned for you. If you supply them, validate the format first (UPC 12 or 13 digits, ISRC 12 characters). - Select stores by the exact
platform_namestrings fromGET /api/releases.php?available_dsps=1. - Nothing leaves your account until
submit_release. PollGET /api/releases.php?id=for status changes, or read notifications. - Retry reads on
5xxwith backoff. Do not blindly retry writes such asadd_trackorwithdraw: check state first to avoid duplicates. - For bulk catalog delivery you can also send DDEX packages over SFTP, described below.
DDEX / SFTP Inbound Delivery
InterSpace accepts music releases via SFTP using the DDEX ERN (Electronic Release Notification) standard — the same format used by major distributors worldwide. This guide explains everything a label or rights-holder needs to know to deliver music to InterSpace for distribution to DSPs (Spotify, Apple Music, YouTube Music, etc.).
What is DDEX?
DDEX (Digital Data Exchange) is a global standard for sending music metadata to digital music services. It packages your release information (title, artist, ISRC, UPC, release date, territories, etc.) into a structured XML file that systems can read automatically. Think of it as a digital "delivery note" that travels alongside your audio files and artwork.
What is SFTP?
SFTP (SSH File Transfer Protocol) is a secure way to transfer
files over the internet — like a private, encrypted folder you can upload to.
It works with apps like FileZilla, Cyberduck,
or WinSCP (all free). Once connected, you simply drag and drop
your release folder into the /incoming/ directory.
How it works — step by step
- You receive your SFTP credentials from InterSpace (host, username, password)
- You connect to our SFTP server using FileZilla or any SFTP client
- You upload your release as a folder inside
/incoming/ - Our system automatically detects the upload within 5 minutes
- It validates your DDEX XML, audio files and artwork
- You receive a confirmation email (or an error email if something needs fixing)
- InterSpace reviews and QC-checks your delivery
- You receive a final approval or rejection email with details
- On approval, InterSpace pushes your release to the agreed DSPs
Connecting via SFTP
InterSpace will email you your credentials when your account is set up. Here is how to connect using the most popular SFTP clients.
Connection details
| Setting | Value |
|---|---|
| Host | sftp.interspacemusic.com |
| Port | 22 |
| Protocol | SFTP (not FTP or FTPS) |
| Username | Provided by InterSpace |
| Password | Provided by InterSpace |
| Upload folder | /incoming/ |
Using FileZilla (recommended for beginners)
- Download FileZilla from filezilla-project.org (free)
- Open FileZilla → click File → Site Manager
- Click New Site and enter a name like "InterSpace Delivery"
- Set Protocol to SFTP – SSH File Transfer Protocol
- Host:
sftp.interspacemusic.com| Port:22 - Logon Type: Normal → enter your username and password
- Click Connect
- Navigate to the
/incoming/folder on the right side - Drag your release folder from your computer into
/incoming/
Using command line (advanced)
# Connect
sftp your_username@sftp.interspacemusic.com
# Navigate to incoming folder
sftp> cd incoming
# Create a folder for your release
sftp> mkdir ArtistName_AlbumTitle_UPC_20260323
# Upload files
sftp> cd ArtistName_AlbumTitle_UPC_20260323
sftp> put metadata.xml
sftp> put *.wav
sftp> put artwork.jpg
# Done — type exit to close
sftp> exit
/incoming/. Do not place
files directly in /incoming/ — the system will not detect them.
Package Structure
Each release you upload must be a folder containing exactly three types of files: a DDEX XML metadata file, your audio files, and your cover artwork. The folder name should be descriptive and unique.
Required folder structure
incoming/
└── ArtistName_AlbumTitle_UPC_YYYYMMDD/ ← your release folder
├── metadata.xml ← DDEX ERN XML (required)
├── 01_TrackTitle.wav ← audio track 1 (WAV or FLAC)
├── 02_TrackTitle.wav ← audio track 2
├── 03_TrackTitle.wav ← audio track 3 ...
└── artwork.jpg ← cover art (3000×3000 min)
Naming your release folder
We recommend this naming pattern for your release folder:
ArtistName_AlbumTitle_UPC_ReleaseDate
Example:
Davido_TimelessDeluxe_5034567890123_20260401
File checklist
| File | Required | Format | Notes |
|---|---|---|---|
metadata.xml |
✅ Yes | DDEX ERN XML | Must be a valid DDEX 3.8.2 NewReleaseMessage |
| Audio files | ✅ Yes | WAV or FLAC | One file per track. 16-bit/44.1kHz minimum, 24-bit preferred |
| Cover artwork | ✅ Yes | JPEG or PNG | Minimum 3000×3000px, square, under 20MB |
DDEX XML Metadata Guide
The metadata.xml file tells InterSpace (and eventually the DSPs)
everything about your release — artist name, track listing, release date,
territories, pricing and more. Most professional DAWs and distribution tools
can generate this automatically. Below is a minimal example for a single
track release.
Minimal example (single)
<?xml version="1.0" encoding="UTF-8"?>
<NewReleaseMessage
xmlns="http://ddex.net/xml/ern/382"
MessageSchemaVersionId="ern/382"
LanguageAndScriptCode="en">
<MessageHeader>
<MessageThreadId>MSG-001</MessageThreadId>
<MessageId>MSG-001</MessageId>
<MessageSender>
<PartyId>YOUR_LABEL_ID</PartyId>
<PartyName><FullName>Your Label Name</FullName></PartyName>
</MessageSender>
<MessageRecipient>
<PartyId>INTERSPACE</PartyId>
</MessageRecipient>
<MessageCreatedDateTime>2026-03-23T10:00:00</MessageCreatedDateTime>
<MessageControlType>LiveMessage</MessageControlType>
</MessageHeader>
<ResourceList>
<SoundRecording>
<SoundRecordingType>MusicalWorkSoundRecording</SoundRecordingType>
<SoundRecordingId>
<ISRC>GBDUM7620001</ISRC>
</SoundRecordingId>
<ResourceReference>A1</ResourceReference>
<ReferenceTitle>
<TitleText>My Track Title</TitleText>
</ReferenceTitle>
<Duration>PT3M42S</Duration>
<SoundRecordingDetailsByTerritory>
<TerritoryCode>Worldwide</TerritoryCode>
<DisplayArtist>
<PartyName><FullName>Artist Name</FullName></PartyName>
<ArtistRole>MainArtist</ArtistRole>
</DisplayArtist>
<Genre><GenreText>Afrobeats</GenreText></Genre>
<ParentalWarningType>NotExplicit</ParentalWarningType>
<TechnicalSoundRecordingDetails>
<TechnicalResourceDetailsReference>T1</TechnicalResourceDetailsReference>
<AudioCodecType>WAV</AudioCodecType>
<File>
<URI>01_MyTrackTitle.wav</URI>
</File>
</TechnicalSoundRecordingDetails>
</SoundRecordingDetailsByTerritory>
</SoundRecording>
</ResourceList>
<ReleaseList>
<Release>
<ReleaseId>
<UPC>5034567890123</UPC>
</ReleaseId>
<ReleaseReference>R1</ReleaseReference>
<ReferenceTitle>
<TitleText>My Album Title</TitleText>
</ReferenceTitle>
<ReleaseDate>2026-04-01</ReleaseDate>
<ReleaseType>Single</ReleaseType>
<ReleaseResourceReferenceList>
<ReleaseResourceReference>A1</ReleaseResourceReference>
</ReleaseResourceReferenceList>
</Release>
</ReleaseList>
<DealList>
<ReleaseDeal>
<DealReleaseReference>R1</DealReleaseReference>
<Deal>
<DealTerms>
<CommercialModelType>SubscriptionModel</CommercialModelType>
<Usage><UseType>OnDemandStream</UseType></Usage>
<TerritoryCode>Worldwide</TerritoryCode>
<ValidityPeriod><StartDate>2026-04-01</StartDate></ValidityPeriod>
</DealTerms>
</Deal>
</ReleaseDeal>
</DealList>
</NewReleaseMessage>
Required XML fields
| Field | Where | Example | Notes |
|---|---|---|---|
ISRC | SoundRecording | GBDUM7620001 | One per track. 12 characters. |
UPC or ICPN | ReleaseId | 5034567890123 | Your release barcode (12-13 digits) |
TitleText | ReferenceTitle | My Album | Official release title |
FullName | DisplayArtist | Artist Name | As it should appear on stores |
GenreText | Genre | Afrobeats | Primary genre |
ReleaseDate | Release | 2026-04-01 | Format: YYYY-MM-DD |
ReleaseType | Release | Single / Album / EP | Must match actual content |
TerritoryCode | DealList | Worldwide | Or specific ISO country codes |
ParentalWarningType | SoundRecording | NotExplicit | NotExplicit, Explicit, or NoAdviceAvailable |
File URI | TechnicalDetails | 01_Track.wav | Must exactly match your audio filename |
Artwork Requirements
All DSPs (Spotify, Apple Music, etc.) have strict artwork rules. Your artwork must meet these standards before your release can be approved.
| Requirement | Spec |
|---|---|
| Minimum size | 3000 × 3000 pixels |
| Recommended size | 3000 × 3000 px (exactly) |
| Shape | Square — width must equal height |
| Format | JPEG (.jpg) or PNG (.png) |
| Colour mode | RGB (not CMYK) |
| Max file size | 20 MB |
| Filename | artwork.jpg or artwork.png (recommended) |
Content rules
- No explicit sexual imagery
- No third-party logos, social media icons or watermarks
- No URLs or website addresses
- Must not be pixelated, blurry or stretched
- Text must be legible at small sizes
- Must not mislead consumers about the artist or content
Audio File Requirements
Your audio files must be high-quality, uncompressed masters. Do not send MP3s — they will be flagged during validation.
| Requirement | Spec |
|---|---|
| Accepted formats | WAV, FLAC, AIFF |
| Not accepted | MP3, AAC, OGG (will be flagged) |
| Sample rate | 44.1kHz or 48kHz |
| Bit depth | 16-bit minimum, 24-bit preferred |
| Channels | Stereo (2 channels) |
| Silence at start/end | Maximum 1 second |
| Clipping | Not permitted — master to -0.3 dBTP max |
| Filename | Must match the <URI> in your DDEX XML |
Track ordering
We recommend prefixing your audio filenames with their track number for clarity:
01_OpeningTrack.wav
02_SecondTrack.wav
03_ThirdTrack.wav
The filename must exactly match the <URI> value
in your DDEX XML — including capitalisation and spaces.
Delivery Status Explained
After you upload a package, you will receive emails at each stage. Here is what each status means:
| Status | Meaning | What happens next |
|---|---|---|
| Incoming | Your files have been uploaded and detected | Automatic validation begins within 5 minutes |
| Needs QC | Validation passed — awaiting InterSpace review | Our team reviews within 1–2 business days |
| Invalid | Validation failed — errors found in your package | You receive an email listing exactly what to fix. Re-upload the corrected package. |
| QC Approved | Passed review — ready for DSP delivery | InterSpace pushes your release to the agreed DSPs |
| QC Rejected | Failed review — see notes from InterSpace team | You receive detailed notes. Fix and re-upload. |
| Delivered to DSPs | Release has been submitted to DSPs | Stores typically go live within 1–7 business days |
Frequently Asked Questions
Still have questions? Email us at support@interspacemusic.com or visit our Glossary for definitions of music industry terms.