Swayzio Core API v1 — base path /api/swayzio/v1
Core APITrack Resources

Track Resources

Everything that hangs off a single track — tags, peaks, lyrics, stems, versions, artwork and documents, the split sheet, share links, processing status, and metadata edits. All routes are owner‑scoped to the track’s owner; a track that isn’t yours returns 404 track_not_found. See Authentication.

Base path: /api/swayzio/v1. Runtime: native→proxy.

Sub‑resources are reached two ways:

  1. A generic dispatcher at /v1/tracks/:trackId/:resource that handles GET, PUT, and PATCH for a fixed set of resource names.
  2. Dedicated paths for actions that don’t fit the generic shape (signed URLs, uploads, version promotion, share links, processing).

The :resource dispatcher

GET /v1/tracks/:trackId/:resource reads a sub‑resource. Supported :resource values:

:resourceReturnsNotes
tagsTrackTagDetailResponse?includeHidden=true to include hidden tags
peaksTrackPeaksResponse404 peaks_not_found until a waveform exists
rights{ entries: [], status: { isComplete: false } }Stub — always empty (see below)
lyricsClientTrackLyricsResponseprovider/model fields redacted
stemsClientTrackStemsResponsestorageUri + playbackUrl stripped
versionsClientTrackVersionsResponseplaybackUrl stripped per version
media-assetsartwork + documents
split-sheetPublicTrackSplitSheetPayloadownerId omitted throughout

Writes go through the same dispatcher but only for specific resources:

Method:resourcePurposeRequestResponse
PUTlyricsReplace lyric textupdateTrackLyricsRequestSchemaClientTrackLyricsResponse
PUTsplit-sheetReplace the split sheetreplaceTrackSplitSheetRequestSchemaPublicTrackSplitSheetPayload
PATCHmetadataEdit core track fieldsupdateTrackMetadataRequestSchemaTrack

An unknown :resource returns 404 track_resource_not_found. A supported resource with an unsupported method returns 405 method_not_allowed. A PUT split-sheet whose entries don’t balance returns 422 split_sheet_structural_validation_failed.

⚠️

GET …/rights is a placeholder. It always returns { entries: [], status: { isComplete: false, validationErrors: [], validatedAt } } regardless of the track. The legacy per‑entry rights model (trackRightsEntrySchema) is not the live ownership surface — real rights data lives in the split sheet (GET/PUT …/split-sheet). See Two rights models.

PATCH /v1/tracks/:trackId/metadata

Edits the track’s user‑facing metadata. At least one field must be present.

Auth: Clerk actor.

Request: updateTrackMetadataRequestSchema — any of title, artist, isrc, iswc (nullable), releaseDate (YYYY-MM-DD, nullable). An empty body is rejected with 400.

Response: Track (the updated client DTO).

curl -s -X PATCH https://app.swayzio.com/api/swayzio/v1/tracks/trk_123/metadata \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Night Drive","artist":"AVR","releaseDate":"2026-05-01"}'

PUT /v1/tracks/:trackId/split-sheet

Replaces the track’s split sheet wholesale. The payload is a structured ownership document — master and writers are required (at least one each), publishers optional with nested sub‑publishers and collection splits. Shares are validated for structural balance server‑side.

Auth: Clerk actor.

Request: replaceTrackSplitSheetRequestSchema.

Response: PublicTrackSplitSheetPayload (owner ids stripped at every level). On a balance failure: 422 { "error": "split_sheet_structural_validation_failed", "issues": [...] }.

curl -s -X PUT https://app.swayzio.com/api/swayzio/v1/tracks/trk_123/split-sheet \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "address": "…",
        "intendedUse": "Sync licensing",
        "master": [{ "collaboratorId": "col_1", "percentage": 100 }],
        "writers": [{ "collaboratorId": "col_1", "percentage": 100 }],
        "publishers": []
      }'

Signed URLs & processing

MethodPathPurposeAuthRequestResponse
GET/v1/tracks/:trackId/playback-urlShort‑lived signed stream URLClerk actorClientPlaybackUrlResponse
GET/v1/tracks/:trackId/download-urlSigned download URL; records a track.downloaded eventClerk actor?assetId= (optional)downloadUrlResponseSchema
GET/v1/tracks/:trackId/processing-statusPer‑lane readinessClerk actorClientTrackProcessingStatus
POST/v1/tracks/:trackId/processing/media-intelligence/retryRe‑dispatch media intelligenceClerk actor202 / 200 / 404

playback-url strips the internal storageUri from the response; processing-status reports readiness lanes for playback, waveform, tags, stems, lyrics, embeddings so a client can poll until a track is fully usable. retry re‑queues the media‑intelligence pipeline (returns 202 when newly dispatched, 200 when already running, 404 if the track is unknown).

GET /v1/tracks/:trackId/processing-status

Returns the processing state for every lane plus an asset summary and readiness roll‑up — the canonical thing to poll after a finalize. Internal storage URIs are stripped from asset entries.

Auth: Clerk actor.

Response: clientTrackProcessingStatusSchema{ trackId, stages, assets, remoteDispatches, ingestionRun?, readiness }.

