Webhook — 把品牌事件即時推送到你的後端
🪄 Max 方案專屬功能 — Free / Pro 方案下,dashboard 會顯示鎖定畫面、API 呼叫會被拒(403)。 已建立的 endpoint 在降級後也會停止 fire,升回 Max 自動恢復。
當有人在你的品牌頁報名、改付款狀態、新增連結⋯⋯HypeLink 會即時 POST 一份 JSON payload 到你預先設定的 URL。不用 polling、不用 cron,你的客服系統 / CRM / Slack / 自家後端就能在事件當下接到通知。
「想設定一個 endpoint,報名一進來就推到我的 CRM」→ 這頁就是你要的。
| 訂閱方案 | Max 方案專屬 |
| 設定位置 | https://hypelink.app/dashboard/brands/<hypeId>/settings/webhooks |
| 可設多少組 | 不限 — 一個品牌可以設多個 endpoint,各自訂閱不同事件 |
| 簽名 | HMAC-SHA256,標頭 X-HypeLink-Signature: sha256=<hex> |
| 重試 | 1m → 5m → 30m → 4h → 24h(共 5 次),全失敗後 state=dead |
| 自動暫停 | 連續 20 次失敗自動暫停 endpoint(你修好接收端後重新啟用) |
三步驟開始
1. 在 dashboard 新增 endpoint
設定 > Webhooks > 新增 endpoint
- 顯示名稱:給自己看的(例:「CRM 同步」「Discord 通知」)
- Receiver URL:你的伺服器要 POST 進去的網址(必須能接 POST + application/json)
- 訂閱事件:不勾任何項 = 全訂閱;要過濾就勾你關心的
建立後會只此一次顯示 secret(whsec_*)— 立刻複製保存,之後只能 rotate 換新。
2. 在你的後端驗證簽名
# Python(Flask 範例)
import hmac, hashlib
from flask import Flask, request, abort
SECRET = b"whsec_xxxxx" # 從 dashboard 取得
@app.post("/webhook/hypelink")
def hypelink_webhook():
sig = request.headers.get("X-HypeLink-Signature", "")
if not sig.startswith("sha256="):
abort(401)
expected = hmac.new(SECRET, request.data, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig[7:], expected):
abort(401)
payload = request.get_json()
event_type = request.headers["X-HypeLink-Event"]
# ...處理事件...
return "", 200// Node.js / TypeScript
import crypto from "crypto";
const SECRET = "whsec_xxxxx";
app.post("/webhook/hypelink", express.raw({ type: "application/json" }), (req, res) => {
const sig = String(req.headers["x-hypelink-signature"] ?? "");
if (!sig.startsWith("sha256=")) return res.sendStatus(401);
const expected = crypto
.createHmac("sha256", SECRET)
.update(req.body) // 原始 bytes
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(sig.slice(7)), Buffer.from(expected))) {
return res.sendStatus(401);
}
const payload = JSON.parse(req.body.toString());
// ...處理事件...
res.sendStatus(200);
});3. 回傳 2xx 結束流程
只要回 200(或任何 2xx),HypeLink 就視為成功。3xx/4xx/5xx、timeout、network error 都視為失敗,會走 backoff retry。
事件類型
v1 開放:
| 事件類型 | 何時觸發 |
|---|---|
event.created | 主辦建立新活動 |
event.updated | 活動資訊變更 |
event.cancelled | 主辦取消活動 |
event.deleted | 活動被刪除 |
event.registration.created | 公開頁有人成功報名(含候補 / 待審) |
event.attendee.status_changed | 主辦改報名狀態(核准 / 拒絕 / 取消) |
event.attendee.payment_changed | 主辦更新付款狀態(已付 / 退款 / 豁免⋯⋯) |
link.created | 新增連結 |
link.updated | 編輯連結 |
link.deleted | 刪除連結 |
profile.updated | 品牌 profile 變更 |
每次推送都會在 payload 中明確帶 eventType / hypeId / deliveredAt。
Payload 範例
// event.registration.created
{
"eventType": "event.registration.created",
"hypeId": "@goodshot",
"deliveredAt": "2026-06-01T18:32:11.521Z",
"eventUuid": "206e879c-a0ec-4964-a43f-5c5f7903c5e5",
"eventName": "高中同學回娘家聚餐",
"eventSlug": "reunion-2026",
"attendee": {
"uuid": "1adc39bf-2a87-4f59-a43d-4c3f2b77e098",
"name": "陳小華",
"email": "[email protected]",
"phone": "0912345678",
"status": "registered",
"ticketNumber": "T-20260601-001",
"ticketTypeUuid": "a2fc47f9-...",
"ticketTypeName": "校友票",
"paymentStatus": "unpaid",
"paymentLast5": "98765",
"customValues": { "grad_class": "305 班", "hs_nickname": "華華" },
"regSource": "social",
"utmSource": "instagram",
"utmMedium": "stories",
"utmCampaign": null
}
}// event.attendee.payment_changed
{
"eventType": "event.attendee.payment_changed",
"hypeId": "@goodshot",
"deliveredAt": "2026-06-01T19:01:00.012Z",
"eventUuid": "206e879c-...",
"attendeeUuid": "1adc39bf-...",
"paymentStatus": "paid",
"paymentNote": "現場現金",
"name": "陳小華",
"email": "[email protected]"
}// link.created
{
"eventType": "link.created",
"hypeId": "@goodshot",
"deliveredAt": "2026-06-01T17:45:22.143Z",
"linkId": 110,
"uuid": "5de6b58f-...",
"name": "我的 IG",
"url": "https://instagram.com/myhandle",
"folderId": 180
}標頭規格
每次 POST 都會帶:
| Header | 範例 | 用途 |
|---|---|---|
X-HypeLink-Event | event.registration.created | 事件類型 |
X-HypeLink-Delivery | 1adc39bf-... | 此 delivery 的 uuid(log 對照用) |
X-HypeLink-Timestamp | 1717258200 | Unix seconds — 建議拒絕 > 5 分鐘前的請求防 replay |
X-HypeLink-Signature | sha256=ab12cd… | HMAC-SHA256(secret, raw_body) |
User-Agent | HypeLink-Webhook/1.0 | — |
重試 / 自動停用
| 情境 | 行為 |
|---|---|
| 收到 2xx | 標記 succeeded,連續失敗計數歸零 |
| 收到非 2xx / network error / timeout(10s) | 標記 failed_retry,按 backoff 排程下次 |
| 已重試 5 次仍失敗 | 標記 dead,停止重試(仍可在 dashboard 手動 redeliver) |
| 連續失敗 20 次 | 自動暫停 endpoint(其他事件不再 fire) |
Backoff 排程:1 分 → 5 分 → 30 分 → 4 小時 → 24 小時(總嘗試 5 次)。
Dashboard 功能
設定 > Webhooks:
| 動作 | 說明 |
|---|---|
| 新增 endpoint | 設定 URL + 訂閱事件 → 拿到 secret |
| 開關 enabled | switch 暫停 / 啟用,重啟動時連續失敗計數歸零 |
| 測試送出 | 一鍵推送一筆 webhook.test payload 驗證接收端 |
| 看 Log | 最近 50 筆 delivery — 狀態、attempt、response、payload |
| 重送 | 對 failed_retry / dead 的 delivery 手動再試 |
| 重設 secret | 產新 secret,舊的立即失效(接收端需更新) |
| 刪除 endpoint | 連同 delivery log 一起刪 |
透過 MCP / OpenAPI 管理
不想用 dashboard?同一組功能也開在 MCP:
| MCP tool | 等效 REST |
|---|---|
webhooks.list | POST /api/v1/tools/webhooks.list |
webhooks.create | POST /api/v1/tools/webhooks.create |
webhooks.update | POST /api/v1/tools/webhooks.update |
webhooks.delete | POST /api/v1/tools/webhooks.delete(兩階段確認) |
webhooks.rotate_secret | POST /api/v1/tools/webhooks.rotate_secret |
webhooks.test | POST /api/v1/tools/webhooks.test |
webhooks.list_deliveries | POST /api/v1/tools/webhooks.list_deliveries |
webhooks.redeliver | POST /api/v1/tools/webhooks.redeliver |
webhooks.event_types | POST /api/v1/tools/webhooks.event_types |
Scope 沿用 homeinfo:read / homeinfo:write。
FAQ
Q:可以反向接 webhook 嗎(你來推給 HypeLink)? A:v1 只有 outbound。如果要 inbound 看 OpenAPI REST。
Q:如何擋 replay 攻擊?
A:比對 X-HypeLink-Timestamp,> 5 分鐘的就拒絕。
Q:HypeLink 會記住事件 ID 避免我收到重複嗎?
A:每筆 delivery 都有獨立 X-HypeLink-Delivery UUID — 強烈建議接收端用它做冪等。重試與 dashboard 手動 redeliver 可能會送到同一個邏輯事件多次。
Q:可以指定 endpoint 只收某些 hypeId 的事件嗎?
A:endpoint 已綁定品牌(hypeId),不會收到別人的事件。一個品牌底下可設多個 endpoint 各自訂閱。
Q:如果接收端慢,會阻塞 HypeLink 嗎? A:不會。我們對接收端設 10 秒 timeout,超時視為失敗走 retry,不會阻塞前台流程(報名、編輯都正常完成)。
Roadmap
| 規劃中 | 預期 |
|---|---|
event.checkin.created(現場簽到) | v1.1 |
event.broadcast.sent(廣播完成) | v1.1 |
member.created / member.tier_changed | v1.2(會員系統) |
payment.received / payment.refunded(接 Stripe) | v1.2 |
| Webhook payload version 欄位(升 schema 用) | v2 |