JIUFENG API

Webhook 回调

任务终态回调 —— 触发规则、payload、签名验证与重试。

本页描述的是网关 真实的 Webhook 契约(区别于早期文档里的"约定示例")。字段与行为以此为准。

用 Webhook 就不用一直轮询:任务进入终态时,平台主动把结果 POST 到你的地址。

怎么开启

生成图片 提交时带上 webhook 字段(一个 base 地址):

{
  "model": "gpt-image-2",
  "prompt": "一只在霓虹城市奔跑的柴犬,赛博朋克风",
  "size": "16:9",
  "resolution": "1K",
  "webhook": "https://your-app.example.com/hooks/jiufeng"
}

平台会在终态时 POST 到 <webhook>/callback/callback 后缀由平台自动拼接)。上例即回调到 https://your-app.example.com/hooks/jiufeng/callback

触发规则

情形是否回调
任务成功(completed推一次
任务失败(failed推一次
中间态(pending / processing不推
任务超时退款不推(这类只能靠 轮询

回调只在终态各推一次。你的接收端应当幂等(同一 id 可能因重试收到多次),且不要把成功链路完全押在回调上——建议保留一个轮询兜底。

Payload

回调 body 就是 查询任务 里的 data 对象本身,不带 { code, data } 外壳

{
  "id": "task_01M0CGM9MRSMD6R6PPNQWXQZ77",
  "status": "completed",
  "progress": "100%",
  "created": 1785076811,
  "completed": 1785076816,
  "actual_time": 5,
  "credits_cost": 12,
  "result": {
    "url": ["https://token-img.jiufeng.ai/f/image/xxx_0.png"],
    "expires_at": 1785163216,
    "image_ids": ["img_01M0..."]
  }
}

失败时 statusfailed,带 error: { message, type, param, code }

签名验证

每次回调带请求头:

X-Webhook-Signature: <hex(HMAC-SHA256(webhook_secret, 原始 body))>
  • webhook_secret账号级设置(在控制台配置),不是请求参数。
  • secret 为空时,平台不带签名头。
  • 原始 body 字节(不是 JSON 解析后再序列化的结果)算 HMAC-SHA256,再和头里的值做常量时间比较

接收端最小示例

Python (Flask)
import hmac
import hashlib
from flask import Flask, request, abort
 
WEBHOOK_SECRET = b"your-account-webhook-secret"
app = Flask(__name__)
 
@app.post("/hooks/jiufeng/callback")
def callback():
    raw = request.get_data()  # 原始 body,勿用解析后的对象
    sig = request.headers.get("X-Webhook-Signature", "")
    expected = hmac.new(WEBHOOK_SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(401)
 
    event = request.get_json()
    if event["status"] == "completed":
        urls = event["result"]["url"]
        # TODO: 尽快下载 / 转存 urls(24h 后过期)
    return "", 200  # 返回 2xx 即 ack,平台不再重试
Node (Express)
import crypto from "node:crypto";
import express from "express";
 
const WEBHOOK_SECRET = "your-account-webhook-secret";
const app = express();
 
// 用 raw body 验签,勿先 JSON 解析
app.post(
  "/hooks/jiufeng/callback",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.get("X-Webhook-Signature") || "";
    const expected = crypto
      .createHmac("sha256", WEBHOOK_SECRET)
      .update(req.body)
      .digest("hex");
    const ok =
      sig.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
    if (!ok) return res.sendStatus(401);
 
    const event = JSON.parse(req.body.toString());
    if (event.status === "completed") {
      // event.result.url ...
    }
    res.sendStatus(200); // 返回 2xx 即 ack
  }
);

重试策略

你的接收端返回平台行为
2xxack,完成,不再推送
4xx(非 429视为永久失败,不重试
5xx / 超时 / 网络错误 / 429退避重投:10s → 30s → 60s,最多 3 次;仍失败进死信队列
  • 单次回调请求超时 10s,请尽快返回(重活异步做,先回 200)。
  • 想要"稳成功",接收端应尽量快 ack、把下载转存放到后台任务里。

本页目录

Webhook 回调 · Jiufeng Open API