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

Production API
https://cms.interspacemusic.com/api/

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:

CredentialHow to get itLifetime
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.

Server-to-server only
The API does not send CORS headers for third-party origins, so browser JavaScript on your own domain cannot call it directly. Proxy requests through your backend.

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) or application/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/json with 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" }
HTTPMeaningTypical codes
400Unknown action or malformed requestBAD_ACTION, INVALID_JSON
401Missing, expired or revoked tokenUNAUTHENTICATED
402Plan limit reached or payment requiredPLAN_RELEASE_LIMIT, PAYMENT_REQUIRED
403Not allowed for this account or planPLAN_LOCKED, ARTIST_LIMIT_REACHED, KYC_REQUIRED
404Not found, or not owned by your accountNOT_FOUND
405Wrong HTTP method for this endpointMETHOD_NOT_ALLOWED
409Conflicts with the current state (for example the release is no longer a draft)NOT_DRAFT, ALREADY_SUBMITTED, ALREADY_PENDING
413 / 415File too large / unsupported file typeFILE_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE
422Validation failed; error explains which fieldVALIDATION and field-specific codes
429Rate limitedRATE_LIMITED
5xxServer or storage error; safe to retry idempotent readsDB_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.

POST
Agent Signup
POST 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 with 422 AGENT_NAME_REQUIRED. Can also be sent as an X-Agent-Name header.
  • 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.

POST
MCP endpoint
POST 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.

MethodActionAuthBodyReturns
POST?action=loginNoneemail, password, optional device (label for the token){ token, expires_in, user }
POST?action=signupNoneSee Account Creation{ token, expires_in, user }
GET?action=meBearerNoneThe user object
POST?action=logoutBearerNonedata: null; revokes the login token used
POST?action=verify_otp_emailBearerotp (6 digits){ user }, marks the email verified
POST?action=resend_verificationBearerNonemessage
POST?action=change_passwordBearercurrent_password, new_password (min 8)message
POST?action=forgot_passwordNoneemailAlways a generic message; emails a 6-digit sign-in code if the account exists (max 5 per hour)
POST?action=verify_otp_signinNoneemail, 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).

GET
List artists
GET /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
}
GET
Get one artist
GET /api/artists.php?id=812

Returns id, artist_name, artist_photo, platforms (object of platform to profile URL), albums[] with album_count, and tracks[] with track_count.

POST
Create artist
POST /api/artists.php (form) with action=quick_create or action=create
  • artist_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=create also accepts parallel arrays platform_name[] and platform_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)

actionFieldsReturns
update_photoartist_id, artist_photo (file){ artist_photo }
save_platform_linkartist_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.

GET
List releases
GET /api/releases.php?q=&status=&page=1&limit=20

q 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
}
GET
Get one release (with tracks)
GET /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.

POST
Create or edit a draft release
POST /api/releases.php (multipart) with action=save_draft_step1

Required fields

  • title, release_type (Single | EP | Album)
  • metadata_language, primary_genre
  • primary_artist_id (an artist on your account)
  • p_copyright_year, p_copyright_owner (sound recording, ℗)
  • c_copyright_year, c_copyright_owner (composition, ©)
  • cover_art file (JPG or PNG, square, at least 3000×3000 px, max 10 MB), or existing_cover_url when editing

Optional fields

  • release_id_edit: ID of an existing draft to update instead of creating a new one
  • title_version, record_label, secondary_genre, upc (digits; leave empty to have one assigned)
  • featured_artist, additional_artists
  • featured_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 arrays
  • previously_released (yes | no) and previous_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

RequestReturns
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=123A 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.

POST
Add a track
POST /api/releases.php with action=add_track

Audio (one of)

  • track_audio file in the same request (MP3, WAV, FLAC, AAC/M4A, max 200 MB). WAV or FLAC masters are recommended.
  • s3_key returned by get_presigned_url after a direct upload (best for large files, see below)
  • existing_audio_url of audio already on your account (for example from ?catalog=1)

