Webhook(JSON)

將每筆 Google 表單回應以已簽署的 JSON 傳送到您自己的端點、Zapier、Make 或 n8n,並驗證簽章

Webhook 規則會將每筆符合的回應以 JSON 透過 POST 傳送到您提供的網址。可用來餵入 CRM、試算表工具、您自己的伺服器,或 Zapier、Make、n8n 等自動化平台。

建立 Webhook 規則

  1. 點擊 + 新增規則 並選擇 Webhook
  2. 時機:保留 每筆回應,或選擇 僅在核准後 / 遭到拒絕時 並設定審核者。加入 僅當 條件,只傳送符合的回應
  3. 傳送至:貼上您的端點網址(僅限 HTTPS)。沒有訊息步驟:規則會傳送固定格式的 JSON。點選網址下方的顯示傳送內容,即可查看含有表單問題的內容並複製
  4. 點擊 建立規則,然後複製只顯示一次的簽署密鑰(見下文)

傳送測試 會將已簽署的範例內容傳送到端點,並顯示它回傳的 HTTP 狀態碼。已儲存的網址會加密儲存並以遮罩顯示;點擊 取代 可貼上新的網址。

傳送內容

每個請求都是 POST、Content-Type: application/json,內容如下:

{
  "version": 1,
  "event": "submitted",
  "form": { "id": "1AbC...", "name": "Workshop sign-up" },
  "response": {
    "id": "a3f1...",
    "submittedAt": "2026-01-31T09:30:00.000Z",
    "respondentEmail": "respondent@example.com",
    "answers": [
      { "questionId": "q1", "title": "Your name", "value": "Ada Lovelace" },
      { "questionId": "q3", "title": "Department", "value": "Ops" }
    ]
  },
  "rule": { "id": "rule id", "name": "Zapier: new lead" }
}
  • event 為 submitted、approved、rejected 之一;也會放在 X-Skyfield-Event 標頭中
  • 除非表單收集電子郵件地址,否則 response.respondentEmail 為 null
  • answers[].value 一律是字串;核取方塊的答案以 , 連接
  • rule.id 與 rule.name 識別觸發的規則;規則沒有名稱時,name 為 null
  • 在 approved 與 rejected 時,會額外包含 approval: { "decision": "approved", "decidedBy": "Dana" }。decidedBy 是審核者在確認頁面輸入的姓名;若未輸入則為審核者的電子郵件,否則為 null

只要上述結構維持穩定,version 就保持為 1。可能會新增欄位;未提升版本前不會移除或重新命名任何欄位。

驗證簽章

每個請求都帶有 X-Skyfield-Signature 標頭:sha256=<hex>,即以規則的簽署密鑰對原始請求內容計算的 HMAC-SHA256。請在信任請求之前先驗證它。

密鑰(whsec_...)只會顯示一次,就在讓規則取得第一個 Webhook 網址的那次儲存之後。請當下複製;之後無法再顯示。之後取代網址時,密鑰維持不變。若遺失,請開啟規則,點擊 新的簽署密鑰,然後儲存:新密鑰會顯示一次,舊密鑰立即失效。

請對收到的原始內容位元組計算 HMAC。若先解析再重新序列化 JSON,會改變空白字元並導致比對失敗。

Node.js

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

// rawBody: string or Buffer of the request body, before any JSON parsing
function verify(rawBody, header, secret) {
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')
  const a = Buffer.from(expected)
  const b = Buffer.from(header ?? '')
  return a.length === b.length && timingSafeEqual(a, b)
}

// Express example: use express.raw() so req.body is the untouched bytes
app.post('/hook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verify(req.body, req.get('X-Skyfield-Signature'), process.env.SKYFIELD_SECRET))
    return res.status(401).end()
  const payload = JSON.parse(req.body)
  res.status(200).end()
})

Python

import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest("sha256=" + digest, header or "")

# Flask example
@app.post("/hook")
def hook():
    if not verify(request.get_data(), request.headers.get("X-Skyfield-Signature"), SECRET):
        return "", 401
    payload = request.get_json()
    return "", 200

請回應任何 2xx 狀態碼。回應內容會被忽略。

Zapier、Make、n8n

三者都接受一般的 JSON Webhook:

  • Zapier:觸發器選 Webhooks by Zapier,事件選 Catch Hook(或 Catch Raw Hook 以自行驗證簽章)。將 Zapier 顯示的網址貼到規則中
  • Make:新增 Webhooks 模組、Custom webhook,複製其網址
  • n8n:新增 Webhook 節點,方法為 POST,並使用其 production 網址

從規則傳送一次測試,然後在平台中對應 response.answers 的欄位。範例內容使用佔位答案,因此每個欄位都會出現。

傳送與限制

  • 端點必須可透過 HTTPS 從公開網際網路連線。儲存時會拒絕私人與本機位址
  • 不會跟隨重新導向:請使用最終網址
  • 端點有 5 秒的回應時間。逾時或 5xx 回應會重試一次
  • 404 或 410 回應代表端點已不存在:規則會被暫停,您會收到一封電子郵件。在規則上貼上新網址即可恢復
  • 紀錄頁面會列出每次傳送以及您的端點回傳的 HTTP 狀態碼
  • 已暫停或已停用的 Webhook 規則不會阻擋表單上的其他規則

注意事項

  • Webhook 規則同樣遵循 寄送時機,因此端點可以接收審核決定而非每筆提交。參閱寄送時機
  • 若只想傳送經過審查的回應,請將規則的 寄送時機 設為 僅在核准後 並選擇審核者。參閱審核。傳送內容就會帶有 approval.decision
  • 變更寄送管道… 可將 Webhook 規則移到電子郵件或聊天頻道,並在那裡設定新的目的地。切換回 Webhook 時,必須重新貼上網址
  • Webhook 規則需要有效授權。免費試用也算。參閱哪些功能需要授權