Suno Studio Project Integration Guide

POST https://api.fesilent.com/suno/projects is the Beta API for managing multi-track projects. Use action to distinguish between creating, saving, generating candidates, committing candidates, and exporting.

POST /suno/projects
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: <每次写操作的新 UUID>

Except for retrieve, every operation requires Idempotency-Key. The same key with the same request only returns the original result; the same key with a different request returns 409. When a paid generation encounters a network timeout or an uncertain submission result, first query using the original task ID or the same idempotency key; do not directly change the key and resend.

Identifiers and Time Units

Name Where to obtain it Where to use it
id data.id in the create response All subsequent requests for the current project
version_id data.version_id from the latest retrieve, save, add_track, commit_candidate, or remove_track Modifying, generating candidates, and exporting; the latest version must be used
audio_id / source_audio_id response.data.candidate.audio_id from the final upload result, or an existing completed audio ID Referencing the original audio when using add_track, generating, or replacing
render_audio_id response.data.audio_id from the final render result Project mix reference for generate_track
operation_id response.data.operation_id from the final generate_track or replace_section result commit_candidate; usually the same as the generation task's task_id, but the returned value should be read
candidate_id response.data.candidates[].id from the final generation result Selecting a candidate to commit
track_id data.state.tracks[].id in the project Committing a candidate or deleting a track
task_id Async operation submission response Querying the final result using /suno/tasks; it is not a project ID

start_seconds / end_seconds are seconds within the source audio; start_beats / end_beats are beats on the project timeline. state.timing.bps is beats per second, with a default of 2. For example, a 41-second clip starting at beat 0 has a default endpoint at beat 82. The seconds range of generate_track is used to specify the generation reference and does not guarantee that the candidate is only the length of that range.

Operation Flow

Order action Purpose Return Method
1 create Create an empty project Synchronous project data
2 retrieve Read the latest version and complete state Synchronous project data
3 save Initial save or save the modified overall state Synchronous project data
4 upload Create an asset from an HTTPS audio URL Async task
5 add_track Place asset audio into the project Async task, updates version
6 save To receive new track candidates, first save an empty target track Synchronous project data, updates version
7 render Export currently audible tracks and obtain the mix reference audio_id Async task
8 generate_track or replace_section Generate new track candidates or partial replacement candidates respectively Async task, does not modify the project yet
9 commit_candidate Select and commit a candidate Async task, updates version upon success
Optional remove_track Delete the specified track Synchronous project data, updates version

If only performing a partial replacement, existing usable source audio is sufficient and render is not required first; if the source audio is already in the project, upload and add_track can also be skipped. render_audio_id is only the required reference for generate_track.

When an async operation is submitted, it first returns { "task_id": "...", "trace_id": "..." }, which does not indicate successful generation. Poll:

POST /suno/tasks
Content-Type: application/json

{"action":"retrieve","id":"上一步返回的 task_id"}

A task is considered successful only when the task record has finished_at and its response.success is true; when response.success=false, read response.error. Although save may include task_id, it completes synchronously within this API. upload, render, generate_track, and replace_section do not modify the project version; after save, add_track, commit_candidate, and remove_track succeed, the new version_id should be used.

Request Conventions

Parameter Type and Applicable Scope Description
action Required string Fixed as one of the operation names listed on this page; each request performs only one operation.
id Required string except for create Project ID, from create.data.id.
version_id Required string when modifying, generating candidates, or exporting; may be omitted for the first save of an empty project Use the latest version; old versions will be rejected. retrieve does not require it.
Idempotency-Key Required request header except for retrieve, 1–128 characters Use a new key for each new operation; the same key and same request return the original result without regenerating or charging again.
async Optional Boolean for upload, add_track, generate_track, replace_section, commit_candidate, and render These operations are always async; false will not make generation synchronous.
callback_url Optional URL for async operations Receives final status notifications; when omitted, query /suno/tasks using task_id. response.success must still be checked.

