Instagram · Facebook · Threads

一個 Meta 真的會收的 webhook 網址。

Meta 存檔前會先檢查 callback URL,之後若沒人接收,36 小時就不再試。AnyHook 替你回那個檢查、把每個事件存下來,並且在 Meta 放棄之後繼續重試你的 handler。

時序問題

Meta 會對你的 callback URL 打一個 GET,帶著 hub.mode=subscribe 和一個 challenge,你必須原封不動把 challenge 當成整個回應內容送回去,它才願意存檔。用 tunnel 可以蔑混過一次,但 ngrok 下次給你新網址時,你又得回 App Dashboard 重做一次,而且已經建好的訂閱正指著一個不存在的網址。

事件開始進來之後,期限不是每次請求算,是 36 小時。Meta 在這個窗口內以遞減的頻率重試,之後把沒人接收的丟掉。對自動回覆機器人來說,這個窗口就是「回了留言」跟「根本不知道有這則留言」的差別。

36h
Meta 丟掉未接收通知的期限
1 個網址
同時涵蓋 IG、FB、Threads
GET
AnyHook 替你回的握手
0
tunnel 重啟後要重驗證的次數

把 callback 指到 AnyHook,只驗一個標頭

inbound 網址永遠不變,所以 App Dashboard 只要設一次。AnyHook 在 edge 驗 Meta 的 X-Hub-Signature-256、把事件存下來,再重新簽過 forward 給你的 handler。

// 1. AnyHook 這邊:把 app 的 source 設成 Meta,貼上 App Secret。
//    Verify Token 選填。那串字是你自己編的,不是從 Meta 複製來的。
//
// 2. Meta App Dashboard -> Webhooks:
//    Callback URL: https://in.anyhook.net/{user-slug}/{app-slug}
//    Verify Token: 你剛剛選的同一串
//
//    AnyHook 會回 GET 握手,所以網址第一次就存得進去。

import { verifyWebhook } from "anyhook-verify";

export async function POST(req: Request) {
  const ok = await verifyWebhook(req, process.env.ANYHOOK_SIGNING_SECRET!);
  if (!ok) return new Response("invalid signature", { status: 401 });

  const event = await req.json();
  const change = event.payload.entry?.[0]?.changes?.[0];

  // Meta 已經拿到回應了。這裡要跑多久都行。
  if (change?.field === "comments") {
    await replyToComment(change.value);   // LLM 呼叫,幾秒
  }

  return new Response("ok", { status: 200 });
}

工程團隊常問的問題

Meta 說我的 callback URL 驗證失敗,為什麼?
那個驗證是一個帶著 hub.mode=subscribe 跟 hub.challenge 的 GET,你的網址必須把 challenge 原封不動當成整個 body 送回去,不能包成 JSON、也不能多一個換行。AnyHook 會替你回,所以 inbound 網址第一次就存得進去。如果你在 AnyHook 設了 Verify Token,App Dashboard 那邊要填同一串。
IG、FB、Threads 要分開設三次嗎?
不用。三個走的是同一套 Graph API webhook 契約、同一個 X-Hub-Signature-256 簽章,所以一個 AnyHook app 就涵蓋完。要訂閱哪些事件在 App Dashboard 選。
機器人重新部署的時候,留言和私訊會怎樣?
AnyHook 在事件進來的當下就存起來,再以遞增間隔重試你的端點,所以部署或崩潰不會讓你損失那則訊息。Meta 只看到一次接收成功,就不再管了。
Verify Token 跟 App Secret 是同一個東西嗎?
不是,而且不該填成同一串。App Secret 負責簽每一個 payload,永遠不會出現在網址裡。Verify Token 是你自己編的,它從設定握手的查詢字串進來,而且只用那一次。把 App Secret 放進查詢字串,等於洒進沿途每一份日誌。

改一個 URL,保留你的 Instagram handler。

Free tier 每月含 3,000 events。沒有 SDK、沒有 code 變更,把 Instagram 指到你的 AnyHook inbound URL 就好。