不用再轮询。无论请求是同步的(图像模型)还是异步的(视频模型、排队较久的任务),只要生成一落地,Flixly就会向你的URL POST一个带签名的事件。
每次投递都带有对`${timestamp}.${body}`计算出的签名,你可以据此验证它确实来自我们,并且没有被篡改。
X-Flixly-Timestamp请求头让你可以拒绝超过约5分钟的签名,用来防范被截获后的重放攻击。
共3次尝试,采用指数退避(立即、+2秒、+6秒)。每次尝试的超时时间为5秒。轮询/generations/{id}是你的兜底方案。
每个API密钥都有自己的webhook签名密钥。你可以随时在工作台里轮换,旧签名会立即校验失败。
每完成(或失败)一次生成就POST一次。JSON请求体,附带签名请求头。
POST https://your-server.example.com/flixly-webhook
Content-Type: application/json
User-Agent: Flixly-Webhook/1.0
X-Flixly-Event: generation.completed
X-Flixly-Timestamp: 1781085600
X-Flixly-Signature: sha256=abc123...
X-Flixly-Delivery-Id: wh_a1b2c3d4e5f6g7h8{
"event": "generation.completed",
"id": "j5h2k9...",
"status": "completed",
"type": "TEXT_TO_IMAGE",
"model": "flux-dev",
"output_url": "https://cdn.flixly.ai/outputs/...",
"credits_charged": 1,
"error": null,
"created_at": "2026-06-06T12:00:00Z",
"completed_at": "2026-06-06T12:00:05Z"
}generation.completed或generation.failed。POST /api/v1/generate返回给你、并用于GET /api/v1/generations/{id}的那一个。completed或failed。null。null。已做过脱敏处理,可以安全地展示给你的用户。我们用HMAC-SHA256对`${timestamp}.${body}`为每次投递签名。你在自己的服务器上,用生成密钥时展示给你的那串值重新计算HMAC,然后用常数时间比较。
JSON.parse之后再JSON.stringify会改变空白字符和字段顺序,签名因此对不上。在Express中请使用raw-body中间件;在Next.js App Router中,请先调用await req.text(),再调用await req.json()。
import { Flixly } from "@flixly/sdk";
// In your webhook handler — Express, Hono, Next.js, etc.
export async function POST(req) {
const rawBody = await req.text(); // MUST be raw — re-stringifying breaks the signature
const signature = req.headers.get("x-flixly-signature");
const timestamp = req.headers.get("x-flixly-timestamp");
const valid = await Flixly.verifyWebhookSignature({
secret: process.env.FLIXLY_WEBHOOK_SECRET,
timestamp,
signature,
body: rawBody,
tolerance: 300, // optional — defaults to 300s replay window
});
if (!valid) {
return new Response("invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
// event.event === "generation.completed" | "generation.failed"
// event.id, event.status, event.output_url, event.credits_charged, ...
await handleGenerationEvent(event);
return new Response("ok");
}无论同步还是异步,每次投递都遵循同样的流程:
2xx响应。如果没什么要返回的,回一个空响应体的200 OK就行。301、302等3xx响应视为失败——它们会在下一跳中悄悄丢掉X-Flixly-Signature请求头。每次投递都带有唯一的X-Flixly-Delivery-Id请求头。如果重试导致你收到同一个事件两次(比如我们的重试已经发出,而你之前那次响应太慢才到达),可以用这个请求头在自己这边去重。
事件中的id字段是生成任务id——同一次投递的多次重试中它保持不变。只有当我们重新构建载荷时,X-Flixly-Delivery-Id才会逐次变化(目前我们不会这么做)。