Postal Developers
REST API

Track versions

Replace a track's audio while keeping its ID, and read, switch, label or delete the versions in its history.

A track keeps every audio file that was ever uploaded to it as a version. Exactly one version is active: the file the track plays, analyses, shares and sells. Uploading a new version is how you replace a track's audio without losing its ID, tags, playlists, share links or analytics.

All routes live under /tracks/{trackId}/versions and only work for tracks the key owner owns. Reading needs read:tracks; every change needs update:tracks.

The version object

{
  "id": "cm2v…",
  "trackId": "cm1x…",
  "label": "v2",
  "name": "alpha_master.wav",
  "order": 2,
  "active": true,
  "visible": true,
  "type": "audio/wav",
  "size": 48213904,
  "cid": "bafy…",
  "createdAt": "2026-09-07T10:12:00.000Z",
  "downloadUrl": "https://…"
}
FieldMeaning
labelShort label shown in the app, v2, v3, … by default. null on the original upload
nameDisplay name, the uploaded filename unless renamed
orderPosition in the history; the original upload is 0
activetrue for the file the track currently uses. Derived, not stored: it is the version whose file the track points at
visibleHidden versions stay in the history but are not shown in the app
cidContent hash of the audio bytes, when the file has been analysed
downloadUrlPre-signed link to the original file, valid for one hour. Fetch it fresh rather than storing it

List versions

GET /api/v1/tracks/{trackId}/versions

Scope read:tracks. Oldest first.

data
{
  "trackId": "cm1x…",
  "activeVersionId": "cm2v…",
  "count": 2,
  "versions": [ { "id": "cm1v…", "label": null, "active": false,  }, { "id": "cm2v…", "label": "v2", "active": true,  } ]
}

Get a track also returns a lighter versions summary (id, label, name, active, visible, createdAt) and activeVersionId on the track itself, so a sync job can spot a changed track without a second call.

Get one version

GET /api/v1/tracks/{trackId}/versions/{versionId}

Scope read:tracks. Returns one version object. 404 version_not_found if the version does not belong to that track.

Add a version from a file

POST /api/v1/tracks/{trackId}/versions
Content-Type: multipart/form-data

Scope update:tracks. Fields:

FieldTypeNotes
filebinaryrequired; audio/*, up to 1 GB
namestringdisplay name, defaults to the filename
labelstringdefaults to v<n> where n is the position in the history
activatebooleandefault true
curl -X POST "https://api.postal.music/api/v1/tracks/cm1x…/versions" \
  -H "X-API-Key: $POSTAL_API_KEY" \
  -F "file=@alpha_master.wav" \
  -F "label=master" \
  -F "activate=true"

Returns the new version object.

Add a version from a URL

POST /api/v1/tracks/{trackId}/versions/url

Scope update:tracks. Same rules as Upload by URL: the URL must be public, http or https, and serve audio/* or application/octet-stream.

Request
{
  "fileUrl": "https://cdn.example.com/audio/alpha_master.wav",
  "label": "master",
  "activate": true
}

Returns the new version object.

What activating does

Whether through activate: true on upload or the endpoint below, making a version active re-points the track at that file. On upload this also resets and re-runs analysis (waveform, tags, audio profile) for the new audio, and the app notifies the owner that the audio was replaced. Switching to an existing version only swaps the file and, if needed, transcodes a playback rendition; analysis results stay as they were.

Anything that references the track keeps working: playlists, share links, storefront listings and analytics all follow the track ID, not the file.

Activate a version

POST /api/v1/tracks/{trackId}/versions/{versionId}/activate

Scope update:tracks.

data
{ "ok": true, "trackId": "cm1x…", "activeVersionId": "cm1v…" }

Rename, relabel or hide

PATCH /api/v1/tracks/{trackId}/versions/{versionId}

Scope update:tracks. All fields optional; only the fields you send change.

FieldType
namestring, up to 200 characters
labelstring, up to 80 characters
visibleboolean

Returns the updated version object.

Delete a version

DELETE /api/v1/tracks/{trackId}/versions/{versionId}

Scope update:tracks. Removes the version and its audio file permanently.

data
{ "ok": true, "trackId": "cm1x…", "deletedVersionId": "cm1v…" }

The active version cannot be deleted: the request fails with 409 active_version. Activate another version first, then delete.

Errors

StatusmessageCodeMeaning
404track_not_foundNo such track for this key's owner
404version_not_foundThe version is not on this track
409active_versionTried to delete the active version
400validation_failedBad body, or a URL without a protocol

On this page