Amux

Create task

最近更新:2026年9月1日

站内统一的异步生成入口。一条端点服务全部模态,提交拿 ID、按 ID 取结果。

站内统一的异步生成入口。一条端点承载全部模态;当前可用于图像,后续接入视频与音频时仍沿用同一路径与响应形状。

下表列出全部请求参数与响应字段。

POSThttps://gateway.amux.ai/v1/tasks

鉴权

header
Authorizationstring必填

Bearer <你的 Amux 密钥> 控制台创建的 Amux 密钥。忘了可以回控制台的密钥页再看一次。

Preferstring

wait=N 表示在当前连接上最多等待 N 秒。若任务完成则直接返回 200 和完整结果;未完成则返回 202。

N 最大 90;超过按 90 处理。适合想少一次轮询、但又不想改成 SSE 的场景。

Idempotency-Keystring

相同键的重试不会再次创建任务,也不会再次预留费用;返回的是原有任务。

幂等键按工作区隔离。客户端重试同一次提交时,复用同一个键即可。

请求

application/json
modelstring必填

站内模型 ID,可带 :供应商 后缀锁定供应商。

模态由模型决定,不用另外声明——填一个图像模型就是出图任务。

promptstring必填

提示词。

webhook_urlstring

任务到终态时往这里 POST 一份结果,形状见上面「回调」那一节。

只收 https,且不能指向内网——我们是从服务端发过去的。

地址在提交这一刻就校验,写错了当场报错,

而不是等任务跑完才发现回调从来没来。

响应

200response

已经是终态(Prefer: wait 等到了),或者这是一次重放(同一个幂等键)。

和 202 分开是有意的:202 说「我接下了一件事」,而这两种情况都没有接下新的事。

202response

任务已受理,并且预扣已经扣住。拿 id 去取结果。

返 202 而不是 200,是因为这次调用没有产出——它只是接下了一件事。

400response

请求不合法。也包括 stream: truepartial_images 大于 0——这条端点是异步的, 同步流式在厂商兼容端点上(/v1/images/generations)。

402response

可用余额不足以覆盖预扣上界。可用 = 余额 − 已过期批次 − 已冻结的预扣

503response

在途任务已满,稍后重试(响应带 Retry-After)。

什么时候用它

一次高质量出图实测 80–115 秒。走同步端点的话,这条路上任何一环超时(反向代理、网关、客户端的默认超时)都会让你拿不到图,而钱已经花了——上游已经出图并计了费。

异步接口把请求提交与结果获取拆开:提交立即返回,生成在后台进行,结果可按需轮询或回调接收。

和同步端点的关系

计费完全一致:提交时按上界预扣,完成后按实际结算、释放差额。

区别只有一处:预扣发生在提交这一刻。所以余额不足会在提交时就 402,而不是等跑完才发现扣不出来。

模态由模型决定

model 决定任务类型。图像模型对应图像任务,后续的视频或音频模型也会沿用同一规则。请求体中无需额外声明 type 一类的字段。

请求体支持两种形态

纯生成直接发 application/json。带参考图的编辑改用 multipart/form-data:参考图放 imageimage[],蒙版放 mask,其余参数继续沿用同名字段。

webhook_url 接收结果

任务进入终态后,会向该地址 POST 一份结果:

{
  "event": "task.succeeded",
  "sent_at": "2026-09-01T04:31:00.000Z",
  "task": { "id": "task_…", "status": "succeeded", "output": { "images": [...] }, "usage": {...}, "cost": "0.005925" }
}

task 与取回接口的响应体保持一致,可直接复用同一套解析逻辑。

验证签名

请求带 Amux-Signature: t=<秒级时间戳>,v1=<hex>,签的是 `${t}.${原始请求体}`(HMAC-SHA256)。密钥在控制台的工作区设置里取。

如果不校验签名,任何知道该地址的人都可以伪造回调内容。

import { createHmac, timingSafeEqual } from 'node:crypto'

function verify(rawBody, header, secretHex) {
  const m = /t=(\d+),v1=([0-9a-f]+)/.exec(header)
  if (!m) return false

  // ⚠️ 先校验时间戳,避免旧回调被重复使用
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(m[1]))
  if (age > 300) return false

  const expected = createHmac('sha256', Buffer.from(secretHex, 'hex'))
    .update(`${m[1]}.${rawBody}`)
    .digest('hex')

  // ⚠️ 使用定长比较,避免基于耗时的签名推断
  return timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]))
}

⚠️ 校验对象必须是原始请求体字节,不能先 JSON.parsestringify;重新序列化会改变键顺序或空白,从而导致签名不匹配。

重试与去重

非 2xx(3xx 也算)与超时都算失败。首次之后按 10 秒 / 1 分 / 5 分 / 30 分 / 1 小时退避,最多再试 5 次(合计 6 次,跨度约 2 小时)。

  • 尽快返回 2xx,后续处理建议放入你自己的异步队列;
  • task.id 去重,因为同一事件在重试场景下可能重复送达。

回调地址限制

仅支持 https,且不能指向私有网络地址。 地址会在提交时校验,不符合要求会直接报错。

不跟随重定向。 回调地址需要直接返回 2xx。

这条端点不流式

stream: truepartial_images 大于 0 在这里会返回 400。(partial_images: 0 是默认值,照常放行。)

流式意味着这个请求自己把结果跑完并顺着连接送出去——那样任务不会进队列, 连接断了结果就没有了,而「连接断了也不丢」正是这条端点存在的理由。

想在当前请求里拿到结果,用下面的 Prefer: wait:它返回的是和取回接口 完全一样的那份 JSON,你只需要维护一套解析逻辑。想要生成过程中的低清预览, 用厂商兼容端点 /v1/images/generations 并设置 stream: true

短暂等待结果:Prefer: wait

发送 Prefer: wait=60 后,服务端会在当前连接上最多等待 60 秒。若任务在此期间完成,则直接返回 200 和完整结果,响应形状与取回接口一致。

如果超过等待时间仍未完成,则返回 202,后续继续按任务 ID 查询即可。

N 最大 90;超过按 90 处理。适合想少一次轮询、但又不想改成 SSE 的场景。

重试保护:Idempotency-Key

为创建请求指定 Idempotency-Key 后,相同键的重试不会再次创建任务,也不会再次预留费用;接口会返回原有任务。

适合处理“请求已发出但客户端未收到响应”的场景。

幂等键按工作区隔离。客户端重试同一次提交时,复用同一个键即可。

cURL
curl https://gateway.amux.ai/v1/tasks \
  -H "Authorization: Bearer $AMUX_API_KEY" \
  -H "Prefer: <value>" \
  -H "Idempotency-Key: <value>" \
  -d '{
    "model": "openai/gpt-image-2",
    "prompt": "A ginger cat running through fresh snow"
  }'
{
  "id": "task_01M1CG35C16CJ790D00BV1RBVM",
  "status": "queued",
  "model": "openai/gpt-image-2",
  "created_at": "<string>",
  "output": {
    "images": [
      {
        "index": 0,
        "url": "<string>"
      }
    ],
    "videos": [
      {
        "index": 0,
        "url": "<string>"
      }
    ]
  },
  "usage": {
    "input_tokens": 0,
    "output_tokens": 0,
    "total_tokens": 0,
    "output_video_seconds": 0
  },
  "cost": "0.2108",
  "notes": [
    "dropped audio: this model decides the soundtrack itself"
  ],
  "error": {
    "message": "<string>",
    "code": "<string>"
  }
}