curl -s https://app.swayzio.com/api/swayzio/v1/tracks/trk_123/processing-status \
  -H "Authorization: Bearer $TOKEN"

Versions

A track can carry multiple audio versions; one is current. Promoting a version swaps which audio plays everywhere the track is referenced.

MethodPathPurposeAuthRequestResponse
POST/v1/tracks/:trackId/versions/uploadsCreate a version upload intent (audio only)Clerk actorcreateTrackVersionUploadRequestSchema201 clientCreateUploadResponseSchema
POST/v1/tracks/:trackId/versions/:versionId/promotePromote a version to currentClerk actorClientTrackVersionsResponse
DELETE/v1/tracks/:trackId/versions/:versionIdDelete a versionClerk actorClientTrackVersionsResponse

versions/uploads returns a signed‑PUT upload intent (same shape as Uploads); finalize it the same way. Deleting the original version returns 409 original_version_cannot_be_deleted.

POST /v1/tracks/:trackId/versions/uploads

Creates a signed upload intent for a new audio version of an existing track. Audio only — non‑audio content types are rejected at validation.

Auth: Clerk actor.

Request: createTrackVersionUploadRequestSchema{ fileName, contentType, byteSize }.

Response: 201 clientCreateUploadResponseSchema{ uploadId, trackId, assetId, signedUrl, method: "PUT", expiresAt, requiredHeaders, finalizeUrl }. PUT the bytes, then call the finalizeUrl. See the upload flow.

curl -s -X POST https://app.swayzio.com/api/swayzio/v1/tracks/trk_123/versions/uploads \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"night-drive-v2.wav","contentType":"audio/wav","byteSize":48211200}'

Artwork & documents

MethodPathPurposeAuthRequestResponse
POST/v1/tracks/:trackId/artwork/uploadsCover‑art upload intent (image only)Clerk actorcreateTrackArtworkUploadRequestSchema201 clientCreateUploadResponseSchema
POST/v1/tracks/:trackId/documents/uploadsDocument upload intent (PDF/office/text/image)Clerk actorcreateTrackDocumentUploadRequestSchema201 clientCreateUploadResponseSchema

Both return a signed‑PUT intent finalized the same way as audio uploads. Reads of the resulting assets come back through GET …/media-assets.

Private, tokenized links that grant playback of a single track without an account. Creating a link requires the tracks.share entitlement.

MethodPathPurposeAuthRequestResponse
GET/v1/tracks/:trackId/share-linksList links (redacted)Clerk actor{ records: ClientTrackShareLink[] }
POST/v1/tracks/:trackId/share-linksCreate a linkClerk actor + tracks.sharecreateTrackShareLinkRequestSchema201 TrackShareLinkCreateResponse
POST/v1/tracks/:trackId/share-links/:linkId/resetRotate the tokenClerk actor + tracks.sharelink + new token
DELETE/v1/tracks/:trackId/share-links/:linkIdRevoke a linkClerk actor204

The plaintext token is only ever returned once — at create and reset. Listing returns the redacted ClientTrackShareLink (no raw token, no Dub provider internals). See Public Share Links for the unauthenticated consumer side.

POST /v1/tracks/:trackId/share-links

Mints a private playback link. Returns the link record plus the one‑time plaintext token and a sharePath you can render directly.

Auth: Clerk actor + tracks.share entitlement (else 403 entitlement_required).

Request: createTrackShareLinkRequestSchema{ name?, expiresAt? } (expiresAt must be in the future).

Response: 201 trackShareLinkCreateResponseSchema — the link fields plus token and sharePath.

curl -s -X POST https://app.swayzio.com/api/swayzio/v1/tracks/trk_123/share-links \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"For the sync brief","expiresAt":"2026-12-31T00:00:00.000Z"}'
MethodPathPurposeAuthRequestResponse
GET/v1/tracks/:trackId/split-sheet/share-linksList split‑sheet review linksClerk actor{ records: [] } (stub natively)
POST/v1/tracks/:trackId/split-sheet/share-linksCreate a review linkClerk actorcreateTrackSplitSheetShareLinkRequestSchematoken + sharePath (proxy‑only)
DELETE/v1/tracks/:trackId/split-sheet/share-links/:linkIdRevoke a linkClerk actorrevoked (proxy‑only)
⚠️

Split‑sheet share links are not fully native yet. The native web surface serves GET as a stub that always returns { "records": [] }. Create and delete are proxy‑only — they run only against the Fastify Core API (createTrackSplitSheetShareLinkRequestSchema → token + sharePath; DELETE revokes). Don’t rely on these natively until the flag flips.

Two rights models

There are two distinct ways rights show up, and only one is live:

  • Legacy per‑entry rightstrackRightsEntrySchema, a flat list of collaborator entries with mechanical/performance/sync splits. This backs the GET …/rights route, which is currently a stub that always returns empty.
  • Split sheet (live) — the structured, discriminated document under GET/PUT …/split-sheet (replaceTrackSplitSheetRequestSchema / PublicTrackSplitSheetPayload). This is the authoritative ownership surface today.

When you need real ownership data, use the split sheet — not /rights.