Google 給 Gemini API 上線了「事件驅動 webhook」,把過去那種「長時任務靠輪詢」的做法直接砍掉。涵蓋三組事件:Batch(succeeded / cancelled / expired / failed)、Interactions(requires_action / completed / failed / cancelled)、Video Generation(video.generated)。實作走的是 Standard Webhooks 規範:靜態(專案級)webhook 用 HMAC-SHA256 簽章,動態(單請求)webhook 用非對稱 JWT(RS256)簽章、公鑰經 JWKS 發布;每個 payload 都帶 `webhook-signature`、`webhook-id`、`webhook-timestamp` 三個 header。投遞語意是 at-least-once,重試窗 24 小時,採用指數回退。官方建議拒絕時間戳早於 5 分鐘的請求,以防重放攻擊。

兩種設定模式的差異很關鍵。*靜態*:透過 WebhookService API 在專案級設定,該專案下所有匹配事件都會觸發,用一個共享密鑰做 HMAC 驗證。*動態*:每次請求裡用 `webhook_config` 欄位做覆蓋,簽章用 Google 自己的密鑰做非對稱 JWT,公鑰從 `https://generativelanguage.googleapis.com/.well-known/jwks.json` 拿。動態模式還支援一個 `user_metadata` 欄位做路由 —— 這份 metadata 會隨 webhook payload 原樣回到你的伺服器,意味著單一 endpoint 可以按租戶、按使用者、按工作流自由 fan-out,不需要在自己這一頭另起一張「job-ID → 上下文」狀態表。Payload 故意做薄:狀態快照加上指標,比如 Batch 給你 `output_file_uri`,Video 給你 `file_id` 和 `video_uri`,不直接塞原始輸出。伺服器收到後應當先回 `2xx`,再非同步處理,免得觸發重試迴圈。`webhook-id` 這一個 header 就是你的去重 primitive。

放到生態層面,這一步把 Gemini API 在「事件投遞」這一項推到了「持平 + 一點點超」的位置。OpenAI 的 Batch API、Anthropic 的 Message Batches 當前都是 async + 你自己輪詢 —— 一次 24 小時的批跑,意味著你這邊按時間表反覆打 API 等結果,白白吃掉請求預算。Webhook 把這個反過來:你的應用睡覺,等 Google 推過來。影片生成尤其受益(Veo 這一類工作負載,單請求可能跑幾十秒到數分鐘),用輪詢去守它,浪費得更明顯。靜態/動態這個切分也挺有想法 —— 靜態適合「我們就一個團隊在跑批,給我一個密鑰統一驗」,動態適合「我是 SaaS、我跑的是多租戶 Gemini 工作負載,每個租戶的 webhook 要落到他們各自隔離的 endpoint 上、還要帶著他們自己的 metadata 一起回來」。這是一個值得抄進自己產品裡的能力形狀。

可落地的讀法。如果你在跑 Gemini Batch 或影片任務、還在輪詢,直接切過來 —— 省 API 呼叫、縮短「任務完成-我察覺到」之間的尾部延遲、整體也更乾淨。5 分鐘反重放窗 + `webhook-id` 去重的組合意味著你的 webhook handler 必須自帶一層冪等(大多數本來就有)。多租戶場景裡,「動態 webhook + `user_metadata`」就是正解 —— 別想著用靜態 webhook 然後在自己一頭從 job ID 反推租戶。再回頭比一下你現在在 OpenAI / Anthropic 那一側的輪詢開銷,看看是不是值得去推這兩家也補齊 Standard Webhooks 這一檔。這件事的訊號不是「webhook 是新東西」—— webhook 是老東西;訊號是 AI API 終於開始按 Standard Webhooks 這個工程標準被對待了,這意味著「agent + 批處理」的基礎設施,從此可以跟你棧裡其他東西用同一套維運原語來搭。