The desktop backend exposes a local FastAPI API under /api/v1. It is intended for the bundled TuneForge frontend on the same machine and binds to 127.0.0.1 by default.
This document summarizes the current route surface. The generated OpenAPI schema in packages/shared-types/openapi.json remains the implementation source of truth for exact schemas and generated TypeScript types.
- Base path:
/api/v1 - Request and response format: JSON unless an endpoint streams a file.
- IDs: string identifiers generated by the backend.
- Timestamps: ISO 8601 strings in API responses.
- Errors use a structured
errorobject withcode,message, anddetails.
Example error response:
{
"error": {
"code": "PROJECT_NOT_FOUND",
"message": "Project not found.",
"details": {}
}
}Jobs represent asynchronous work such as analysis, chords, lyrics, transforms, stems, previews, and exports.
Important fields:
idproject_idtypestatusprogresssource_artifact_idchord_backendchord_sourceerror_messageruntime_devicecreated_atupdated_at
Artifacts represent generated or imported files associated with a project.
Important fields:
idproject_idtypeformatpathsize_bytesgenerated_bycan_deletecan_regeneratemetadatacreated_at
GET /api/v1/health
Returns backend name, legacy backend git ref in version, backend/frontend package versions and git refs, status,
API base URL, data root, default export format, and preview format. Build git refs use the packaged build
metadata when available, otherwise local development resolves them with git describe --tags --long --dirty --always.
GET /api/v1/chord-backends
Returns available chord backends and capability metadata. Built-in chords are always expected to be available. Advanced Chords may be unavailable when optional desktop-only dependencies are not installed or the runtime platform is mobile.
GET /api/v1/stem-models
Returns supported stem models and availability metadata. Labels are Default (6 stems model) for htdemucs_6s and 2 stems model for htdemucs_ft.
The sync API exposes local sync metadata, identity, and trust management for the bundled desktop app. FastAPI remains bound to loopback; LAN transport, peer discovery, QR scanning, and file transfer belong to the separate native sync layer.
device_id is the stable identity for one TuneForge install. sync_group_id identifies the sync group that
trusted devices may share. Display names are editable convenience labels and must not be treated as identity.
GET /api/v1/sync/identity
Returns the local sync identity, creating it if it does not exist yet.
Response wrapper:
identity
Identity fields:
device_idsync_group_iddisplay_name- nullable convenience label.public_keycreated_at- nullable when the service returns an identity DTO without persistence metadata.updated_at- nullable when the service returns an identity DTO without persistence metadata.
PATCH /api/v1/sync/identity
Updates the local identity display name.
Request fields:
display_name
Response wrapper:
identity
Changing display_name does not change device_id, sync_group_id, or public identity material.
POST /api/v1/sync/pairing/offers
Creates a short-lived manual pairing offer for copy/paste style pairing. The endpoint does not start LAN discovery, expose FastAPI beyond loopback, or create QR UI.
Request fields:
endpoint_hints- optional advisory peer endpoints, default[].ttl_seconds- pairing offer lifetime in seconds, default600, maximum3600.
Response wrapper:
pairing_offer
Pairing offer fields:
payloadexpires_atttl_seconds
The payload is the portable value another device submits inside the payload field of
POST /api/v1/sync/trusted-peers. For this backend-first slice, a peer trust request must carry
the pairing_offer_id and pairing_secret from a pending local offer so this install can verify
the manual exchange before storing trust. It includes:
protocol_versionpairing_offer_idsync_group_iddevice_iddisplay_namepublic_keyendpoint_hintspairing_secretexpires_atsignature
Pairing payloads are signed by the source device's private identity key so copied payloads are
tamper-evident. Endpoint hints are advisory and do not authenticate a peer. The receiving install
must still explicitly trust the payload before accepting manifests, revisions, tombstones, or
artifact bytes from that device. The payload submitted to this endpoint must reference a locally
issued pairing_offer_id; that local offer must still be unused, unexpired, and match the
payload's pairing_secret.
GET /api/v1/sync/trusted-peers
Returns active peers this install explicitly trusts. Revoked peers are not returned by this list.
Response wrapper:
trusted_peers
Trusted peer fields:
device_idsync_group_iddisplay_namepublic_keyendpoint_hintstrusted_atrevoked_atupdated_at
Trust is explicit and non-transitive. Joining the same sync_group_id, discovering a device, or hearing about
a peer from another trusted device does not automatically trust that peer.
POST /api/v1/sync/trusted-peers
Stores trust for one peer from a manual pairing payload.
Request fields:
payload- pairing payload copied from another device.adopt_sync_group- whether this install should adopt the payload'ssync_group_id, defaultfalse.
Response wrapper:
trusted_peer
Trusting a peer records its device_id, public identity, display name, endpoint hints, and sync group. The
peer can later be revoked without deleting local projects or changing this install's own device_id.
Standalone payloads that do not reference a local pending offer are rejected.
DELETE /api/v1/sync/trusted-peers/{device_id}
Revokes trust for the peer with the matching device_id.
Response wrapper:
trusted_peer
Revocation stops this install from accepting future sync material from that peer unless it is explicitly paired again. Revocation is local to this install and does not revoke trust transitively on any other device.
GET /api/v1/sync/preflight
Checks the local library before multi-device sync is enabled. The endpoint returns HTTP 200 for both passing and failing preflight results because failures are actionable diagnostics rather than request errors.
The response includes:
ok- project status counts
- per-project sync identity status
- duplicate source-hash groups
- manual cleanup guidance
Project status values are ready, missing_source_hash, invalid_source_hash, duplicate_source_hash, and noncanonical_project_id. Canonical project IDs use proj_sha256_<full_source_sha256>, while project storage directories use a shorter derived key such as proj_<first_24_sha256_hex>. This endpoint is sync-specific; general project responses do not expose source hashes.
GET /api/v1/sync/metadata
Returns sync-safe project and artifact metadata so sync clients do not need to read the local SQLite database directly. The endpoint does not expose absolute local file paths.
Project fields:
project_iddisplay_namesource_key_overridesource_sha256duration_secondssample_ratechannelscreated_atupdated_at
Artifact fields:
artifact_idproject_idtypeformatrelative_pathcontent_sha256size_bytesgenerated_bycan_deletecan_regeneratecache_keymetadatacreated_at
relative_path is project-root relative when the artifact is stored under the backend-managed project root; otherwise it is null. Artifact metadata is sanitized recursively before returning and removes local path-bearing keys such as source_path, original_copy_path, playback_path, imported_path, and any metadata key ending in _path. Non-path metadata such as retune/transpose settings, stem_model, and source_artifact_id is preserved. Job internals such as result_artifact_ids_json are not exposed.
GET /api/v1/sync/projects/{project_id}/manifest
Returns a single project manifest for manifest-only sync export. The response is wrapped as project_manifest and includes:
schema_versionexported_atprojectartifacts
Manifest paths are project-root relative and use portable path separators. The response does not expose absolute local source, project, artifact, or app data paths. Artifact entries include content_sha256, and the project entry includes source_sha256; receiving peers must verify staged bytes against these SHA-256 values before accepting the import. For this v1 manifest spike, a portable manifest must contain exactly one source_audio artifact, and that artifact must match the project source_sha256; normalized proxy-only source imports are not portable until original-source artifacts are modeled explicitly. The endpoint exports metadata only. File transfer and LAN peer discovery belong to the native sync layer, not the loopback FastAPI API.
POST /api/v1/sync/projects/import
Imports a project from a previously exported project manifest plus files that have already been staged on disk by the sync transport.
Request fields:
manifeststaging_root
staging_root is a local directory containing the staged project files at the relative paths declared in the manifest. During import, the backend verifies source and artifact bytes with SHA-256, rewrites accepted paths into this install's backend-managed project root, and persists the project through backend services instead of copying database rows from another device.
If the local library already contains the same canonical project or source SHA-256, staged import rejects the duplicate with HTTP 409 instead of creating a second project. The response uses the normal project wrapper shape:
project
Staged import does not enqueue analysis, chord, lyrics, stem, or other generation jobs. Synced projects should import the durable state that was actually exported; future rebuilds are explicit user actions. This endpoint also does not expose FastAPI on the LAN. Peer communication should keep using the separate native sync layer while FastAPI remains bound to loopback.
POST /api/v1/projects/import
Creates a project from a source path and queues initial analysis and chord jobs.
Request fields:
source_pathcopy_into_projectdisplay_name
Response: ProjectResponse.
If the same source track has already been imported, the endpoint returns HTTP 409 with code DUPLICATE_PROJECT_SOURCE, message This project is already imported with name "{name}"., and details containing:
project_idproject_name
GET /api/v1/projects
Optional query:
search
Response: ProjectsResponse.
GET /api/v1/projects/{project_id}
Response: ProjectResponse.
PATCH /api/v1/projects/{project_id}
Supported fields:
display_namesource_key_override
Response: ProjectResponse.
DELETE /api/v1/projects/{project_id}
Deletes the project and project-owned data.
Response: DeleteResponse.
POST /api/v1/projects/{project_id}/analyze
Queues an analysis job.
Request fields:
include_tempoforce
Response: JobResponse.
GET /api/v1/projects/{project_id}/analysis
Returns the stored analysis result, or null if no result exists.
Analysis includes key, key confidence, reference tuning, tuning offset, tempo, analysis version, source artifact, and creation time.
POST /api/v1/projects/{project_id}/chords
Queues chord generation.
Request fields:
backendbackend_fallback_fromforceoverwrite_user_edits
Response: JobResponse.
GET /api/v1/projects/{project_id}/chords
Returns source segments, current timeline segments, backend, source artifact, source kind, user-edit state, metadata, and timestamps. If no chord timeline exists, the response contains an empty timeline.
POST /api/v1/projects/{project_id}/lyrics
Queues local lyrics generation.
Request fields:
force
Response: JobResponse.
GET /api/v1/projects/{project_id}/lyrics
Returns source transcript segments, current edited segments, backend, source artifact, model/device metadata, language, user-edit state, and timestamps. If no lyrics exist, the response contains empty segment lists.
PUT /api/v1/projects/{project_id}/lyrics
Persists edited lyric segment text.
Request fields:
segments
Each segment currently accepts:
text
Response: LyricsResponse.
POST /api/v1/projects/{project_id}/retune
Queues a retune job.
Request fields:
- exactly one of
target_reference_hzortarget_cents_offset preview_onlyoutput_format
Response: JobResponse.
POST /api/v1/projects/{project_id}/transpose
Queues a transpose job.
Request fields:
semitonespreview_onlyoutput_format
Response: JobResponse.
POST /api/v1/projects/{project_id}/preview
Queues a preview job for a retune and/or transpose transform.
Request fields:
retunetransposeoutput_format
At least one transform must be provided.
Response: JobResponse.
POST /api/v1/projects/{project_id}/stems
Queues stem generation for the selected source audio or practice mix.
Request fields:
modestem_modeloutput_formatforcesource_artifact_idchord_backendchord_backend_fallback_fromoverwrite_chord_edits
Current validation allows mode: "stems" or mode: "two_stem" and output_format: "wav". If stem_model is omitted, mode: "stems" uses the backend default htdemucs_6s; mode: "two_stem" maps to htdemucs_ft for compatibility.
htdemucs_6s creates visible Vocals, Drums, Bass, Guitar, Piano, and Other artifacts. htdemucs_ft creates visible Vocals and Instrumental artifacts.
Response: JobResponse.
GET /api/v1/projects/{project_id}/artifacts
Returns artifacts for a project ordered by newest first.
Response: ArtifactsResponse.
DELETE /api/v1/projects/{project_id}/artifacts/{artifact_id}
Deletes a project artifact when it belongs to the project and can be removed.
Response: DeleteResponse.
POST /api/v1/projects/{project_id}/export
Queues an export job for selected project artifacts.
Request fields:
artifact_idsmixdown_modeoutput_formatdestination_path
At least one artifact ID is required.
Response: JobResponse.
GET /api/v1/jobs
Returns all jobs ordered by most recently updated.
Response: JobsResponse.
GET /api/v1/jobs/{job_id}
Response: JobResponse.
POST /api/v1/jobs/{job_id}/cancel
Requests cancellation for a job.
Response: JobResponse.
GET /api/v1/artifacts/{artifact_id}/stream
Streams the artifact file as an audio response. Returns ARTIFACT_NOT_FOUND if the artifact row or file is missing.