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

# 音乐 Webhook

# 音乐 Webhook 回调

除了轮询[查询音乐详情](https://docs.poyo.ai/zh/api-manual/music-series/query-music-detail.md)接口，你还可以在提交音乐生成任务时提供 `callback_url`，在生成完成时自动接收通知。

## 工作原理

1. **提交时附带回调 URL**：在音乐生成请求中加入 `callback_url` 参数
2. **任务处理**：PoYo AI 处理你的音乐生成任务
3. **接收通知**：当任务状态变为 `finished` 或 `failed` 时，PoYo AI 会向你的回调 URL 发送 POST 请求
4. **处理结果**：你的服务器会收到完整的任务数据，包括生成的文件

## 回调请求

任务完成时，PoYo AI 会向你的回调 URL 发送 POST 请求：

### 请求头

```http theme={"dark"}
Content-Type: application/json
X-Webhook-Timestamp: 1783680000
X-Webhook-Signature: base64_hmac_sha256_signature
```

### 请求体结构

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [...],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

> [!NOTE]
>
> Webhook 请求体使用与音乐详情响应体相同的 `task_id`、`status`、`files`、`created_time` 和 `error_message` 字段，但不包含外层的 `code` 和 `data` 包装。Webhook 目前不包含仅在详情接口中返回的 `credits_amount` 和 `progress` 字段。`files` 数组的内容因模型而异。

## 签名验证

PoYo 使用 HMAC-SHA256 对每个音乐 Webhook 请求签名。现有接收端可以忽略这些请求头，但生产系统应当验证签名。

获取你的 Webhook HMAC 密钥：

```bash theme={"dark"}
GET /api/api-keys/webhook-secret
```

轮换你的 Webhook HMAC 密钥：

```bash theme={"dark"}
POST /api/api-keys/webhook-secret/rotate
```

签名规则：

```text theme={"dark"}
base64(HMAC-SHA256(task_id + "." + timestamp, webhook_hmac_key))
```

其中：

* `task_id` 是回调请求体中顶层的 `task_id`
* `timestamp` 是 `X-Webhook-Timestamp` 的值
* `webhook_hmac_key` 由 `GET /api/api-keys/webhook-secret` 返回

### Python 验证

```python theme={"dark"}
import base64
import hashlib
import hmac
import time


def verify_poyo_webhook(payload, headers, webhook_hmac_key):
    task_id = payload.get("task_id")
    timestamp = headers.get("X-Webhook-Timestamp")
    received_signature = headers.get("X-Webhook-Signature")

    if not task_id or not timestamp or not received_signature:
        return False

    if abs(int(time.time()) - int(timestamp)) > 300:
        return False

    expected_signature = base64.b64encode(
        hmac.new(
            webhook_hmac_key.encode("utf-8"),
            f"{task_id}.{timestamp}".encode("utf-8"),
            hashlib.sha256,
        ).digest()
    ).decode("utf-8")

    return hmac.compare_digest(expected_signature, received_signature)
```

## files 数据响应字段

`files` 数组的内容取决于创建任务的模型。

| 模型 | 字段 | 类型 | 说明 |
| - | - | - | - |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `audio_id` | string | 音频文件的唯一标识 |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `audio_url` | string | 音频文件的直接下载 URL |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `image_url` | string | 封面图 URL |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `title` | string | 曲目标题 |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `tags` | string | 风格标签 |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `duration` | number | 音频时长（秒） |
| `generate-music`, `extend-music`, `upload-and-cover-audio`, `upload-and-extend-audio`, `add-instrumental`, `add-vocals`, `replace-section` | `prompt` | string | 生成时使用的提示词 |
| `generate-mashup`, `generate-sounds` | `audio_id`, `audio_url`, `stream_audio_url`, `image_url`, `title`, `tags`, `prompt`, `duration` | mixed | 生成的音频字段（如有） |
| 标准音乐结果 | `model_name`, `source_audio_url`, `source_image_url`, `source_stream_audio_url` | string | 模型及衍生来源元数据（如有） |
| `minimax-music-2.6` | `audio_id` | string | 生成音频文件的唯一标识 |
| `minimax-music-2.6` | `audio_url` | string | 生成音频文件的直接下载 URL |
| `minimax-music-2.6` | `title` | string | 根据提交的提示词生成的曲目标题 |
| `minimax-music-2.6` | `duration` | number | 音频时长（秒） |
| `minimax-music-2.6` | `prompt` | string | 生成时使用的原始提示词 |
| `get-timestamped-lyrics` | `timestampe_lyrics` | string | 带时间戳的歌词 |
| `generate-lyrics` | `title` | string | 歌词标题 |
| `generate-lyrics` | `text` | string | 完整歌词内容 |
| `boost-music-style` | `style` | string | 增强后的音乐风格 |
| `convert-to-wav` | `wav_url` | string | 转换后 WAV 文件的 URL |
| `separate-vocals` | `separate_vocals` | object | 分离后的音频 URL。[查看详情](#separate-vocals) |
| `upload-and-separate-vocals` | `vocal_removal` | object | 分离后的音轨 URL。[查看详情](#upload-and-separate-vocals) |
| `stem-split` | `stem_split` | object | 完整音轨分离 URL。[查看详情](#stem-split) |
| `generate-music-cover` | `generate_cover` | array | 生成的封面图。[查看详情](#generate-music-cover) |
| `generate-persona` | `persona_id` | string | 生成的 persona 标识 |
| `create-music-video` | `video_url` | string | 生成的视频 URL |
| `suno-voice-validate`, `suno-voice-regenerate` | `validate_info`, `voice_status` | string \| null | 验证短语及当前验证状态 |
| `suno-voice-generate` | `voice_id`, `voice_status` | string \| null | 生成的语音标识及创建状态 |
| `suno-voice-check` | `is_available` | boolean | 生成的语音当前是否可用 |
| Suno Voice 任务 | `error_message`, `error_code` | string/number \| null | 结构化错误详情（如有） |

### JSON 字段详情

#### separate-vocals

`separate_vocals` 字段：

* `vocal_url` - 提取出的人声音轨 URL
* `instrumental_url` - 伴奏音轨 URL

#### upload-and-separate-vocals

`vocal_removal` 字段：

* `bass` - 贝斯音轨 URL
* `drums` - 鼓音轨 URL
* `piano` - 钢琴音轨 URL
* `guitar` - 吉他音轨 URL
* `vocals` - 人声音轨 URL
* `other` - 其他音频元素 URL

#### stem-split

`stem_split` 字段：

* `backing_vocals_url` - 和声 URL
* `bass_url` - 贝斯音轨 URL
* `brass_url` - 铜管乐器 URL
* `drums_url` - 鼓音轨 URL
* `fx_url` - 音效 URL
* `guitar_url` - 吉他音轨 URL
* `keyboard_url` - 键盘音轨 URL
* `percussion_url` - 打击乐音轨 URL
* `strings_url` - 弦乐音轨 URL
* `synth_url` - 合成器音轨 URL
* `vocal_url` - 主唱人声 URL
* `woodwinds_url` - 木管乐器音轨 URL
* `piano_url` - 钢琴音轨 URL

#### generate-music-cover

`generate_cover` 是包含封面图对象的 JSON 数组：

* `file_url` - 生成的封面图 URL
* `file_type` - 文件类型（例如 `image`）

## 回调示例

### 音乐生成（generate-music、extend-music 等）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "audio_id": "62e4542a-73be-44e1-b397-7716ee7505c6",
      "audio_url": "https://storage.poyo.ai/audio/8FDN1I7M7Q68DDG8/audio_62e4542a.mp3",
      "image_url": "https://storage.poyo.ai/audio/8FDN1I7M7Q68DDG8/cover_62e4542a.jpeg",
      "title": "Peaceful Piano Meditation",
      "tags": "Classical",
      "duration": 240.0,
      "prompt": ""
    },
    {
      "audio_id": "8e755734-a840-4c13-beac-b9f1044979ca",
      "audio_url": "https://storage.poyo.ai/audio/8FDN1I7M7Q68DDG8/audio_8e755734.mp3",
      "image_url": "https://storage.poyo.ai/audio/8FDN1I7M7Q68DDG8/cover_8e755734.jpeg",
      "title": "Peaceful Piano Meditation",
      "tags": "Classical",
      "duration": 130.0,
      "prompt": ""
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### Suno Voice Generate

```json theme={"dark"}
{"task_id":"POYO_VOICE_TASK","status":"finished","files":[{"voice_status":"success","voice_id":"voice_abc123"}],"created_time":"2026-08-26T10:00:00","error_message":null}
```

### Suno Voice Validate 或 Regenerate

```json theme={"dark"}
{"task_id":"POYO_VOICE_VALIDATE_TASK","status":"finished","files":[{"voice_status":"wait_validating","validate_info":"Read the displayed verification phrase clearly."}],"created_time":"2026-08-26T10:00:00","error_message":null}
```

### Suno Voice Check

```json theme={"dark"}
{"task_id":"POYO_VOICE_CHECK_TASK","status":"finished","files":[{"is_available":true}],"created_time":"2026-08-26T10:01:00","error_message":null}
```

### MiniMax Music 2.6

```json theme={"dark"}
{
  "task_id": "task-unified-1757165031-uyujaw3d",
  "status": "finished",
  "files": [
    {
      "audio_id": "task-unified-1757165031-uyujaw3d_audio",
      "audio_url": "https://storage.poyo.ai/audios/task-unified-1757165031-uyujaw3d/output.mp3",
      "title": "City pop, 80s retro, groovy synth bass",
      "duration": 0,
      "prompt": "City pop, 80s retro, groovy synth bass"
    }
  ],
  "created_time": "2026-04-15T10:30:00",
  "error_message": null
}
```

### 带时间戳的歌词（get-timestamped-lyrics）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "timestampe_lyrics": "[00:00.00] First line of lyrics\n[00:05.23] Second line of lyrics"
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 生成的歌词（generate-lyrics）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "title": "Peaceful Piano Meditation",
      "text": "Peaceful Piano Meditation lyrics content..."
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 音乐风格增强（boost-music-style）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "style": "Enhanced music style result"
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### WAV 转换（convert-to-wav）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "wav_url": "https://storage.poyo.ai/audio/8FDN1I7M7Q68DDG8/output.wav"
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 人声分离（separate-vocals）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "separate_vocals": {
        "vocal_url": "https://storage.poyo.ai/audio/vocals.mp3",
        "instrumental_url": "https://storage.poyo.ai/audio/instrumental.mp3"
      }
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 上传并分离人声（upload-and-separate-vocals）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "vocal_removal": {
        "bass": "https://storage.poyo.ai/audio/bass.mp3",
        "drums": "https://storage.poyo.ai/audio/drums.mp3",
        "piano": "https://storage.poyo.ai/audio/piano.mp3",
        "guitar": "https://storage.poyo.ai/audio/guitar.mp3",
        "vocals": "https://storage.poyo.ai/audio/vocals.mp3",
        "other": "https://storage.poyo.ai/audio/other.mp3"
      }
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 音轨分离（stem-split）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "stem_split": {
        "backing_vocals_url": "",
        "bass_url": "",
        "brass_url": "",
        "drums_url": "",
        "fx_url": "",
        "guitar_url": "",
        "keyboard_url": "",
        "percussion_url": "",
        "strings_url": "",
        "synth_url": "",
        "vocal_url": "",
        "woodwinds_url": ""
      }
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 音乐封面图（generate-music-cover）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "generate_cover": [
        {"file_url": "https://storage.poyo.ai/audio/cover_1.png", "file_type": "image"},
        {"file_url": "https://storage.poyo.ai/audio/cover_2.png", "file_type": "image"}
      ]
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 生成的 Persona（generate-persona）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "persona_id": "persona_abc123"
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 音乐视频（create-music-video）

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "finished",
  "files": [
    {
      "video_url": "https://storage.poyo.ai/video/8FDN1I7M7Q68DDG8/music_video.mp4"
    }
  ],
  "created_time": "2025-11-25T08:50:13",
  "error_message": null
}
```

### 失败任务

```json theme={"dark"}
{
  "task_id": "8FDN1I7M7Q68DDG8",
  "status": "failed",
  "files": [],
  "created_time": "2025-11-25T08:50:13",
  "error_message": "The prompt violates our content policy"
}
```

## 要求

你的回调接口必须满足以下要求：

* **仅支持 HTTPS**：必须使用 HTTPS 协议（不支持 HTTP）
* **URL 最大长度**：2048 个字符
* **响应超时**：必须在 30 秒内响应
* **成功响应**：应返回 HTTP 200-299 状态码
* **禁止内网 IP**：不能使用私有/内网 IP 地址（例如 `192.168.x.x` 和 `10.x.x.x`）
* **公网可访问**：必须能从互联网公开访问

## 重试策略

如果你的回调接口未响应或返回错误：

* **重试次数**：最多自动重试 5 次
* **重试间隔**：指数退避，从约 60 秒开始，最长 10 分钟
* **最终失败**：所有重试均失败后，不再继续重试

> [!WARNING]
>
> 如果所有重试均失败，你仍可以通过轮询[查询音乐详情](https://docs.poyo.ai/zh/api-manual/music-series/query-music-detail.md)接口获取结果。

## 安全最佳实践

> [!TIP]
>
> **验证请求**：处理回调前先校验 `X-Webhook-Timestamp` 和 `X-Webhook-Signature`。

> [!TIP]
>
> **幂等性**：将 Webhook 处理程序设计为幂等，以应对重复投递

> [!TIP]
>
> **异步处理**：异步处理回调并尽快返回 200，避免超时

> [!TIP]
>
> **日志记录**：记录所有 Webhook 请求，便于调试和监控


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.