# 音乐生成 API

使用 POST /api/v1/music 生成音乐，包括选项、计价与错误处理。

## 创建音乐任务

`POST /api/v1/music` 根据文本提示生成原创音乐。音乐在请求返回前生成完毕，因此响应通常已带 `status: "success"`，音乐 URL 在 `data.taskUrls` 中；请保持连接并设置足够长的客户端超时。返回的 `data.id` 同样可用于 [`GET /api/v1/tasks/{taskId}`](https://viewmax.studio/zh/docs/api/tasks)，便于统一轮询流程。

```http
POST https://viewmax.studio/api/v1/music
Authorization: Bearer sk-your-api-key
Content-Type: application/json
Idempotency-Key: lofi-001
```

## 请求体

| 字段      | 必填 | 类型   | 说明           |
| --------- | ---- | ------ | -------------- |
| `prompt`  | 是   | string | 非空的音乐描述 |
| `options` | 否   | object | 生成控制项     |

### `options`

| 字段               | 类型    | 说明                                           |
| ------------------ | ------- | ---------------------------------------------- |
| `duration_seconds` | number  | 音乐时长（秒），取 3–300 的 JSON 数字，默认 60 |
| `instrumental`     | boolean | `true` 表示纯音乐，无人声                      |
| `style`            | string  | 曲风或情绪。会改变 `prompt` 的解读方式，见下文 |
| `title`            | string  | 歌名。会改变 `prompt` 的解读方式，见下文       |
| `lyrics`           | string  | 要演唱的歌词                                   |

`options.duration` 和 `options.music_length_ms` 会被拒绝，请使用 `duration_seconds`。

设置了 `style` 或 `title` 时，模型会把 `prompt` 当作歌词。若要描述一首曲子，请把完整描述写在 `prompt` 中，并且不要设置 `style` 和 `title`。若要为歌曲指定曲风，请把歌词写在 `prompt` 中，且不要再传 `lyrics`。

## 计价

音乐费用为 `max(60, ceil(duration_seconds))` 积分：60 秒以内 60 积分，超出部分每秒 1 积分（300 秒为 300 积分）。不传 `duration_seconds` 时生成 60 秒，消耗 60 积分。套餐额度不适用于 API 的音乐生成。`GET /api/v1/models?type=music` 中的 `credits` 为基础价格。

## 示例

```bash
curl -X POST https://viewmax.studio/api/v1/music \
  -H "Authorization: Bearer $VIEWMAX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lofi-001" \
  -d '{
    "prompt": "Warm lo-fi hip-hop with a mellow piano loop",
    "options": { "duration_seconds": 30, "instrumental": true }
  }'
```

该请求消耗 60 积分。

## 成功响应

```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "id": "5c7e9a1b-3d5f-4b6c-8e0a-2f4b6d8e0a12",
    "userId": "usr_123",
    "mediaType": "music",
    "model": "music_v2",
    "prompt": "Warm lo-fi hip-hop with a mellow piano loop",
    "options": "{\"duration_seconds\":30,\"instrumental\":true}",
    "status": "success",
    "createdAt": "2026-09-29T08:00:00.000Z",
    "updatedAt": "2026-09-29T08:00:42.000Z",
    "deletedAt": null,
    "taskId": "sync:5c7e9a1b-3d5f-4b6c-8e0a-2f4b6d8e0a12",
    "taskInfo": null,
    "taskResult": "{\"output\":[\"https://cdn.example.com/music/track.mp3\"]}",
    "taskUrls": ["https://cdn.example.com/music/track.mp3"],
    "costCredits": 60,
    "scene": "text-to-music",
    "visibility": "user",
    "creditId": "cr_789",
    "waivedCredits": 0,
    "fairUseKey": null
  }
}
```

字段说明见[视频生成 API](https://viewmax.studio/zh/docs/api/video-generation)中的**任务字段**。`taskUrls` 始终是数组。

## 幂等与退款

- 发送 `Idempotency-Key`（1–200 个可见 ASCII 字符，不含空格）。重复使用同一个键会返回原任务及其 `costCredits`，不会再次扣费；服务端不比较请求体。超时后用同一个键重试；有意发起新尝试时换用新键。不带该请求头时，每次 POST 都会创建新任务并扣费。
- 任务创建时扣除积分。余额低于费用时返回 402，不创建任务。以 `failed` 或 `canceled` 结束的任务会自动退还积分。

## 错误

所有错误都使用[错误信封](https://viewmax.studio/zh/docs/api/errors)。

| HTTP | `error.type`           | `message` 示例                                                           | 处理方式                                                                                            |
| ---- | ---------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| 400  | `invalid_request`      | `prompt is required`                                                     | 发送非空提示词                                                                                      |
| 400  | `invalid_request`      | `options.duration_seconds must be a number from 3 to 300`                | 发送范围内的 JSON 数字                                                                              |
| 400  | `invalid_request`      | `options.duration is not supported; use options.duration_seconds`        | 改用 `duration_seconds`                                                                             |
| 400  | `invalid_request`      | `the generation request uses unsupported settings`，并带 `error.task_id` | 提供方拒绝了这些设置，任务已失败并退还积分。读取该任务的 `taskInfo.errorMessage`，调整后用新键重试  |
| 400  | `content_rejected`     | `input content was rejected by safety filters`                           | 修改提示词或歌词，不要原样重试                                                                      |
| 402  | `insufficient_credits` | `insufficient credits`                                                   | 未创建任务。补充积分前不要重试                                                                      |
| 500  | `internal_error`       | `the generation service is temporarily unavailable`                      | 带 `error.task_id` 时任务已失败并退还积分，可重试一次。若 hint 说明结果未知，请轮询该任务，不要重试 |
| 503  | `internal_error`       | —                                                                        | 内容安全检查暂不可用，稍后重试                                                                      |
