任务完成回调(Webhook)

提交异步生成任务时带上回调地址,任务完成(成功或失败)后主动把结果 POST 给你,并可指定失败错误文案的语言。

提交视频 / 图片 / 音频等异步生成任务时,可以带上一个回调地址。任务完成(成功或失败)后,我们会主动把结果 POST 到你的地址,你就不用一直轮询查询了。对于视频和图片生成任务,还可以通过 language 指定失败回调中错误文案的语言。

快速开始

提交任务时,在请求体顶层加入 webhook 字段;如需翻译失败回调中的错误文案,再加入 language

curl -X POST https://你的接入域名/v1/images/generations \
  -H "Authorization: Bearer 你的APIKey" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red apple on a table",
    "size": "1024x1024",
    "webhook": "https://your-server.com",
    "language": "zh"
  }'

任务做完后,我们会向 你的地址 + /callback 发一个 POST 请求。

视频、音频等其它异步任务接口同理,webhook 应放在提交请求体的顶层。language 目前适用于 POST /v1/videos/generationsPOST /v1/images/generations,同样放在请求体顶层。

指定错误文案语言

language 是可选字符串参数,只影响失败回调中的 error.message。任务 ID、状态、进度、金额和结果 URL 等字段不会随语言变化。不传时,错误文案保持上游或平台返回的原文。

语言参数值语言参数值
EnglishenРусскийru
简体中文zhFrançaisfr
日本語jaDeutschde
한국어koBahasa Indonesiaid
PortuguêsptEspañoles
  • 参数值不区分大小写,首尾空格会自动移除,例如 "ZH"" zh " 都会按 zh 处理。
  • 请使用表格中的两位语言码。zh-CNen-USpt-BR 等带地区标签的值不会被识别。
  • 不支持的参数值不会导致任务提交失败,回调会保留原始错误文案。
  • 如果错误原文已经是目标语言,会直接返回原文,不会重复翻译。
  • 翻译失败时会回退到原始错误文案,不会影响回调投递。

轮询任务时,可通过任务状态接口的 language 查询参数指定相同的错误文案语言。Webhook 没有查询参数,因此必须在提交任务时指定 language

POST /mj/submit/*POST /v1/images/edits 不支持 webhook / language。官方 xAI 图片模型不支持 language,携带该参数会返回 400 parameter "language" is not supported;调用这些模型时请不要传入该参数。

地址规则

你填的 webhook基础地址(base),我们会自动在后面拼 /callback

你填的 webhook我们实际 POST 到
https://your-server.comhttps://your-server.com/callback
https://your-server.com/apihttps://your-server.com/api/callback
https://your-server.com/api/https://your-server.com/api/callback

所以你的服务端需要准备一个接收 POST .../callback 的接口。

你会收到什么

推送内容和「获取任务状态」接口返回的完全一致——你用同一套解析逻辑处理即可。

{
  "id": "task_01KV7FXR8BEYS1BWHJCT3JMCJ5",
  "status": "completed",
  "progress": 100,
  "created": 1781589029,
  "completed": 1781589058,
  "actual_time": 29,
  "cost": 0.006,
  "credits_cost": 0.06,
  "result": {
    "images": [{ "url": ["https://.../result.png"], "expires_at": 1781675458 }]
  }
}
{
  "id": "task_xxx",
  "status": "failed",
  "progress": 100,
  "created": 1781589029,
  "completed": 1781589050,
  "error": {
    "message": "输入图片无法访问,请确认链接是公网可达的",
    "type": "task_failed",
    "param": "",
    "code": "task_failed"
  }
}

视频任务结果在 result.videos,音频在 result.audios

上面的失败示例使用了 "language": "zh"。语言参数只改变 error.message,其他字段与不传时一致。

只有任务到达终态completed / failed)才会推送;处理中不推。

重试与去重(重要)

  • 重试:如果你的服务端没在约 10 秒内返回 2xx,或返回了 5xx,我们会自动重试,最多 3 次,间隔约 10 秒、30 秒、60 秒。3 次都失败就放弃(约 2 分钟内结束)。
  • 不重试的情况:你的接口返回 4xx(视为地址 / 请求有问题),我们直接放弃,不重试。
  • 去重:正常情况下一个任务只推一次。但在极端情况下(如我们这边发送后、确认前发生重启)你可能收到重复推送。请务必id(task_id)做幂等去重,避免重复处理。

你的接收接口建议:

尽快返回 2xx

先收下、入队,再异步处理,别让我们等你处理完。

按 id 去重

以 `id`(task\_id)作为幂等键,避免重复处理。

配置并校验签名

在生产环境对回调请求做来源校验,拒绝伪造请求。

对回调地址的要求

为安全起见,回调地址必须满足:

要求说明
公网可访问不能是内网 / 本地地址(如 127.0.0.110.x192.168.x 等会被拒绝)
协议httphttps(推荐 https
端口用标准端口(80 / 443),非标端口可能被拦截
域名不能指向我们自己的服务域名

不满足要求的地址会被直接丢弃(不会推送、也不会重试)。

常见问题

提交了带 webhook 的任务,但没收到推送?
逐项排查:

1. 任务**是否真的完成了**?查一下任务详情,`status` 是不是 `completed` / `failed`(处理中不推)。
2. 你的地址是不是**公网可访问**?我们能不能连上你的 `/callback`?
3. 端口是不是标准端口(80 / 443)?非标端口可能被安全策略拦掉。
4. 你的 `/callback` 是不是**及时返回了 2xx**?返回 4xx 会被直接放弃。
5. 用 `https` 了吗?证书是否有效?
收到的 result 里 url 怎么是个数组?
部分模型一次会出多张图,`images[].url` 可能是数组,按数组处理即可。
结果链接有有效期吗?
`result` 里若带 `expires_at`(Unix 时间戳),表示该链接的过期时间,请及时转存。
会推送「处理中」的状态吗?
不会。只在任务**最终成功或失败**时推送一次。

最小接收端示例

from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(n)
        data = json.loads(body)
        print("收到任务回调:", data["id"], data["status"])
        # TODO: 这里按 id 去重、校验签名、再处理
        self.send_response(200); self.end_headers()
        self.wfile.write(b'{"ok":true}')

HTTPServer(("0.0.0.0", 443), H).serve_forever()

收到后请尽快返回 200,处理逻辑放后台异步做。