The following examples demonstrate the passing of IDs and versions between operations. Responses list only the fields relevant to the current step; when integrating, replace the example IDs with the response values from the current application.

Creating and Retrieving Projects

Operation Parameter Type and Description
create title Required string, 1–200 characters; title of the new project.
retrieve No dedicated parameters Use the project id to read the current version and complete state; no idempotency key is required.

Create Project: create

Request:

{
  "action": "create",
  "title": "Suno Projects Documentation Verification 2026-10-09"
}

Response:

{
  "success": true,
  "trace_id": "42408a82-988b-4127-ba68-9b89348a7c56",
  "data": {
    "id": "af0e104a-2a4c-40e0-9c70-7614566824a6",
    "title": "Suno Projects Documentation Verification 2026-10-09",
    "state": {
      "tracks": [],
      "timing": {
        "bps": 2
      }
    }
  }
}

Retrieve Project: retrieve

Request:```json { "action": "retrieve", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6" }

Response:

{ "success": true, "trace_id": "a335ceac-5cb7-4b1c-9773-77b9d5c84bd7", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "title": "Suno Projects Documentation Verification 2026-10-09", "state": { "tracks": [], "timing": { "bps": 2 } } } }


## Save Project State

| Parameter | Type and Description |
|---|---|
| `state` | Required object. Submit the **complete project state**, not a partial patch; base it on the `data.state` returned by the most recent retrieve or modify operation, and preserve unknown fields. |
| `title` | Optional string; when omitted, use `state.title`; if that is also unavailable, use the default title. |
| `version_id` | May be omitted when first saving a newly created empty project; thereafter, the latest version must be used. |

`state.timing.bps` must be a positive number, with a default of 2. `state.tracks` is the track array; existing audio clips have `clipId` references to audio IDs, and `startBeats`, `endBeats`, and `readStartBeats` use project beat units. The safest editing method is to retrieve the complete `state`, modify only the target track or clip, and then `save` it as a whole. A newly added empty target track should at minimum have a unique `id`, `type:"audio"`, and `clips:[]`; see the example in “Generate New Audio Track” for the complete empty track structure. Do not directly use beat points in audio analysis results as project timeline coordinates.

### First Save: `save`

A newly created project has no `version_id`; omit it for the first save:

{ "action": "save", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "title": "Suno Projects Documentation Verification 2026-10-09", "state": { "tracks": [], "timing": { "bps": 2 } } }

Response:

{ "success": true, "task_id": "ab172b47-4d89-404b-a10e-71f67c36ccba", "trace_id": "fc7b2e43-7a7a-4276-8671-27008a1153ea", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "8f0c62fb-304f-420e-9707-426e8b64b5e3", "state": { "timing": { "bps": 2 }, "tracks": [] } } }


## Upload Audio and Add a Track

| Operation | Parameter | Type and Description |
|---|---|---|
| `upload` | `audio_url` | Required HTTPS URL, pointing to audio that is publicly accessible and that you have the right to use. The `candidate.audio_id` in the final state can be used to add a track. |
| `add_track` | `audio_id` | Required string; the audio ID of completed material, not a URL. |
| `add_track` | `name`, `color` | Optional strings; the track name defaults to the audio title, the recommended color is `#RRGGBB`, and the default is `#7251F7`. |
| `add_track` | `title` | Optional string; also modifies the project title, usually omitted. |
| `add_track` | `start_beats`, `end_beats` | Optional non-negative numbers; the endpoint must be greater than the start point; by default, it starts from beat 0, and the endpoint is calculated based on audio duration and `timing.bps`. |
| `add_track` | `gain` | Optional non-negative number, default 1; the volume multiplier for clips and tracks. |

### Upload Material: `upload`

{ "action": "upload", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "8f0c62fb-304f-420e-9707-426e8b64b5e3", "audio_url": "https://cdn.acedata.cloud/suno_demo.mp3", "async": true }

Acceptance response:

