异步任务、轮询与回调
媒体生成有三种使用方式:
| 方式 | 使用方法 |
|---|---|
| 同步 | 原有 /v1/images/generations、/v1/videos/generations、/v1/audio/generations 等待生成结束并返回结果,参数与响应不变 |
| 异步轮询 | POST /api/v1/jobs/createTask 创建任务,然后用 GET /api/v1/jobs/recordInfo?taskId=... 查询本站保存的进度与结果 |
| 异步回调 | 创建时另传 callBackUrl 和 callbackSecret,任务成功或失败后,本站主动 POST 结果;仍可随时查询 |
首期支持已接入统一任务协议、且具有参数参考的图像、视频、音频模型;不支持对话、Suno 等独立协议。暂不支持的模型返回 HTTP 400,不扣积分,可继续使用原同步接口。模型参数见各模型页面;异步请求将这些参数放进 input,同步请求仍放顶层或 params。
所有创建和查询请求都需要 Authorization: Bearer mf-...。任务归属于创建它的 API Key,同账号的其它 Key 也不能查询。请求体上限 1 MB。创建、查询合计每个 Key 每分钟最多 120 次。
创建任务
curl https://ai.modelfold.online/api/v1/jobs/createTask \
-H "Authorization: Bearer $MODELFOLD_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-20261028-001' \
-d '{"model":"seedream/5-pro-text-to-image","input":{"prompt":"a paper boat in the rain","aspect_ratio":"1:1","quality":"basic"}}'{"code":200,"msg":"success","data":{"taskId":"ecf94083-6e5c-4b3c-b0d1-554d38b3dc20","state":"queued","reused":false}}| 字段 | 必填 | 说明 |
|---|---|---|
model | 是 | 本站模型 ID |
input | 是 | 模型原生参数对象,缺必填参数或类型错误返回 400;计费相关时长和分辨率默认值会明确补入后提交 |
callBackUrl | 否 | 客户的 HTTPS 公网接收地址,仅 443 端口;不允许账号密码、片段、内网或保留 IP,支持公网 IPv4 / IPv6;DNS 所有地址均须合规 |
callbackSecret | 有回调时必填 | 客户自行生成的 32–256 字符独立签名密钥,请安全保存;本站加密存储,不在响应中回显 |
Idempotency-Key 请求头 | 推荐 | 1–200 字符。同 Key、同幂等键、同请求返回原任务且不重复扣费;同幂等键换参数返回 409。保留期为任务记录存续期 |
input 不接受回调地址和 n、num_images、num_outputs 等自定义出图数量字段;按模型默认数量生成。已知参数冲突在创建前拦截。缺少参数或参数值不合法可能在上游进一步校验后异步失败。
HTTP 200 只代表本站已受理和预扣积分,不代表生成成功。创建超时应使用原幂等键重试,不能换新幂等键重新创建。
查询任务
curl 'https://ai.modelfold.online/api/v1/jobs/recordInfo?taskId=ecf94083-6e5c-4b3c-b0d1-554d38b3dc20' \
-H "Authorization: Bearer $MODELFOLD_API_KEY"{
"code": 200,
"msg": "success",
"data": {
"taskId": "ecf94083-6e5c-4b3c-b0d1-554d38b3dc20",
"model": "seedream/5-pro-text-to-image",
"state": "success",
"resultJson": "{\"resultUrls\":[\"https://ai.modelfold.online/uploads/outputs/result.png\"]}",
"failMsg": null,
"usage": {"credits": 19, "settled": true},
"callback": {"state": "none", "attempts": 0}
}
}积分仅为示例,实际以 usage.credits 为准。resultJson 是 JSON 字符串,解析后读取 resultUrls。文件地址均为完整地址;结构化检测结果提供 JSON 文件下载地址。
state | 含义与处理 |
|---|---|
queued | 已受理,等待提交上游 |
submitting | 正在提交 |
processing | 已获得上游任务 ID,等待生成、下载和结算 |
uncertain | 提交结果不明,等待上游回调确认;不自动重建或退款。长时间未恢复请带 taskId 联系支持,不要重新下单 |
success | 结果已保存且结算完成,读取 resultJson |
fail | 已确认失败并退还预扣积分,读取 failMsg;usage.credits 为 0 |
未结束时 usage.credits 为预扣积分,settled 为 false;结束后为本站实际扣费。建议每 5–10 秒查询一次;收到 success 或 fail 后停止。查询只读本站数据,不重复提交上游或结算。异步任务超过原同步接口的等待时限后仍继续核实,不按超时自动判失败。
接收客户回调
在创建请求中增加:
{
"callBackUrl": "https://your-service.example/webhooks/modelfold",
"callbackSecret": "replace-with-your-own-random-secret-at-least-32-characters"
}回调请求体:
{
"eventId": "c783846f-7f2e-4736-9fe3-8524a51836b0",
"event": "task.completed",
"data": {
"taskId": "ecf94083-6e5c-4b3c-b0d1-554d38b3dc20",
"model": "seedream/5-pro-text-to-image",
"state": "success",
"resultJson": "{\"resultUrls\":[\"https://ai.modelfold.online/uploads/outputs/result.png\"]}",
"failMsg": null,
"usage": {"credits":19,"settled":true}
}
}失败也发送 task.completed,此时 state 为 fail,resultJson 为 null,failMsg 为具体错误信息。回调在结果保存、结算完成后才入队。
请求头包括:
X-ModelFold-Event-Id:事件 ID,与请求体的eventId相同。X-ModelFold-Timestamp:本次发送的 Unix 秒数。X-ModelFold-Signature:Base64(HMAC-SHA256(callbackSecret, timestamp + "." + eventId + "." + 原始请求体))。
必须使用收到的原始请求体字节验签,不要先解析再重新序列化。校验时间戳偏差不超过 5 分钟,并以事件 ID 去重。同一任务的重试保持事件 ID 和正文不变,但时间戳与签名重新生成。不要拿本站 API Key 代替独立回调密钥。
Node.js 验签示例(使用 express.raw 获取原始正文):
import crypto from 'node:crypto'
const timestamp = req.get('X-ModelFold-Timestamp') || ''
const eventId = req.get('X-ModelFold-Event-Id') || ''
const received = Buffer.from(req.get('X-ModelFold-Signature') || '', 'base64')
const expected = crypto.createHmac('sha256', process.env.MODELFOLD_CALLBACK_SECRET)
.update(`${timestamp}.${eventId}.`).update(req.body).digest()
const valid = /^\d+$/.test(timestamp) && Math.abs(Date.now()/1000 - Number(timestamp)) <= 300
&& received.length === expected.length && crypto.timingSafeEqual(received, expected)
if (!valid) return res.sendStatus(403)
const event = JSON.parse(req.body.toString('utf8'))
if (event.eventId !== eventId) return res.sendStatus(403)
// 持久化事件,按 eventId 去重,再返回 2xx;在后台处理业务。10 秒内返回任意 2xx 即确认收到;重定向不跟随。网络失败、超时或非 2xx 时,最多尝试 10 次,间隔从 30 秒指数递增,最高 1 小时。通知独立持久化,重启后继续重试。查询响应中的 callback.state 为 none、pending、delivered 或 exhausted;attempts 为已尝试次数。通知失败或重试耗尽不改变任务结果和计费,客户仍可查询。
错误与兼容
创建或查询失败时使用 HTTP 状态码和 {"code":400,"msg":"input.prompt is required","data":null} 返回;鉴权沿用现有网关的 error 结构,限流返回 429。常见状态码为 400 参数不合法、401 Key 无效、402 积分不足、404 任务不存在或不属于当前 Key、409 幂等冲突、503 上游未配置。
任务记录、预扣和钱包流水在同一事务中创建;终态、退款或补扣、结果及客户通知正文在同一事务中写入。上游回调只负责唤醒,本站再向上游查询权威结果;30 秒兜底轮询与回调处理共用数据库互斥和终态检查,不重复结算。每个任务使用独立随机上游回调令牌;若运营已配置上游 HMAC 校验密钥,还会校验其签名和时间戳。客户无需配置或接收上游回调。
原同步接口与网页试跑调用方式继续保留;不传任何新参数即可照常使用。同步参数示例见 API 网关。