Required fields

  • album_id, track_title
  • primary_genre, language
  • track_composer, track_producer: JSON arrays of full legal names, e.g. ["Ada Okafor"]
  • track_lyricist: JSON array, required unless lyrics_type=instrumental
  • track_properties: JSON array with at least one of remix, 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_code
  • explicit_content (1 | 0)
  • lyrics_type (contains_lyrics default | instrumental), language_of_lyrics, lyrics
  • secondary_genre, preview_start_time
  • track_origin (original_work default | 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

  1. POST action=get_presigned_url with album_id, filename, mime_type. Returns { presigned_url, s3_key, s3_url }; the URL is valid for 15 minutes.
  2. PUT the raw file bytes to presigned_url with the same Content-Type (no Authorization header).
  3. Call add_track (or update_track) with s3_key instead of a file.

Other track actions (POST, form)

actionFieldsReturns
update_tracktrack_id plus the same fields as add_track (audio optional; omit to keep the current file)The updated track
delete_tracktrack_id{ deleted: true }
reorder_tracksalbum_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).

POST
Set schedule, territories and stores
POST /api/releases.php with action=save_distribution
  • album_id
  • schedule_type: scheduled (with release_date YYYY-MM-DD, at least 5 days out on paid plans, 16 on Free) or asap (rush release; a rush fee may apply)
  • territory_mode: worldwide (default) | include | exclude, with territory_countries as a JSON array of ISO country codes for include/exclude
  • itunes_price_tier: back | mid (default) | front | premium
  • platforms: JSON array of store names from GET ?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).

POST
Submit for review
POST /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)

actionFieldsNotes
request_updatealbum_id, reason (max 255), optional notesAsks our team to reopen an APPROVED release for edits. Returns { request_id, request_type, status: "pending" }.
request_takedownalbum_id, reason, optional notesRequests 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), document file (JPG, PNG, WebP or PDF, max 10 MB), optional description.
  • 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).

POST
Upload a file
POST /api/upload.php (multipart)
FieldDescription
fileRequired. Images: JPG, PNG, WebP (max 10 MB). Audio: MP3, WAV, FLAC (max 100 MB; use the direct upload flow for larger masters).
folderOptional 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).

actionParamsReturns
summary (default)period{ total_earned, total_streams, commission, country_count, dsp_count, available_balance }
trendperiodArray of { statement_period, label, revenue, streams } per month
breakdownby = release | track | country | dsp, periodRows 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).
statementsnoneArray of statement months: { statement_period, label, distributed_at, earned, gross, streams, dsp_count, country_count, line_count }
historypage, 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.

actionReturns
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)
trendMonthly { statement_period, label, short, revenue, streams }
platformsPer store { label, streams, revenue, share_pct, photo_url } plus total_revenue
tracksTop tracks by streams { title, release, isrc, streams, revenue, share_pct }; limit up to 20 (default 10)
territoriesTop countries { country, streams, revenue, share_pct }; limit up to 20 (default 8)
albumsTop 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.

MethodactionBody / paramsReturns
GETlistpage, limit (max 50){ splits: [...], stats: { total_count, pending_count, accepted_count, revoked_count, total_pct_given } }
GETcollab_listnoneSplits where you are the collaborator, with earned_amount, available and payout_history
POSTinvitefirst_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.
POSTresenduuidmessage (pending invites only)
POSTrevokeuuidCancels a pending invite
POSTforce_revokeuuid, optional reasonEnds 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).

MethodactionFieldsReturns
GEToverviewnone{ balance, frozen, available, min_payout, total_earned, total_withdrawn, this_month, pending_withdrawal, recent_txns[], chart[], payout_methods_count, kyc: { status, approved } }
GETtxnspage, limit (max 50), type = credit | debitPaginated ledger; pagination fields total, page, limit, pages
GETpayout_methodsnone{ bank: [], wire: [], paypal: [] }
GETwithdrawalspage, limitPaginated withdrawal requests with withdrawal_status, withdrawal_code, processed_at
POSTadd_bankbank_name, account_name, account_number, optional country, is_default{ id } (max 3 local accounts)
POSTadd_wirebank_name, account_name, account_number, swift_code, optional routing_no, bank_address, country, is_default{ id } (max 2)
POSTadd_paypalpaypal_email, optional is_default{ id } (max 1)
POSTset_defaulttype = bank | wire | paypal, method_idmessage
POSTdelete_payout_methodpayment_method = BANK | WIRE | PAYPAL, method_idmessage; 422 METHOD_IN_USE while a withdrawal is pending
POSTwithdrawpayment_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.

