> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mountsea.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 定制模型

> 基于您自己的音频创建定制音乐模型

基于您自己的音频创建个性化的音乐模型。该流程需要三个步骤：**prepare**（创建会话）→ **upload**（上传训练音频，至少 6 个）→ **create**（提交训练）。

## 工作流程

```
 ① customModel/prepare
      │  选择账户，创建会话
      │  返回: { sessionId }
      │
      ▼
 ② customModel/upload  ×N（至少 6 次）
      │  逐个上传音频文件（异步）
      │  返回: { taskId }
      │  轮询: GET /suno/v2/status?taskId=xxx
      │  结果包含片段信息（包括 id）
      │
      │  可以并行上传，每个独立轮询
      │
      ▼
 ③ customModel/create
      │  提交 clipIds + 模型名称 → 开始训练
      │  返回: { taskId }（taskId = sessionId）
      │  轮询: GET /suno/v2/status?taskId=xxx
      │  训练大约需要 2-3 分钟
      │  结果包含定制模型详情（包括模型 ID）
      ▼
    完成 → 在 /generate 中使用模型 ID
```

***

## 第一步：Prepare — 创建会话

创建会话并绑定账户。所有后续的上传和创建调用都会自动使用同一账户。

### 请求

```
POST /suno/v2/customModel/prepare
```

无需请求体。

### 响应

