Webhook(JSON)
將每筆 Google 表單回應以已簽署的 JSON 傳送到您自己的端點、Zapier、Make 或 n8n,並驗證簽章
Webhook 規則會將每筆符合的回應以 JSON 透過 POST 傳送到您提供的網址。可用來餵入 CRM、試算表工具、您自己的伺服器,或 Zapier、Make、n8n 等自動化平台。
建立 Webhook 規則
- 點擊 + 新增規則 並選擇 Webhook
- 時機:保留 每筆回應,或選擇 僅在核准後 / 遭到拒絕時 並設定審核者。加入 僅當 條件,只傳送符合的回應
- 傳送至:貼上您的端點網址(僅限 HTTPS)。沒有訊息步驟:規則會傳送固定格式的 JSON。點選網址下方的顯示傳送內容,即可查看含有表單問題的內容並複製
- 點擊 建立規則,然後複製只顯示一次的簽署密鑰(見下文)

傳送測試 會將已簽署的範例內容傳送到端點,並顯示它回傳的 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 規則不會阻擋表單上的其他規則
