傳送行為

標準承載資料、5 秒時限、不重試的原因,以及各種傳送狀態的含義。

Webhook 傳送機制刻意採用簡單的設計,而這種設計會影響您的接收端:只嘗試 1 次 HTTP 請求,限時 5 秒,不重試。請先閱讀本頁,再決定處理常式要同步執行多少工作。

標準承載資料

除了 listing.submission 之外,每個事件傳入時都使用相同的最上層結構,事件專屬欄位則巢狀存放在 data 下。

JSON
{  "event": "connection.location_connected",  "timestamp": "2026-08-25T14:32:10Z",  "account_id": 11073,  "location_id": "279381",  "data": {    "platform": "google",    "connected_account_id": "ba03bc4b-9f8c-4d3b-8e93-9628034c63cc"  }}
欄位型別說明
eventstring以點號分隔的事件名稱。請依此決定處理分支。
timestampstringISO-8601 UTC 時間,在建立傳送內容時記錄,而非在底層變更發生時記錄。
account_idinteger事件所屬的帳戶。
location_idstring數字字串:"279381",而非 279381。在 interaction.* 中不會強制轉換型別;請參閱下方的例外情況。
dataobject事件專屬欄位。事件參考文件 中有各事件的欄位說明。
agency_account_idinteger只有在帳戶由上層代理商帳戶管理時才會出現。

data 內的選填欄位為空時會省略,而不會以 null 傳送。撰寫程式碼時請考量這種情況:以相同方式處理缺少的索引鍵與明確設為 null 的索引鍵,並且絕不要假設文件中標示為選填的索引鍵一定會存在。

2 個例外情況

5 秒時限

每次傳送只會發出 1 次 POST,連線逾時與讀取逾時皆為 5 秒。如果您的端點未在時限內回應,就會放棄這次嘗試,並記錄為 timeout。

此時限涵蓋整個處理常式。唯一安全的設計如下:

  1. 驗證簽章。
  2. 將承載資料寫入可持久儲存的位置:佇列、資料表或記錄檔。
  3. 回傳 200。

其他所有工作(補充資料、分送、回呼 REST API,以及任何涉及第三方的作業)都應在從該佇列取出資料後執行。

不會重試

每次傳送只會嘗試 1 次。沒有退避機制、重試佇列或無法處理訊息佇列。如果您的端點停機 2 分鐘,這 2 分鐘內觸發的事件就會遺失。它們之後也不會送達。

有 2 個影響需要明確說明:

  • 不會跟隨重新導向。重新導向功能完全停用,因此端點回傳的 301 或 302 會被記錄為傳送失敗,不會繼續前往第 2 個 URL。請將您的 webhooks URL 設為最終 URL。請注意,許多網頁伺服器預設會加入從裸網域到 www 的重新導向,以及與尾端斜線相關的重新導向。
  • 只有 2xx 才算傳送成功。3xx、4xx 和 5xx 都會被記錄為失敗。

重複事件與順序

  • 您可能會收到同一事件 2 次,也可能在 2 次不同的變更中收到相同的 id。data 中的識別碼(互動 id、貼文項目 id、已連結帳戶 id)識別的是資源,而非變更。每次評論變更時,interaction.review 都會以相同的互動 id 再次觸發,而且所有承載資料都不含版本或更新時間欄位,因此,若處理常式根據 id + event 永久排除後續事件,就會在沒有任何提示的情況下捨棄實際的編輯。請改為偵測完全相同的重複事件:計算原始本文位元組的雜湊值,並在有明確期限的短時間範圍內(以分鐘計,而非永久)忽略雜湊值相同的重複事件。同一 id 若有不同的本文,就代表新的變更,而非重複事件。接著從 REST API 重新讀取資源,並以該次讀取結果為準;使用資源 id 作為寫入的索引鍵,而非略過寫入。
  • 不保證事件順序,而且 timestamp 無法解決這個問題。事件來自多個獨立管道,並依抵達順序傳送,因此 local_post.published 可能會比您預期先收到的 local_post.created 更早抵達。請勿依 timestamp 排定同一資源的 2 次變更順序:它記錄的是建立傳送內容的時間,而非變更發生的時間,且承載資料中完全沒有版本或序號。若順序很重要,請重新讀取資源,並使用 API 傳回的資料。
  • profile.updated 採用去彈跳處理,每個地點每 60 秒只傳送 1 次,並在時間範圍開始時傳送。這會確實捨棄後續事件,而非延遲傳送:第一次編輯會開啟時間範圍並觸發傳送,此範圍內的每次編輯都會被捨棄,永遠不會送達,而 changed_fields 只描述開啟此時間範圍的那次編輯。因此,您會在時間範圍的開始時收到事件,所以收到事件時立即觸發的重新讀取,只能看到第一次編輯。請安排在時間範圍結束後再次讀取,也就是事件發生至少 60 秒後,並且也要定時核對地點資料,因為通知本身也可能遺失:只嘗試傳送 1 次,不會重試。其他事件皆不採用去彈跳處理。

漏接事件後的復原

系統不會重試,也不會重播事件,因此每個事件類別都需要有可作為備援的權威資料讀取方式。請定期核對資料,不要只在發現異常時才核對。

