# 上传音频
> 把外部音频接入 Suno 流程，异步任务
> 只接受公网可读的 HTTP(S) 直链，不收二进制文件
> 上传完成后的 task_id 可作为翻唱、续写的源
`POST /v1/music/generations/uploadTask`
## 认证
- `Authorization` (string / 必填) — 所有接口均使用 Bearer Token 进行认证。 获取 API Key：[访问控制台](https://api.oairoute.com/keys) 创建。 请求时在请求头中添加： ```http Authorization: Bearer YOUR_OAIRoute_KEY ```
## 请求参数
请求体是 JSON，**不是** `multipart/form-data`。
- `audioFilePath` (string / 必填) — 已上传至对象存储、可公开读取的音频直链。 本机路径与 `blob:` URL 都不行，必须是外部能访问到的 HTTP(S) 地址。
> **Note:** 
> 本接口不接收二进制文件。想直接传文件是做不到的，必须先把文件放到能公网访问的地方（对象存储、图床、CDN），再把直链传进来。
## 不接受的参数
`version`、`audio_format`，以及文档未列出的其他参数都会被忽略。
## 响应
- `code` (integer) — 响应状态码。
- `data[].status` (string) — 任务状态，取值为 `submitted`。
- `data[].task_id` (string) — 任务唯一标识，用于轮询及后续操作引用。
## 上传之后怎么用
轮询拿到完成的 task_id，把它作为后续操作的源：
```json
{
  "model": "suno",
  "task_id": "上传任务的 task_id",
  "audio_index": 1,
  "tags": "jazz, piano trio"
}
```
配合 `audio_index=1` 即可引用上传的音频做翻唱、续写、分轨等操作。
> **Note:** 
> 上传结果可能只返回 `audio_id`，不保证返回可播放的 `audio_url`。如果后续要用音频本身，轮询时检查 `result.music[]` 里是否有地址，没有的话按 `audio_id` 处理。
## 怎么拿到公网直链
| 存储方式 | 做法 |
|---|---|
| 对象存储（S3、OSS、COS） | 上传后把对象设为公共读，复制对象 URL |
| 图床或音床 | 上传后取直链 |
| 自建服务器 | 放到可公开访问的静态目录 |
要点是**不需要登录、不需要签名**就能下载。带鉴权的私有链接会被拒。
## 错误码
错误响应的格式、状态码与重试建议见[错误码与限流](../../../support/errors.html)，常见报错的排查步骤见[常见问题](../../../support/faq.html)。
## 使用示例
### 导入外部音频
`audioFilePath` 填公网可直接下载的地址。本地文件路径和 `blob:` 都会被拒绝，报 `invalid_request_error`。
### 拿到 task_id 后继续操作
上传本身也是一个异步任务。返回的 `task_id` 可以当作源音轨，传给裁剪、续写、翻唱等接口。
```bash
TASK=$(curl -s --request POST https://api.oairoute.com/v1/music/generations/uploadTask \
  -H "Authorization: Bearer $YOUR_OAIRoute_KEY" \
  -H "Content-Type: application/json" \
  -d '{"audioFilePath":"https://example.com/song.mp3"}' \
  | jq -r '.data[0].task_id')
curl --request POST https://api.oairoute.com/v1/music/generations/crop \
  -H "Authorization: Bearer $YOUR_OAIRoute_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"task_id\":\"$TASK\",\"start_s\":12.5,\"end_s\":42}"
```
### 上传后先确认可用
部分来源只返回 `audio_id`，没有可播放的 `audio_url`。后面要拿音频本体时，先查一次任务，确认 `result.music[]` 里有地址。
```bash
curl --request GET \
  --url "https://api.oairoute.com/v1/music/tasks/$TASK" \
  --header "Authorization: Bearer $YOUR_OAIRoute_KEY" \
  | jq '.data.result.music[] | {id, audio_url}'
```
**上传音频{Upload audio}::cURL**
```
curl --request POST \
  --url 'https://api.oairoute.com/v1/music/generations/uploadTask' \
  --header 'Authorization: Bearer YOUR_OAIRoute_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "audioFilePath": "https://your-bucket.s3.amazonaws.com/demo.mp3"
  }'
```
**上传音频{Upload audio}::Python**
```
import requests, time
HEAD = {"Authorization": "Bearer YOUR_OAIRoute_KEY",
        "Content-Type": "application/json"}
# 1. 提交上传
r = requests.post(
    "https://api.oairoute.com/v1/music/generations/uploadTask",
    headers=HEAD,
    json={"audioFilePath": "https://your-bucket.s3.amazonaws.com/demo.mp3"},
)
task_id = r.json()["data"][0]["task_id"]
# 2. 轮询到完成
while True:
    d = requests.get(
        f"https://api.oairoute.com/v1/music/tasks/{task_id}",
        headers=HEAD,
    ).json()["data"]
    if d["status"] == "completed":
        break
    if d["status"] == "failed":
        raise RuntimeError(d.get("error"))
    time.sleep(4)
# 3. 拿这个 task_id 去翻唱
requests.post(
    "https://api.oairoute.com/v1/music/generations/coverSong",
    headers=HEAD,
    json={
        "model": "suno",
        "task_id": task_id,
        "audio_index": 1,
        "tags": "jazz, piano trio, slow",
    },
)
```
**上传音频{Upload audio}::JavaScript**
```
const url = "https://api.oairoute.com/v1/music/generations/uploadTask";
const options = {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_OAIRoute_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "audioFilePath": "https://your-bucket.s3.amazonaws.com/demo.mp3"
  }),
};
fetch(url, options)
  .then((res) => res.json())
  .then((json) => console.log(json))
  .catch((err) => console.error(err));
```
**200 上传音频{Upload audio}**
```
{
  "code": 200,
  "data": [
    { "status": "submitted", "task_id": "task_01K8C66M0HP1GJWBJYZMKX1XS1" }
  ]
}
```
**导入外部音频{Import external audio}::cURL**
```
curl --request POST \
  --url 'https://api.oairoute.com/v1/music/generations/uploadTask' \
  --header 'Authorization: Bearer YOUR_OAIRoute_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "audioFilePath": "https://example.com/song.mp3"
  }'
```
**导入外部音频{Import external audio}::Python**
```
import requests
url = "https://api.oairoute.com/v1/music/generations/uploadTask"
headers = {"Authorization": "Bearer YOUR_OAIRoute_KEY"}
payload = {
    "audioFilePath": "https://example.com/song.mp3"
  }
res = requests.post(url, headers=headers, json=payload)
print(res.json())
```
**导入外部音频{Import external audio}::JavaScript**
```
const res = await fetch("https://api.oairoute.com/v1/music/generations/uploadTask", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_OAIRoute_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "audioFilePath": "https://example.com/song.mp3"
  }),
});
console.log(await res.json());
```
**200 导入外部音频{Import external audio}**
```
{
  "code": 200,
  "data": [
    { "status": "submitted", "task_id": "task_01K8C66M0HP1GJWBJYZMKX1XS1" }
  ]
}
```
