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://…"
}| Field | Meaning |
|---|---|
label | Short label shown in the app, v2, v3, … by default. null on the original upload |
name | Display name, the uploaded filename unless renamed |
order | Position in the history; the original upload is 0 |
active | true for the file the track currently uses. Derived, not stored: it is the version whose file the track points at |
visible | Hidden versions stay in the history but are not shown in the app |
cid | Content hash of the audio bytes, when the file has been analysed |
downloadUrl | Pre-signed link to the original file, valid for one hour. Fetch it fresh rather than storing it |
List versions
GET /api/v1/tracks/{trackId}/versionsScope read:tracks. Oldest first.
{
"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-dataScope update:tracks. Fields:
| Field | Type | Notes |
|---|---|---|
file | binary | required; audio/*, up to 1 GB |
name | string | display name, defaults to the filename |
label | string | defaults to v<n> where n is the position in the history |
activate | boolean | default 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/urlScope update:tracks. Same rules as Upload by URL:
the URL must be public, http or https, and serve audio/* or
application/octet-stream.
{
"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}/activateScope update:tracks.
{ "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.
| Field | Type |
|---|---|
name | string, up to 200 characters |
label | string, up to 80 characters |
visible | boolean |
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.
{ "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
| Status | messageCode | Meaning |
|---|---|---|
| 404 | track_not_found | No such track for this key's owner |
| 404 | version_not_found | The version is not on this track |
| 409 | active_version | Tried to delete the active version |
| 400 | validation_failed | Bad body, or a URL without a protocol |