# 上传音频
> 把外部音频导入 Flow Music，异步任务
> 只接受公网可访问的 URL，不收二进制文件
> 导入后拿到的 clip_id 供延伸、替换、Cover、分离使用
`POST /v1/music/generations/uploadAudioFlowMusic`
## 认证
- `Authorization` (string / 必填) — 所有接口均使用 Bearer Token 进行认证。 获取 API Key：[访问控制台](https://api.oairoute.com/keys) 创建。 请求时在请求头中添加： ```http Authorization: Bearer YOUR_OAIRoute_KEY ```
- `Content-Type` (string / 必填) — 固定为 `application/json`。
## 请求参数
请求体是 JSON，**不是** `multipart/form-data`。
- `model` (string / 必填) — 固定传 `flowmusic`，文档注明大小写不敏感。
- `audio_url` (string / 必填) — 待导入音频的公网可访问地址。 仅支持常见音频后缀，官方举例 `.mp3` 与 `.wav`。必须是外部能直接访问的地址。
## 上传之后能做什么
导入成功后拿到的 `clip_id` 可以喂给四个下游接口：
| 下游 | 用途 |
|---|---|
| 音乐延伸 | 在已有音频后续写 |
| 片段替换 | 换掉中间某一段 |
| Cover 改编 | 换风格重新演绎 |
| 词曲分离 | 拆成人声与伴奏 |
不用为每个操作重新上传，一个 `clip_id` 可以反复用。
## 提交响应
- `code` (integer) — 成功时为 200。
- `data[].status` (string) — 初始值为 `submitted`。
- `data[].task_id` (string) — 任务唯一标识，用于后续查询。
## 任务查询结果
用 `GET /v1/music/tasks/{task_id}` 查询。
- `data.id` (string) — 任务 ID，与 `task_id` 对应。
- `data.status` (string) — 任务状态，完成后为 `completed`。
- `data.progress` (integer) — 进度百分比。
- `data.created` (integer) — 创建时间戳。
- `data.completed` (integer) — 完成时间戳。
- `data.actual_time` (integer) — 实际耗时，单位秒。
- `data.cost` (number) — 费用。
- `data.credits_cost` (number) — 积分消耗。
- `result.music[].clip_id` (string) — 导入产物的 clip_id，供延伸、替换、Cover、分离使用。
- `result.music[].audio_url` (string) — 音频地址。
- `result.music[].url` (string) — 音频地址，与上一项同值。
## 错误
| HTTP | 场景 |
|---|---|
| 400 | 参数缺失，如未传 `audio_url` |
| 429 | 分组容量饱和 |
- `error.message` (string) — 错误描述。
- `error.type` (string) — 错误类型，如 `invalid_request`、`rate_limit_error`。
- `error.param` (string) — 相关参数。
- `error.code` (string) — 错误码：`invalid_request`、`quota_not_enough`、`rate_limit_error`。
> **Note:** 
> 这个接口与 Suno 的「上传音频」是两个不同端点。Flow Music 用 `uploadAudioFlowMusic`，Suno 用 `uploadTask`。两边的产物不能互相引用。
更多状态码与重试建议见[错误码与限流](../../../support/errors.html)，常见报错的排查步骤见[常见问题](../../../support/faq.html)。
## 使用示例
提交一个任务并拿到 `task_id`：
> **Note:** 
> 提交成功只表示任务已受理。真正的产物要拿返回的 `task_id` 去任务查询接口轮询，`status` 变成 `completed` 之后才有结果。
**上传音频{Upload audio}::cURL**
```
curl -X POST https://api.oairoute.com/v1/music/generations/uploadAudioFlowMusic \
  -H 'Authorization: Bearer YOUR_OAIRoute_KEY' \
  -H 'Content-Type: application/json' \
  --data '{
    "model": "flowmusic",
    "audio_url": "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/uploadAudioFlowMusic",
    headers=HEAD,
    json={
        "model": "flowmusic",
        "audio_url": "https://your-bucket.s3.amazonaws.com/demo.mp3",
    },
)
task_id = r.json()["data"][0]["task_id"]
# 2. 轮询到完成，取 clip_id
while True:
    d = requests.get(
        f"https://api.oairoute.com/v1/music/tasks/{task_id}",
        headers=HEAD,
    ).json()["data"]
    if d["status"] == "completed":
        clip_id = d["result"]["music"][0]["clip_id"]
        print("可复用的 clip_id:", clip_id)
        break
    if d["status"] == "failed":
        raise RuntimeError(d.get("error"))
    time.sleep(4)
# 3. 用这个 clip_id 做延伸
requests.post(
    "https://api.oairoute.com/v1/music/generations/extendFlowMusic",
    headers=HEAD,
    json={
        "model": "flowmusic",
        "clip_id": clip_id,
        "extend_from_s": 90,
        "extend_s": 45,
        "instruction": "延续情绪，加入弦乐",
    },
)
```
**上传音频{Upload audio}::JavaScript**
```
const url = "https://api.oairoute.com/v1/music/generations/uploadAudioFlowMusic";
const options = {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_OAIRoute_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "model": "flowmusic",
    "audio_url": "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_01K8F77M0HP1GJWBJYZMKX1XS1" }
  ]
}
```