```json theme={null}
{
  "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

<Info>
  `sessionId` 是整个工作流程的唯一标识符。请保存它以用于后续步骤。
</Info>

参见 [Prepare API 参考 →](/zh/api-reference/suno/customModelPrepare)

***

## 第二步：Upload — 上传训练音频

每次请求上传一个音频文件。每次调用是异步的，返回一个 `taskId`。您需要轮询直到任务成功，从结果中获取 `clipId`。

在进入创建步骤之前，**至少需要 6 个音频文件**。多个上传可以并行进行。

### 请求

```
POST /suno/v2/customModel/upload
```

| 字段          | 类型            | 必填 | 描述                 |
| ----------- | ------------- | -- | ------------------ |
| `sessionId` | string (UUID) | 是  | prepare 步骤返回的会话 ID |
| `audioUrl`  | string (URL)  | 是  | 可公开下载的音频 URL       |

### 任务结果

成功后，`result` 包含片段信息，其中有一个 `id` 字段 — 这就是创建步骤所需的 `clipId`。

参见 [Upload API 参考 →](/zh/api-reference/suno/customModelUpload)

***

## 第三步：Create — 提交训练

收集至少 6 个成功上传的 clipId 后，提交训练请求。这将消耗 **100 积分**，大约需要 **2-3 分钟**。

### 请求

```
POST /suno/v2/customModel/create
```

| 字段          | 类型            | 必填 | 描述                  |
| ----------- | ------------- | -- | ------------------- |
| `sessionId` | string (UUID) | 是  | prepare 步骤返回的会话 ID  |
| `clipIds`   | string\[]     | 是  | 上传结果中的片段 ID（至少 6 个） |
| `name`      | string        | 是  | 定制模型名称              |

<Info>
  返回的 `taskId` 等于 `sessionId`。您可以使用其中任何一个进行轮询。
</Info>

成功后，`result` 包含定制模型详情。模型 ID 格式为 `chirp-custom:<uuid>`。

参见 [Create API 参考 →](/zh/api-reference/suno/customModelCreate)

***

## 使用您的定制模型

训练完成后，在 [Generate](/zh/api-reference/suno/generate) 端点中直接使用模型 ID：

```json theme={null}
{
  "task": "create",
  "model": "chirp-custom:3b285ad7-b09c-48fe-99e6-ec8a109f0650",
  "prompt": "[Verse]\nA catchy pop melody...",
  "tags": "Pop, Upbeat"
}
```

系统会自动使用创建定制模型的同一账户进行生成。

***

## 完整示例

<CodeGroup>
  ```javascript Node.js theme={null}
  const API_BASE = 'https://api.mountsea.ai';
  const headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-api-key'
  };

  async function pollTask(taskId) {
    while (true) {
      const res = await fetch(`${API_BASE}/suno/v2/status?taskId=${taskId}`, { headers });
      const task = await res.json();
      if (task.status === 'success') return task.data;
      if (task.status === 'failed') throw new Error(task.failReason);
      await new Promise(r => setTimeout(r, 3000));
    }
  }

  // Step 1: Prepare session
  const prepareRes = await fetch(`${API_BASE}/suno/v2/customModel/prepare`, {
    method: 'POST',
    headers
  });
  const { sessionId } = await prepareRes.json();
  console.log('Session ID:', sessionId);

  // Step 2: Upload audio files (at least 6, can run in parallel)
  const audioUrls = [
    'https://example.com/song1.mp3',
    'https://example.com/song2.mp3',
    'https://example.com/song3.mp3',
    'https://example.com/song4.mp3',
    'https://example.com/song5.mp3',
    'https://example.com/song6.mp3',
  ];

  const uploadPromises = audioUrls.map(async (audioUrl) => {
    const res = await fetch(`${API_BASE}/suno/v2/customModel/upload`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ sessionId, audioUrl })
    });
    const { taskId } = await res.json();
    const result = await pollTask(taskId);
    return result.id; // clipId
  });

  const clipIds = await Promise.all(uploadPromises);
  console.log('Uploaded clips:', clipIds);

  // Step 3: Create custom model
  const createRes = await fetch(`${API_BASE}/suno/v2/customModel/create`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      sessionId,
      clipIds,
      name: 'My Custom Model'
    })
  });
  const { taskId: createTaskId } = await createRes.json();
  const model = await pollTask(createTaskId);
  console.log('Custom model created:', model);
  // Use model.id (e.g. "chirp-custom:xxx") in /generate
  ```

  ```python Python theme={null}
  import requests
  import time
  from concurrent.futures import ThreadPoolExecutor

  API_BASE = 'https://api.mountsea.ai'
  headers = {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer your-api-key'
  }

  def poll_task(task_id):
      while True:
          res = requests.get(f'{API_BASE}/suno/v2/status', params={'taskId': task_id}, headers=headers)
          task = res.json()
          if task['status'] == 'success':
              return task['data']
          if task['status'] == 'failed':
              raise Exception(task.get('failReason', 'Unknown error'))
          time.sleep(3)

  def upload_audio(session_id, audio_url):
      res = requests.post(f'{API_BASE}/suno/v2/customModel/upload', headers=headers, json={
          'sessionId': session_id,
          'audioUrl': audio_url
      })
      result = poll_task(res.json()['taskId'])
      return result['id']

  # Step 1: Prepare
  prepare_res = requests.post(f'{API_BASE}/suno/v2/customModel/prepare', headers=headers)
  session_id = prepare_res.json()['sessionId']

  # Step 2: Upload (parallel)
  audio_urls = [f'https://example.com/song{i}.mp3' for i in range(1, 7)]
  with ThreadPoolExecutor(max_workers=6) as executor:
      clip_ids = list(executor.map(lambda url: upload_audio(session_id, url), audio_urls))

  # Step 3: Create
  create_res = requests.post(f'{API_BASE}/suno/v2/customModel/create', headers=headers, json={
      'sessionId': session_id,
      'clipIds': clip_ids,
      'name': 'My Custom Model'
  })
  model = poll_task(create_res.json()['taskId'])
  print(f"Custom model created: {model}")
  ```
</CodeGroup>

## 故障恢复

* **上传失败**：使用相同的 `sessionId` 重试 — 无需重新 prepare。
* **创建失败**（例如参数错误）：使用相同的 `sessionId` 和修正后的参数重试。系统会自动重置会话状态。
* **Prepare** 很少失败，因为它只创建会话并绑定账户。

## 重要说明

<Warning>
  `create` 步骤消耗 **100 积分**。请在提交前确保所有上传的片段都正确无误。
</Warning>

* **同一账户保证**：prepare / upload / create 自动使用相同的 Suno 账户 — 无需手动指定。
* **积分**：create 步骤消耗 100 积分。upload 步骤也会根据任务定价配置产生少量积分费用。
* **ClipId 验证**：create 请求中的所有 `clipIds` 必须来自当前会话中的上传，否则会返回 400 错误。
* **并行上传**：多个上传请求可以并发发送，不会产生冲突。
* **模型限制**：每个定制账户可创建的模型数量有上限，由管理员配置。