類別重新讀取方式建議間隔讀取無法還原的資訊
listing.submissionGET /locations/{locationId}/listings/premium,分別使用 listingType=premium 和 additional 各讀取 1 次:取得事件所通知的各網站 syncStatus 和 listingUrl。每天針對每個地點執行,並在大量編輯地點後執行。舊版承載資料沒有 timestamp,也沒有 account_id,因此漏接事件後,無法判定其發生時間或所屬帳戶。所有讀取方式都不會提供 incomplete 提交作業中的 error_message。
profile.*GET /locations、GET /locations/search 或 GET /locations-by-ids。各地點的 updatedAt 可讓您判斷哪些地點有所變更。每小時執行,並加上前述時間範圍結束後的重新讀取。source(user/api/bulk_edit)和 changed_fields:讀取只會顯示目前狀態,無法得知誰變更了哪些內容。因 60 秒去彈跳處理而被捨棄的編輯完全不會留下紀錄,而已刪除地點在刪除前的狀態也已不存在。
connection.*使用 GET /connected-accounts 和 GET /connected-accounts/{connectedAccountId}/details 查詢連線、斷線與驗證狀況。針對 Google 驗證的 2 種事件,請從任一地點讀取結果中取得 googleVerificationStatus { status, message }。每小時執行;驗證進行期間,每隔幾分鐘執行。failure_type(rejected 與 transient 的區別)以及 reason 字串。無法看出連線反覆中斷的情況:曾中斷又恢復的連線,讀取結果會顯示為已連線。
interaction.*使用 GET /locations/{locationId}/reviews 依地點讀取、GET /rollup_interactions 跨地點讀取、GET /reviewDetails 依 id 讀取。每 15 分鐘檢查新評論,每天進行一次更大範圍的檢查。曾發生變更這件事。文件所列的互動欄位不含更新時間,因此編輯過的評論看起來與您已儲存的評論完全相同:請比較您保留的欄位。
local_post.*使用 GET /locations/{locationId}/posts、GET /locations/{locationId}/bulk-posts、GET /posts/{postId} 取得各網站目前的發布狀態。貼文發布處理期間,每小時執行。傳送時的 rejection_reason,以及貼文項目經歷各狀態的順序。讀取會提供目前狀態,無法得知狀態轉換過程。
review_analytics.*使用 GET /locations/{locationId}/review-analytics-overview,並指定該期間的 startDate 和 endDate。每天在彙總時間範圍結束後執行;每週事件類別則每週執行。截至當時的數值。總覽會在讀取時重新計算,因此延遲抵達或遭刪除的評論,會使總覽與快照中的內容不同。

傳送結果

每次傳送嘗試都會記錄狀態,包括尚未發出請求的嘗試。以下是實際使用的狀態字串。

4 種結果及其對應的狀態字串。「已阻擋」和「受速率限制」表示完全未發出 HTTP 請求,因此需要與實際的傳送失敗分開記錄。
狀態結果含義處理方式
success已送達您的端點回傳了 2xx。無須處理。
以字串表示的 HTTP 狀態碼,e.g。"404"、"500"失敗您的端點回傳了該非 2xx 狀態碼。3xx 也屬於此類,因為系統不會跟隨重新導向。檢查路徑、重新導向鏈,以及處理函式的錯誤率。
999失敗已收到回應,但沒有可用的狀態碼。通常與端點前方的 Proxy 或負載平衡器有關。
timeout失敗在 5 秒的時限內未收到回應。將工作移出請求處理流程,並提早回應以確認收到請求。
network_error失敗無法解析主機名稱或連線至主機。檢查 DNS,並確認可透過公用 DNS 解析主機名稱。
error失敗傳送嘗試發生了上述類型以外的錯誤,例如 TLS 交握失敗。檢查您的憑證鏈和伺服器記錄。
blocked_url已阻擋URL 在嘗試連線時未通過安全檢查:未使用 HTTPS、缺少主機,或解析為私有位址、迴路位址、連結本機位址或中繼資料位址。將 URL 指向公開的 HTTPS 主機。請注意,每次傳送都會重新執行此檢查,因此 DNS 變更可能導致原本可用的 URL 開始遭到阻擋。
endpoint_unverified已阻擋帳戶已設定簽章密鑰,但端點尚未通過驗證,或驗證狀態已重設。在儀表板中重新執行驗證端點。
rate_limited受速率限制已超過每分鐘或每日的上限。事件已被捨棄。減少傳送量,或改用上限較高的方案。請參閱方案與限制。

接收端檢查清單

  • 驗證每個請求的簽章;若不符,則回傳 401,且不進行任何處理。
  • 對原始主體位元組計算雜湊值,絕不可使用重新序列化的物件。
  • 在遠短於 5 秒的時間內回傳 2xx;將工作排入佇列。
  • 以 data 內的 ID 作為處理函式每次寫入的索引鍵,讓重複處理產生相同的結果,但絕不可因為 id 重複就認為無須處理。
  • 不要根據抵達順序推斷先後順序,也不要依 timestamp 排序。
  • 接收時,將 location_id 統一轉換為字串。
  • 忽略無法識別的 event 值,並仍然回傳 200。
  • 讓 webhooks URL 直接提供服務,作為最終 URL,不要使用重新導向。
  • 定期與 REST API 核對資料;系統不會重試,無法藉此補回遺漏的資料。