平台

回调 (webhooks)

无需轮询,只需在图片或视频请求中添加 callback_url。生成完成或失败时,Zurelay 会向该 URL POST 结果,并附上签名,供你验证来源。

设置回调

在以下任一请求中添加 callback_url。它必须是可通过公网访问的 https URL。

端点执行内容
POST /v1/videos照常启动视频生成。任务结束后,回调会返回生成的视频或失败原因。无需轮询。
POST /v1/images/generations立即返回 202 和请求 id,不等待图片生成;随后在后台生成图片,完成后发送回调。
POST /v1/images/edits图片编辑同理:在 JSON 中添加字段,或在 multipart 请求中添加表单字段。
curl https://api.zurelay.com/v1/videos \
-H "Authorization: Bearer $ZURELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"prompt": "A fox running through fresh snow at dawn",
"seconds": 5,
"callback_url": "https://example.com/hooks/zurelay"
}'

带回调的图片请求

不带 callback_url 时,图片请求会等待生成完成后返回图片,与 OpenAI 的 API 一致。添加后,请求会立即被接受并返回任务对象。仅在生成图片后计费:

响应
HTTP/1.1 202 Accepted
x-request-id: req_6fJd0kQ3pW9xT2mB7cZ1hR4u
{
"id": "req_6fJd0kQ3pW9xT2mB7cZ1hR4u",
"object": "image.generation",
"model": "nano-banana-pro",
"status": "in_progress",
"created_at": 1791000000,
"completed_at": null,
"data": [],
"url_expires_at": null,
"cost": null,
"error": null,
"callback_url": "https://example.com/hooks/zurelay"
}
GET/v1/images/{id}

你也可以轮询:GET /v1/images/{id} 返回同一个对象,当其 status 为 completed 时,data 中会提供新的链接。无论 response_format 如何设置,回调始终返回链接,不返回 base64.

回调内容

每个事件发送一次 POST,JSON 请求体包含 type、timestamp 和 data。其中的任务对象与 API 返回的完全一致(视频对象或上面的图片请求对象),下载链接有效期为 24 小时。

事件触发条件
video.completed视频已生成:通过 data.url 下载。
video.failed视频生成失败:data.error 说明原因。未收取费用。
image.completed图片已生成:data.data 包含每张图片的链接。
image.failed未生成任何图片:data.error 说明原因。未收取费用。
视频回调示例
POST /hooks/zurelay HTTP/1.1
content-type: application/json
user-agent: Zurelay-Webhooks/1.0 (+https://zurelay.com/docs/webhooks)
webhook-id: msg_2c1f9e0b7a4d4f6e8b3a91c05d7e6f42
webhook-timestamp: 1791000094
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
"type": "video.completed",
"timestamp": "2026-10-03T03:21:34.000Z",
"data": {
"id": "video_Qm3v8Rk2Lx7Pa1Zt9Ws4Yn6B",
"object": "video",
"model": "seedance-2.0",
"status": "completed",
"progress": 100,
"created_at": 1791000000,
"completed_at": 1791000093,
"expires_at": 1793592000,
"seconds": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"size": "1280x720",
"generate_audio": true,
"url": "https://…/video_Qm3v8Rk2Lx7Pa1Zt9Ws4Yn6B.mp4?token=…",
"url_expires_at": 1791086494,
"cost": 0.45,
"error": null,
"callback_url": "https://example.com/hooks/zurelay"
}
}
图片回调示例(请求体)
{
"type": "image.completed",
"timestamp": "2026-10-03T03:20:41.000Z",
"data": {
"id": "req_6fJd0kQ3pW9xT2mB7cZ1hR4u",
"object": "image.generation",
"model": "nano-banana-pro",
"status": "completed",
"created_at": 1791000000,
"completed_at": 1791000041,
"data": [{ "url": "https://…/req_6fJd0kQ3pW9xT2mB7cZ1hR4u-1.png?token=…" }],
"url_expires_at": 1791086441,
"cost": 0.07,
"error": null,
"callback_url": "https://example.com/hooks/zurelay"
}
}
请求头值
webhook-idmsg_…: 同一回调的每次重试都使用相同的值。用它跳过已处理的回调。
webhook-timestamp本次发送的时间,以 Unix 时间戳表示,单位为秒。
webhook-signaturev1 后跟签名(见下文)。轮换密钥后的一天内会包含两个签名,以空格分隔。
user-agentZurelay-Webhooks/1.0

验证签名

回调遵循 Standard Webhooks 格式,与 OpenAI 的 webhook 使用相同格式。签名以工作区的签名密钥对 <webhook-id>.<webhook-timestamp>.<body> 进行 HMAC-SHA256 计算,再以 base64 编码。你可以在控制台的 API 密钥页面找到该密钥(以 whsec_ 开头),请仅将其保存在服务端。

import os
from fastapi import FastAPI, HTTPException, Request
from standardwebhooks.webhooks import Webhook # pip install standardwebhooks
webhook = Webhook(os.environ["ZURELAY_WEBHOOK_SECRET"]) # whsec_…
app = FastAPI()
@app.post("/hooks/zurelay")
async def zurelay_hook(request: Request):
body = await request.body() # the raw bytes: the signature covers exactly these
try:
event = webhook.verify(body, dict(request.headers))
except Exception:
raise HTTPException(status_code=400)
handle(event) # event["type"], event["data"]; keep it quick, or queue it
return {"ok": True}
  • 请使用原始请求体验证:解析并重新序列化 JSON 会改变字节内容,导致签名不匹配。
  • 拒绝 webhook-timestamp 早于当前时间五分钟以上的回调,防止有人截获回调后重放。相关库会自动完成这项检查。
  • 所有者和管理员可在 API 密钥页面轮换密钥,新密钥会立即生成。旧密钥仍会与新密钥同时用于签名,持续 24 小时,因此请在一天内完成服务端切换。

先响应,再处理

在 15 秒内返回任意 2xx,即视为回调已送达。请先保存事件并响应,再下载视频或图片。回调可能重复送达(例如响应在传输途中丢失),因此请使用 webhook-id 确保每个事件只处理一次。

重试

其他情况(错误状态码、重定向、15 秒内未响应或服务器无法连接)都会触发重试,总共尝试六次:

尝试次数发送时间
1任务结束后立即发送
21 分钟后
3再过 5 分钟
4再过 30 分钟
5再过 2 小时
6再过 8 小时,之后标记为失败

每条回调都会随对应请求显示在控制台的请求中:包括发送地址、每次尝试的响应,以及下次重试时间。点击其中的重试回调即可立即重新发送,无论之前是否已送达。每次尝试都会更新回调中的链接。

回调仅发送到公网 https URL,不支持 localhost、私有地址或解析到此类地址的域名。回调不会跟随重定向,请使用最终 URL。开发时,可通过隧道(ngrok、Cloudflare Tunnel)或请求检查工具测试回调。

有疑问,或发现内容缺失?联系我们的支持团队,我们会通过邮件回复,通常只需一分钟。