{ "task_id": "3fe60d9c-cada-4aab-b1f4-8c0b28247243", "trace_id": "4a54cb56-befb-45da-8001-2a20cdb67d18" }

Task final state:

{ "success": true, "task_id": "3fe60d9c-cada-4aab-b1f4-8c0b28247243", "trace_id": "4a54cb56-befb-45da-8001-2a20cdb67d18", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "candidate": { "audio_id": "b21be571-bfe4-47c1-b59c-2abc55c6f9c3", "title": "suno_demo", "duration": 41, "audio_url": "https://cdn.acedata2.cloud/suno/b21be571-bfe4-47c1-b59c-2abc55c6f9c3.m4a" } } }


### Add Source Track: `add_track`

{ "action": "add_track", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "8f0c62fb-304f-420e-9707-426e8b64b5e3", "audio_id": "b21be571-bfe4-47c1-b59c-2abc55c6f9c3", "name": "Source demo", "start_beats": 0 }

Acceptance response:

{ "task_id": "03f0f74f-c3ed-4d03-896b-74b2a366838b", "trace_id": "3442a5b2-7b16-4225-b8af-24eb872d0e07" }

Task final state:

{ "success": true, "task_id": "03f0f74f-c3ed-4d03-896b-74b2a366838b", "trace_id": "3442a5b2-7b16-4225-b8af-24eb872d0e07", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cf8a6aa5-0f6d-4da3-844e-cc156aa1b731", "track": { "id": "b7ee9e97-f517-44d7-af4c-e9dad71498c9", "name": "Source demo", "clips": [ { "clipId": "b21be571-bfe4-47c1-b59c-2abc55c6f9c3", "startBeats": 0, "endBeats": 82 } ] } } }


## Generate New Audio Track

Candidates are bound to the project version at the time of generation. First save an empty track to receive candidates, then export the current mix as a generation reference; after generation is complete, select one candidate and submit it to that empty track.

### Reserve Target Track: `save`

The candidates of `generate_track` are bound to the current project version. On the **complete** `state` returned in the final state of the previous `add_track`, add the following empty track to `state.tracks`, then save it with `save`; preserve all other existing fields as-is:

{ "id": "bdec15b5-ce96-43fa-b681-26298e013b06", "type": "audio", "name": "Generated piano", "clips": [], "takeLanes": [], "mute": false, "solo": false, "amplitude": 1, "instrument": { "type": "song" }, "color": "#7251F7" }

Save response:

{ "success": true, "task_id": "783f8d49-cc59-4a65-9f9d-ca00e7d7b8a9", "trace_id": "0a9c8f13-7e4f-4915-8a40-010ce4ac61dc", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cb0a82c9-aa5b-48cc-9395-0528675fc18d" } }


### Export reference mix: `render`

| Parameter | Type and description |
|---|---|
| `title` | Required string; exported audio title. |
| `lyrics`, `tags` | Optional strings; lyrics default to `[Instrumental]`, and `tags` are style descriptions. |
| `start_beats`, `end_beats` | Optional non-negative numbers; the endpoint must be greater than the start point; when omitted, covers all audible clips. Muted clips are not included; when there are solo tracks, only solo tracks are selected. |

After success, retrieve `render_audio_id` from the task final state `response.data.audio_id`.

{ "action": "render", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cb0a82c9-aa5b-48cc-9395-0528675fc18d", "title": "Suno Projects Docs Source Mix", "lyrics": "[Instrumental]", "async": true }

Acceptance response:

{ "task_id": "adc559fa-4a87-4a53-9336-28d4302a151b", "trace_id": "165fde93-f34b-4b0b-a7cd-0fba7d7ac154" }

Task final state:

{ "success": true, "task_id": "adc559fa-4a87-4a53-9336-28d4302a151b", "trace_id": "165fde93-f34b-4b0b-a7cd-0fba7d7ac154", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cb0a82c9-aa5b-48cc-9395-0528675fc18d", "render_id": "41038732-4dc4-40bf-b00d-2728d99bc26e", "audio_id": "41038732-4dc4-40bf-b00d-2728d99bc26e", "duration": 41, "audio_url": "https://cdn.acedata2.cloud/suno/41038732-4dc4-40bf-b00d-2728d99bc26e.m4a" } }


### Generate candidates: `generate_track`

| Parameter | Type and description |
|---|---|
| `source_audio_id` | Required; completed source audio ID. First add the source audio to the project, then the candidate position can be determined. |
| `render_audio_id` | Required; `response.data.audio_id` from the above `render` final state, not the audio URL. |
| `model` | Required; options include `chirp-v3-5`, `chirp-v4`, `chirp-v4-5`, `chirp-v4-5-plus`, `chirp-v5`, `chirp-v5-5`, `chirp-v6`, `chirp-v6-wild`, `chirp-v6-mini`. Whether specific combinations are available is subject to the task final state. |
| `start_seconds`, `end_seconds` | Required numbers, `0 ≤ start_seconds < end_seconds`; the time range in seconds within the source audio, not project beats, and the output duration is not guaranteed. |
| `stem_control_tags` | Required string; description of the new part, for example `add Piano`, `add Drums`, `add Bass`, not a fixed enumeration. |
| `batch_size` | Optional integer 1–4, default 2; number of candidates. |
| `instrumental`, `vocal_gender` | Optional; instrumental defaults to `true`, vocal preference can be `f`, `m`, `unspecified`, default is `unspecified`. |
| `title`, `tags`, `negative_tags`, `prompt` | Optional strings; respectively candidate title, desired style, styles to avoid, and text or lyric prompt for the new part. |

{ "action": "generate_track", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cb0a82c9-aa5b-48cc-9395-0528675fc18d", "source_audio_id": "b21be571-bfe4-47c1-b59c-2abc55c6f9c3", "render_audio_id": "41038732-4dc4-40bf-b00d-2728d99bc26e", "model": "chirp-v5", "start_seconds": 12, "end_seconds": 20, "stem_control_tags": "add Piano", "title": "Documentation Piano Take", "tags": "gentle piano, instrumental", "batch_size": 2, "async": true }

Acceptance response:

{ "task_id": "66a7356f-3b2a-4472-bdfd-f7b973380644", "trace_id": "81a4cf70-d9f6-48b3-a889-b68e74d6307f" }

Task final state:

{ "success": true, "task_id": "66a7356f-3b2a-4472-bdfd-f7b973380644", "trace_id": "81a4cf70-d9f6-48b3-a889-b68e74d6307f", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cb0a82c9-aa5b-48cc-9395-0528675fc18d", "operation_id": "66a7356f-3b2a-4472-bdfd-f7b973380644", "candidates": [ { "id": "1a4788a8-6f04-47d7-9698-4199b585b9a8", "title": "Documentation Piano Take", "state": "complete", "duration": 41.76, "audio_url": "https://cdn.acedata2.cloud/suno/20f8ffe5063111b1eb866336242e2c0b2b9b7103a8dc0c0c19106cbf50843f86.mp3" }, { "id": "18d6e0a4-2d73-43c3-92e9-be87cf11e139", "title": "Documentation Piano Take", "state": "complete", "duration": 40.96, "audio_url": "https://cdn.acedata2.cloud/suno/c1c50a016333e3a037f18f651c0a88061880b3069aee7c6a258d6c9cdc041a6b.mp3" } ] } }


The 12–20 seconds in the request is the source audio reference interval, while the candidate audio is approximately 41 seconds; `start_seconds`/`end_seconds` do not guarantee the output duration.

### Adopt candidate: `commit_candidate`

| Parameter | Type and description |
|---|---|
| `operation_id` | Required; the generation task terminal-state `data.operation_id`. |
| `candidate_id` | Required; the selected `data.candidates[].id`. |
| `track_id` | Required; the ID of an empty track `state.tracks[].id` saved before generation. |
| `start_beats`, `end_beats` | Optional for new-track candidates; specifies the project beat range and must not overlap with existing clips on the target track. When omitted, the source clip start point is retained and the end point is calculated based on the candidate duration. |
| `name`, `gain` | Optional for new-track candidates; clip name and non-negative volume coefficient, with `gain` defaulting to 1. |

Generation only produces candidates and **does not automatically place them into the project**. `commit_candidate` checks that the candidate still belongs to the current version. Local replacement candidates can be automatically committed only when their actual duration can reliably match the full source audio or selected interval; when they do not match, the original project is retained and an error is returned, and manually guessing beat counts to forcibly splice them together is not allowed.

{ "action": "commit_candidate", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "cb0a82c9-aa5b-48cc-9395-0528675fc18d", "operation_id": "66a7356f-3b2a-4472-bdfd-f7b973380644", "candidate_id": "1a4788a8-6f04-47d7-9698-4199b585b9a8", "track_id": "bdec15b5-ce96-43fa-b681-26298e013b06", "async": true }

Acceptance response:

{ "task_id": "3291bd86-d524-4720-9b6e-655efa600e3d", "trace_id": "1bfee6c0-e936-4147-8a75-a903173d13d6" }

Task terminal state:

{ "success": true, "task_id": "3291bd86-d524-4720-9b6e-655efa600e3d", "trace_id": "1bfee6c0-e936-4147-8a75-a903173d13d6", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "1ff01ada-e26b-444e-a618-a91f76754360", "committed_candidate_id": "1a4788a8-6f04-47d7-9698-4199b585b9a8", "state": { "tracks": [ { "id": "b7ee9e97-f517-44d7-af4c-e9dad71498c9", "clips": [ { "clipId": "b21be571-bfe4-47c1-b59c-2abc55c6f9c3", "startBeats": 0, "endBeats": 82 } ] }, { "id": "bdec15b5-ce96-43fa-b681-26298e013b06", "clips": [ { "clipId": "1a4788a8-6f04-47d7-9698-4199b585b9a8", "startBeats": 0, "endBeats": 83.52 } ] } ] } } }


### Export after committing

After committing the new-track candidate, exporting the full song containing that candidate was rejected. The request and terminal state:

{ "action": "render", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "1ff01ada-e26b-444e-a618-a91f76754360", "title": "Suno Projects Docs Mix With Piano", "lyrics": "[Instrumental]", "async": true }

Acceptance response:

{ "task_id": "12fe503c-017e-46db-9d99-8cfb861ad4fc", "trace_id": "2f376c45-a081-4c2a-b6a5-658401fe8dc0" }

Task terminal state:

{ "success": false, "task_id": "12fe503c-017e-46db-9d99-8cfb861ad4fc", "trace_id": "2f376c45-a081-4c2a-b6a5-658401fe8dc0", "error": { "code": "studio_audio_unavailable", "message": "A referenced audio is not available for project rendering. Keep the original project and use a supported audio." } }


A readable candidate audio link does not mean that a project containing it can necessarily be exported. When receiving `studio_audio_unavailable`, retain the original project and check the availability of the referenced audio.

## Replace an existing clip

`replace_section` can directly use existing source audio without first exporting a mix. Generation only returns candidates; when committing, select the original track containing the sole source clip.

| Parameter | Type and description |
|---|---|
| `source_audio_id` | Required; the completed source audio ID to replace. |
| `model` | Required; use a public model listed under “Generate candidates”; model and operation availability are subject to the task terminal state. |
| `start_seconds`, `end_seconds` | Required numbers, `0 ≤ start_seconds < end_seconds ≤ source audio duration`; the seconds interval within the source audio. |
| `prompt`, `replacement_lyrics` | Optional strings; the former is the original lyrics or context, and the latter is the new lyrics for the replacement interval. |
| `title`, `tags`, `negative_tags` | Optional strings; candidate title, desired style, and styles to avoid. |
| `fixed` | Optional boolean, default `false`; when set to `true`, the interval must be shorter than 26 seconds, and the model may not support it. |

### Generate replacement candidates: `replace_section`

{ "action": "replace_section", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "1ff01ada-e26b-444e-a618-a91f76754360", "source_audio_id": "b21be571-bfe4-47c1-b59c-2abc55c6f9c3", "model": "chirp-v5", "title": "Documentation Verse Edit", "tags": "dreamy Chinese pop", "prompt": "[Verse 1]\n晨光洒在海边\n浪花笑得灿烂\n你和我在沙滩\n钟表停止转动\n冰淇淋在融化\n手牵手去散步\n[Chorus]\n风儿轻轻吹过\n心跳不停鼓\n夏天夏天\n时间停在这一天\n笑声回荡\n微蓝天空在身旁", "replacement_lyrics": "我们走在海边\n微风吹过晴天", "start_seconds": 12, "end_seconds": 20, "async": true }

Acceptance response:

{ "task_id": "e742a2d5-35f1-4a43-b351-c6aa5a744c4c", "trace_id": "9f9c0bec-963d-417f-aaf2-0b7f89e0dcbc" }

Task terminal state:

{ "success": true, "task_id": "e742a2d5-35f1-4a43-b351-c6aa5a744c4c", "trace_id": "9f9c0bec-963d-417f-aaf2-0b7f89e0dcbc", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "1ff01ada-e26b-444e-a618-a91f76754360", "operation_id": "e742a2d5-35f1-4a43-b351-c6aa5a744c4c", "candidates": [ { "id": "c486e799-5f89-4db0-b414-3b666e0db2b3", "title": "Documentation Verse Edit", "state": "complete", "duration": 36, "audio_url": "https://cdn.acedata2.cloud/suno/287a448c0e6099b5e66901f363607bcfcf38d06312c344b03a3486bbb4fe037b.mp3" }, { "id": "f0b66dff-9f8e-45f2-b441-c94e2ecd3a11", "title": "Documentation Verse Edit", "state": "complete", "duration": 36, "audio_url": "https://cdn.acedata2.cloud/suno/172227abd70f2201081a8f58e4c3b401f1e75fd0b29c03aa95abdb6689bfd0a5.mp3" } ] } }


### Candidate duration mismatch

When submitting a partial replacement candidate, `track_id` should point to the original track containing the unique source segment, and `start_beats` and `end_beats` should be omitted. The interface updates the project only when the candidate duration can reliably match the complete source audio or the selected interval.

After selecting an 8-second interval, both candidates are 36 seconds, which is neither equal to the original audio's 41 seconds nor to the selected 8 seconds. Submitting one of the candidates returns:

{ "action": "commit_candidate", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "1ff01ada-e26b-444e-a618-a91f76754360", "operation_id": "e742a2d5-35f1-4a43-b351-c6aa5a744c4c", "candidate_id": "c486e799-5f89-4db0-b414-3b666e0db2b3", "track_id": "b7ee9e97-f517-44d7-af4c-e9dad71498c9", "async": true }

Acceptance response:

{ "task_id": "2261a2a6-d7fb-42ac-ae58-41005cfc8173", "trace_id": "25c6e92d-e0b9-4f9b-ba35-434635ec7bb3" }

Task terminal state:

{ "success": false, "task_id": "2261a2a6-d7fb-42ac-ae58-41005cfc8173", "trace_id": "25c6e92d-e0b9-4f9b-ba35-434635ec7bb3", "error": { "code": "bad_request", "message": "Candidate duration does not match the source or selected section. Keep the original track and inspect the candidate before editing." } }


Therefore, the original track was not modified. When this error occurs, the candidates should be retained for auditioning and manual processing; do not treat them as having been successfully inserted into the project, and do not automatically resubmit paid generation.

## Delete Track

`remove_track` requires the current `version_id` and `state.tracks[].id`; use the returned new version after success. Below, the candidate track that cannot be used for full-song export is deleted, and the project state is read back.

{ "action": "remove_track", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "1ff01ada-e26b-444e-a618-a91f76754360", "track_id": "bdec15b5-ce96-43fa-b681-26298e013b06" }

Response:

{ "success": true, "trace_id": "5df2f4b4-6f09-4006-93bd-ad44f4ccb8c1", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "c3f650a9-1c49-4e80-88ef-27deb9b16ce8", "removed_track_id": "bdec15b5-ce96-43fa-b681-26298e013b06", "state": { "tracks": [ { "id": "b7ee9e97-f517-44d7-af4c-e9dad71498c9" } ] } } }

Read the project again:

{ "success": true, "trace_id": "c7613f9b-6484-47b1-83ff-f8a0c1529d8e", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "c3f650a9-1c49-4e80-88ef-27deb9b16ce8", "state": { "tracks": [ { "id": "b7ee9e97-f517-44d7-af4c-e9dad71498c9" } ] } } }


### Export after deletion

After deleting this candidate track, the original source track still remains. Exporting again with the latest version succeeds:

{ "action": "render", "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "c3f650a9-1c49-4e80-88ef-27deb9b16ce8", "title": "Suno Projects Docs Mix After Remove", "async": true }

Acceptance response:

{ "task_id": "41c8bcb2-eb98-4da7-8081-7755bcc7be8d", "trace_id": "6480a230-7553-4dd1-a8e2-9131dab418a9" }

Task terminal state:

{ "success": true, "task_id": "41c8bcb2-eb98-4da7-8081-7755bcc7be8d", "trace_id": "6480a230-7553-4dd1-a8e2-9131dab418a9", "data": { "id": "af0e104a-2a4c-40e0-9c70-7614566824a6", "version_id": "c3f650a9-1c49-4e80-88ef-27deb9b16ce8", "render_id": "75aaaa5f-e633-4006-9200-98d03f3542af", "audio_id": "75aaaa5f-e633-4006-9200-98d03f3542af", "duration": 41, "audio_url": "https://cdn.acedata2.cloud/suno/75aaaa5f-e633-4006-9200-98d03f3542af.m4a" } }


A successfully exported `audio_url` can be used for downloading or playback. An accessible audio link does not prove that other candidates are also eligible for project export.

## Errors and Recovery

| Result | Meaning | Next Step |
|---|---|---|
| `project_version_conflict` / `project_candidate_stale` (409) | The project version has changed, or the candidate does not belong to the current version. | `retrieve` the latest project and re-plan the modifications; do not replay old candidates. |
| `bad_request` / `studio_state_invalid` / `studio_model_unsupported` (usually 400) | The parameters, track state, candidate duration, or model do not match the operation. | Check the task terminal-state error and correct the input; retain the original project when candidates cannot be safely stitched together. |
| `studio_audio_unavailable` / `studio_access_denied` / `content_rejected` (403) | The referenced audio is unavailable for this project operation, access is denied, or content review rejected it. | Check the audio source and the task `trace_id`; do not treat an already generated candidate as equivalent to the entire track being exportable. |
| `too_many_requests` (429) | The request is rate-limited. | Query the original task first, then decide whether to initiate a new operation with a new idempotency key. |
| `studio_unavailable` / `studio_model_unavailable` (503) | Project processing or the selected model is temporarily unavailable. | Retain the original task result; explicitly retry later, and do not automatically switch models. |
| `studio_processing_failed` / timeout (500/504) | Processing failed or the terminal state is uncertain. | Query by `task_id` or the original idempotency key to avoid duplicate paid generation. |

Projects and candidates are bound to the application and execution environment that created them, and cross-application reuse or automatic failover must not be assumed. Persist the final successfully exported audio URL, and retain the `trace_id` for troubleshooting.