MethodBody / paramsReturns
GETunread_only=1, page, limitArray of { id, type, title, body, url, is_read, date_fmt, created_at } with pagination fields and unread_count
PATCHJSON {"id":5} or {"all":true}Marks as read; updated count
DELETEJSON {"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.

RequestFieldsReturns
GETstatus (draft | active | paused | completed | archived), album_id, page, limitCampaign list; pagination in meta: { total, page, limit }
GET ?id=Campaign with release, assets[], milestones[], progress
create_or_update_stepstep 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
launchcampaign_idActivates a draft and generates milestones and copy; milestones_generated
set_statuscampaign_id, status (active | paused | completed | archived){ id, status }
asset_toggleasset_id, status (pending | in_progress | done | skipped){ id, status, completed_at }
milestone_togglemilestone_id, status (pending | done | skipped){ id, status, plan_health_score }
regenerate_plan, regenerate_pitch, regenerate_ai_copycampaign_idRebuilt milestones, playlist pitch text, or promotional copy
toggle_reminderscampaign_id, mute (1 | 0){ mute_reminders }
deletecampaign_idDeletes 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.

actionFieldsReturns
get_submissionsnonesubmissions[]: your last 30 service requests with status and delivery links
get_pitchesnonepitches[]: your last 30 editorial pitches
create_smartlinkalbum_id{ slug, url, existing }: a public smart link page for the release
submit_serviceservice_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).

RequestFieldsReturns
GET ?action=listYour 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 respondid, counter_notice (min 20 chars), optional counter_evidence (one URL per line){ status: "artist_responded" }
POST (form) action=messageid, 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.

MethodFieldsReturns
GET{ status, submission }; status is not_submitted, pending, approved or rejected (with reject_reason)
GET ?action=docsAll 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 POSTResubmits 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.

RequestReturns
GETCurrent plan: { plan, status, billing_cycle, currency, amount_paid, started_at, period_start, period_end, is_paid, is_period_expired }
GET ?action=historyBilling records { id, invoice_ref, plan, billing_cycle, status, currency, amount_paid, amount_display, started_at, period_end }
GET ?action=plansAvailable plans with annual and monthly prices in your currency and is_current
POST ?action=cancelCancels 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.

RequestReturns
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=1Whether 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 upc and isrc_code empty 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_name strings from GET /api/releases.php?available_dsps=1.
  • Nothing leaves your account until submit_release. Poll GET /api/releases.php?id= for status changes, or read notifications.
  • Retry reads on 5xx with backoff. Do not blindly retry writes such as add_track or withdraw: check state first to avoid duplicates.
  • For bulk catalog delivery you can also send DDEX packages over SFTP, described below.
SFTP

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

  1. You receive your SFTP credentials from InterSpace (host, username, password)
  2. You connect to our SFTP server using FileZilla or any SFTP client
  3. You upload your release as a folder inside /incoming/
  4. Our system automatically detects the upload within 5 minutes
  5. It validates your DDEX XML, audio files and artwork
  6. You receive a confirmation email (or an error email if something needs fixing)
  7. InterSpace reviews and QC-checks your delivery
  8. You receive a final approval or rejection email with details
  9. 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

SettingValue
Hostsftp.interspacemusic.com
Port22
ProtocolSFTP (not FTP or FTPS)
UsernameProvided by InterSpace
PasswordProvided by InterSpace
Upload folder/incoming/

Using FileZilla (recommended for beginners)

  1. Download FileZilla from filezilla-project.org (free)
  2. Open FileZilla → click File → Site Manager
  3. Click New Site and enter a name like "InterSpace Delivery"
  4. Set Protocol to SFTP – SSH File Transfer Protocol
  5. Host: sftp.interspacemusic.com  |  Port: 22
  6. Logon Type: Normal → enter your username and password
  7. Click Connect
  8. Navigate to the /incoming/ folder on the right side
  9. 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
