APIMaster 异步任务 Webhook
接收 Seedance、视频和异步图像任务的签名完成与失败通知,支持重试、投递历史记录和轮询回退机制。
APIMaster 异步任务 Webhook
将 Webhook 作为异步生成的主要通知机制,并保留轮询作为备用方案。当被跟踪的任务完成或失败时,APIMaster 会发送一个签名的 HTTPS POST 请求。通知与生成服务提供商无关,并使用您的 APIMaster 任务 ID。
支持的模型和端点
Seedance 包含全部四个模型:seedance-2.0、seedance-2.5、seedance-2.0-fast 和 seedance-2.0-mini。请使用 POST /v1/videos/generations 或兼容的 POST /v1/video/generations。
视频任务通知还支持 MiniMax-H3、Kling Omni / Motion Control、Grok Imagine Video 和 Sora。支持的 GPT Image 2 / 2.5 和 Gemini 图像工作流使用 /v1/images/generations/async 或支持的 /v1/images/edits/async;Midjourney v8.2 / Niji 7 使用 /v1/midjourney/generations。路由可用性和生成参数仍需遵循每个模型的指南。同步的 Seedream 和 Grok 图像生成、聊天流式传输以及 Seedance 资产审核不属于此生成 Webhook 合约的一部分。
请查阅 GET /v1/webhook-capabilities?model=seedance-2.0-fast 以了解模型资格。请使用受支持的异步提交端点;具备资格并不意味着每个图像操作或每个模型别名都支持异步生成。
注册并验证端点
在 Authorization: Bearer YOUR_API_KEY 中使用您的 API 密钥。一个 API 密钥可以管理其所属账户的端点和事件。请将其保存在您的服务器上。
curl --fail-with-body "https://apimaster.ai/v1/webhook-endpoints" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-server.example/webhooks/apimaster"}'
响应包含 endpoint.id、endpoint.key_id 和 signing_secret。请安全地保存签名密钥:它仅返回一次。它与您的 API 密钥是分开的,不能用于授权生成或下载。URL 必须是公开可达的 HTTPS 端点,端口为 443。私有地址和重定向会被拒绝。每个账户最多允许 20 个端点;URL 是不可变的,因此更改目标地址时,请创建并验证一个新端点。
配置好您的接收器后,验证所有权:
curl --fail-with-body -X POST "https://apimaster.ai/v1/webhook-endpoints/whep_example/verify" \
-H "Authorization: Bearer YOUR_API_KEY"
APIMaster 会发送一个签名的 webhook.endpoint_verification 事件,其中包含 data.challenge。请在 10 秒内返回 HTTP 200 状态码及 {"challenge":"THE_RECEIVED_CHALLENGE"}。引用未验证、已暂停或属于其他账户的端点的生成请求将在提交前被拒绝。
HTTP 请求总超时为 10 秒(包括 DNS、连接和 TLS 握手),发送请求后等待响应头的上限为 8 秒。接收端应持久化事件后立即确认。
提交带通知的任务
curl --fail-with-body "https://apimaster.ai/v1/videos/generations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model":"seedance-2.5",
"prompt":"A paper boat floating on a calm pond, slow camera movement.",
"duration":4,
"resolution":"480p",
"aspect_ratio":"16:9",
"notifications":{
"webhook":{
"endpoint_id":"whep_example",
"events":["task.completed","task.failed"]
}
},
"client_reference_id":"order_123"
}'
将模型 ID 替换为四个 Seedance ID 中的任意一个,同时保留其自身支持的参数。请保存正常创建响应中的任务 ID。client_reference_id 是一个可选的业务参考标识,最多 128 字节;它不是提交幂等性密钥。请勿将 notifications 与旧的 webhook 或 callback_url 提供商参数结合使用。
对于支持的多部分图像编辑,请将 notifications 作为 JSON 字符串表单字段发送,并将 client_reference_id 作为单独的表单字段发送:
curl --fail-with-body "https://apimaster.ai/v1/images/edits/async" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "model=gpt-image-2" \
-F "prompt=Turn the reference into a watercolor illustration" \
-F "image=@reference.png" \
-F 'notifications={"webhook":{"endpoint_id":"whep_example","events":["task.completed","task.failed"]}}' \
-F "client_reference_id=image_order_123"
此版本中没有账户级别的默认端点:需要在每个任务请求中引用一个端点。Webhook 可能在您的提交响应返回之前到达。即使您的应用程序尚未保存返回的任务 ID,也请持久化这些通知。某些图像异步工作流包装了同步的上游生成,因此提交本身可能仍需要时间。
成功和失败负载
成功通知包含公共任务 ID 和一个经过身份验证的视频链接:
{
"id":"evt_example",
"type":"task.completed",
"api_version":"2026-10-03",
"created_at":1791014400,
"data":{
"task_id":"task_example",
"model":"seedance-2.5",
"kind":"video",
"status":"completed",
"client_reference_id":"order_123",
"completed_at":1791014400,
"task_url":"https://apimaster.ai/v1/videos/task_example",
"result":{
"outputs":[{
"output_id":"0",
"type":"video",
"url":"https://apimaster.ai/v1/videos/task_example/content",
"authentication":"bearer",
"expires_at":null
}]
},
"error":null
}
}
失败通知使用相同的信封结构,包含 type: "task.failed"、data.status: "failed"、data.result: null 和一个错误对象:
{
"code":"generation_failed",
"message":"The generation task failed. Query the task for details."
}
提供商确认的失败和网关任务超时都可能生成失败事件。在任务被接受之前就被拒绝的创建请求不会生成任务事件。通知投递失败不会改变生成状态或重新运行生成。事件不确认账单对账是否已完成。
对于图像,kind 为 image,且 outputs 包含所有可用的图像输出。Midjourney 子任务事件包含 batch_id;您还可以额外订阅 batch.completed,以便在所有子任务完成后接收一个批次摘要。其状态为 completed、partial_failure 或 failed,计数位于 data.summary 中。批次完成并不总是意味着每个子任务都成功。
请使用属于任务账户的 API 密钥下载输出 URL。请及时保存生成的媒体。expires_at: null 意味着未提供保证的过期时间,而非永久存储;后续下载可能返回 HTTP 410。重放事件不会延长媒体的可用性。
验证回调并及时确认
每次POST请求包含:
X-APIMaster-Event-ID: evt_example
X-APIMaster-Delivery-ID: dlv_example
X-APIMaster-Timestamp: 1791014400
X-APIMaster-Signature: v1=<hex-hmac>,kid=<key-id>
使用端点的签名密钥,对时间戳、一个文字点号和原始请求体字节进行HMAC-SHA256验证。使用恒定时间比较,最多允许五分钟的时钟偏差,并检查Event-ID标头是否与正文中的ID匹配。重试会保留相同的事件ID和正文,但会获得新的投递ID、时间戳和签名。
以下Python接收器使用Flask和SQLite。它在确认事件之前以原子方式持久化事件。您的应用程序工作进程应单独消费已保存的行、下载文件并执行业务逻辑。
import hashlib, hmac, json, os, sqlite3, time
from flask import Flask, request, jsonify, abort
app = Flask(__name__)
# During key rotation, configure both active and previous kid/secret pairs.
SECRETS = json.loads(os.environ["APIMASTER_WEBHOOK_SECRETS_JSON"])
DB_PATH = os.environ.get("WEBHOOK_DB_PATH", "webhook-events.sqlite3")
with sqlite3.connect(DB_PATH) as db:
db.execute("CREATE TABLE IF NOT EXISTS events (id TEXT PRIMARY KEY, body BLOB NOT NULL)")
@app.post("/webhooks/apimaster")
def receive():
raw = request.get_data()
try:
timestamp = request.headers["X-APIMaster-Timestamp"]
if abs(time.time() - int(timestamp)) > 300:
abort(401)
fields = dict(part.split("=", 1) for part in
request.headers["X-APIMaster-Signature"].split(","))
secret = SECRETS[fields["kid"]]
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw,
hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, fields["v1"]):
abort(401)
event = json.loads(raw)
if event["id"] != request.headers["X-APIMaster-Event-ID"]:
abort(401)
except (KeyError, ValueError, TypeError):
abort(401)
if event["type"] == "webhook.endpoint_verification":
return jsonify(challenge=event["data"]["challenge"])
with sqlite3.connect(DB_PATH) as db:
db.execute("INSERT OR IGNORE INTO events(id, body) VALUES (?, ?)",
(event["id"], raw))
return "", 204
对于生成事件,请在10秒内返回任何2xx状态。在持久化存储后立即确认,无需等待媒体下载。投递是至少一次:重复事件是可能的,特别是在确认丢失的情况下,因此需要通过id进行去重。跨任务没有顺序保证。
重试、投递历史与重放
APIMaster会对网络故障、确认超时和非2xx响应进行重试,最长持续自事件创建起72小时。初始延迟为1、2、5、15和30分钟,然后是1、2、4和8小时,接着是12小时间隔,最多有20%的抖动。在重试窗口内,429/503状态码上的有效Retry-After会被遵守。不跟随重定向。HTTP 410会暂停端点;其他4xx响应会被重试,以便允许配置修复。
curl --fail-with-body "https://apimaster.ai/v1/webhook-events?task_id=task_example" \
-H "Authorization: Bearer YOUR_API_KEY"
curl --fail-with-body "https://apimaster.ai/v1/webhook-events/evt_example/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY"
curl --fail-with-body -X POST "https://apimaster.ai/v1/webhook-events/evt_example/redeliver" \
-H "Authorization: Bearer YOUR_API_KEY"
状态包括pending、in_flight、retrying、delivered、paused和exhausted。事件和尝试记录会保留30天。重放会保留原始事件ID和负载,并开启一个新的72小时投递窗口;它不会重新生成媒体或再次收费。每个UTC日每个事件最多允许五次手动重放。当前已租约用于投递的事件,在该次尝试结束或其租约到期之前无法重放。
使用PATCH /v1/webhook-endpoints/{id}和{"enabled":false}或{"enabled":true}来暂停或恢复端点。恢复会继续处理那些原始截止时间尚未过期的已暂停事件。使用POST /v1/webhook-endpoints/{id}/rotate-secret轮换密钥;保存新的密钥和kid,并在返回的24小时过渡期内保留前一对密钥。
轮询后备方案
对于未收到终端事件的任务,每60-120秒查询一次/v1/videos/{task_id}或模型文档中记录的图像任务查询。使用任务API作为事实来源,如果生成已完成但通知缺失,请检查投递历史。缺少webhook不是重新提交可计费生成请求的理由。持续的端点停机可能会耗尽自动重试;请恢复接收器并重放保留的事件。
