GET /v1/task/{task_id} 直到它結束,也可以給 WideRouter 一個 https 地址,
讓它主動找你。後者就是回撥,它是任務 API 本身的能力,不屬於某一個模型:
Nano Banana 出圖和 Grok Imagine 出影片,用的是同一個欄位、同一種 payload、同一套規則。
輪詢每次都要帶你的 API Key,回撥一次都不用。 接收端是你自己託管的一個 URL,
由 WideRouter 來呼叫,所以沒有任何憑證需要載入、轉發,也就不會配錯。
如果你的輪詢程式曾經收到過
401,或者同時有幾千個任務在跑,這一頁就是寫給你的。提交時多一個欄位
在model 和 input 旁邊加上 callback_url。請求的其他部分完全不變,
提交響應也還是那個任務 ID。
400 invalid_callback_url,
金鑰錯是 400 invalid_params 且 param 指向 callback_secret——而不是一個永遠不會回報的任務:
WideRouter 會發給你什麼
每個任務到達終態時POST 一次,Content-Type: application/json。
三個頭可以讓你在解析正文之前先分流和驗證來源:
正文就是任務物件,與同一時刻
GET /v1/task/{task_id} 的返回逐位元組相同。
一個解析器同時服務回撥和輪詢兩條路。
- task.completed
- task.failed
task.failed 同時也是「這筆費用已衝回」的訊號。
產物檔名以任務 ID 開頭,落盤歸檔不需要改名。
投遞規則
兩個值得據此設計的後果:
- 做到冪等。 你慢慢地返回了
200,我們這邊可能已經超時重試,同一個任務會到兩次。 handler 按id去重。 - 給漏網之魚留一條輪詢兜底。 提交時存下任務 ID。如果超過該模型通常的時延幾分鐘 還沒收到回撥,就讀一次任務。回撥是省延遲的最佳化,不是投遞保證。
驗籤
提交時帶上callback_secret,這個任務的每次回撥都會帶 X-Wide-Signature。
它證明兩件事:請求確實來自 WideRouter,正文在路上沒有被改過。它不是加密——
正文裡本來只有公開的 URL。
t 是傳送回撥時的 Unix 秒。v1 是
hex(HMAC-SHA256(callback_secret, "<t>.<原始正文>")):時間戳、一個點、
然後是收到的請求正文原樣。
1
拿原始正文
摘要算的是位元組。把 JSON 解析再序列化一遍位元組就變了,所以要在框架碰它之前把正文讀出來。
2
重算並恆定時間比較
用同一個金鑰重算 HMAC,用恆定時間比較函式比對,不要用
==。3
拒絕過期的時間戳
now - t 超過 300 秒的一律丟掉,正負都算。這是防止被截獲的回撥事後重放的手段。
實測偏差約 3 秒。callback_secret 就沒有簽名頭,callback_url 裡那段猜不到的路徑
就是你和一條偽造的「已完成」之間的全部屏障。無論哪種,下載或計費之前都先用你的
API Key 重新讀一次 GET /v1/task/{task_id}:簽名證明的是誰發的回撥,
回查證明的是任務現在是什麼狀態。
三種語言的接收端
每個 handler 做的是同樣的五件事:對原始正文驗籤,檢查事件頭,立刻用200 應答,
把任務 ID 交給佇列,再由 worker 重新讀任務並下載產物。verify 就是上一節那個函式。下載刻意放在請求 handler 之外,
因為產物可能有好幾 MB,而你只有 10 秒的應答時間。
沒有伺服器也能先試
任何能給你一個公網https 地址的請求檢視服務都可以當一次性接收端:
提交任務時把那個地址填進 callback_url,然後看任務物件落進去。
準備在自己機器上接收時,把本地埠打一條 https 隧道出去即可。
記住地址必須公網可達,區域網地址在提交時就會被拒。
儘快下載
產物 URL 在任務完成 24 小時後失效,而回調是你最早知道它們存在的時刻。 到達即下載的接收端永遠不用操心過期;只存 URL、以後再讀的接收端,終有一天會存下一堆指向空的連結。下一步
非同步任務 API
信封、輪詢迴圈、任務狀態與錯誤表。
快速開始
五分鐘內走通提交、輪詢、下載。