⚠ Important: Each release must be uploaded as its own separate folder inside /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

FileRequiredFormatNotes
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

FieldWhereExampleNotes
ISRCSoundRecordingGBDUM7620001One per track. 12 characters.
UPC or ICPNReleaseId5034567890123Your release barcode (12-13 digits)
TitleTextReferenceTitleMy AlbumOfficial release title
FullNameDisplayArtistArtist NameAs it should appear on stores
GenreTextGenreAfrobeatsPrimary genre
ReleaseDateRelease2026-04-01Format: YYYY-MM-DD
ReleaseTypeReleaseSingle / Album / EPMust match actual content
TerritoryCodeDealListWorldwideOr specific ISO country codes
ParentalWarningTypeSoundRecordingNotExplicitNotExplicit, Explicit, or NoAdviceAvailable
File URITechnicalDetails01_Track.wavMust exactly match your audio filename
💡 Tip: Tools like Beets, SoundSystem, or your DAW's metadata export can generate DDEX XML automatically. If you use a distribution platform that supports DDEX export (e.g. DistroKid's label tier, TuneCore for Labels), you can export from there and send the package directly to us.

Artwork Requirements

All DSPs (Spotify, Apple Music, etc.) have strict artwork rules. Your artwork must meet these standards before your release can be approved.

RequirementSpec
Minimum size3000 × 3000 pixels
Recommended size3000 × 3000 px (exactly)
ShapeSquare — width must equal height
FormatJPEG (.jpg) or PNG (.png)
Colour modeRGB (not CMYK)
Max file size20 MB
Filenameartwork.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
⚠ Common rejection reason: Artwork that is 2000×2000 or lower will be automatically flagged. Always export at full 3000×3000px or above.

Audio File Requirements

Your audio files must be high-quality, uncompressed masters. Do not send MP3s — they will be flagged during validation.

RequirementSpec
Accepted formatsWAV, FLAC, AIFF
Not acceptedMP3, AAC, OGG (will be flagged)
Sample rate44.1kHz or 48kHz
Bit depth16-bit minimum, 24-bit preferred
ChannelsStereo (2 channels)
Silence at start/endMaximum 1 second
ClippingNot permitted — master to -0.3 dBTP max
FilenameMust 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:

StatusMeaningWhat 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

Do I need to know how to write XML to use DDEX delivery?
No. Most professional distribution tools (TuneCore for Labels, DistroKid label tier, Fuga, etc.) can export DDEX packages automatically. If you are a developer, our XML guide above includes a complete template to start from. Contact support@interspacemusic.com if you need help.
Can I use FileZilla or does it have to be command line?
FileZilla is perfectly fine and is what we recommend for non-technical users. You can also use Cyberduck (Mac/Windows), WinSCP (Windows), or Transmit (Mac). Any SFTP client works — just make sure to select SFTP (not FTP or FTPS).
What happens if my upload is rejected?
You will receive an email with a detailed list of what needs to be fixed. Fix the issues, create a new folder with a different name (e.g. append _v2) and re-upload it to /incoming/. The system will pick it up automatically.
How long does QC review take?
Automatic validation happens within 5 minutes of upload. Manual QC review by the InterSpace team takes 1–2 business days. You will always receive an email when the status changes.
Can I deliver an album or EP, not just singles?
Yes. Albums and EPs are fully supported. Include all audio files in the same release folder. Set <ReleaseType> to Album or EP in your XML and include one <SoundRecording> entry per track in <ResourceList>.
Do I need a UPC or ISRC? Where do I get them?
Yes — a UPC (release barcode) and at least one ISRC (track identifier) are required. InterSpace can provide these for you as part of your distribution agreement. Alternatively, you can obtain ISRCs from your national ISRC agency (e.g. PHONOGRAPHIC PERFORMANCE LIMITED in the UK, RIAA in the US) and UPCs from GS1 or a barcode provider.

Still have questions? Email us at support@interspacemusic.com or visit our Glossary for definitions of music industry terms.