Webhook
Webhook 允許外部系統在 Kanera 中的工作變化時做出反應。
當另一個系統應該知道變更而不需要不斷呼叫 API 時,使用 webhook。 常見示例包括:
- 更新外部儀表板。
- 同步更改的卡片到另一個系統。
- 觸發部署或審查工作流程。
- 在審計系統中記錄 Kanera 活動。
要將卡片活動釋出到團隊聊天通道中,您不需要自己的 webhook。 Kanera 內建了 聊天通知目標 為 Slack, Discord, Telegram, 並 Zulip 當你需要完整的事件目錄、準確的有效載荷和簽名驗證時,可以使用 webhook。
工作區管理員可以從 工作區設定 -> API,在 自定義webhook 單獨的板有相同的 API 整合還可以透過公共 API 或 TypeScript SDK 註冊端點,並且可以在自身的板設定中註冊端點。
每個 webhook 目標都可以接收所有事件或僅接收選定的事件型別。 Leave eventTypes 空接收每個事件,或提供一個或多個事件型別字串如下所示。
計劃和許可權
- 工作區管理員可以檢視和管理該工作區中的每個通用 Webhook 端點。
- 當端點的使用者是工作區成員時,具有寫入功能的 API 憑證或互動式 OAuth 代理可以在沒有管理員許可權的情況下注冊端點。該端點的範圍僅限於建立連線:只有相同的連線和工作區管理員才能檢視或管理它。讀取範圍的憑據無法管理 Webhook。
- 網路鉤子需要 Pro 30天 Pro試用。它們在託管 Free 上不可用。
- 在託管版 Free 套餐中,建立表單會替換為 您的計劃中不提供 Webhook.
撤銷憑據或代理連線還會刪除其連線範圍內的端點。 管理員建立的工作區端點保留,直到管理員刪除它們。
降級時會發生什麼
當託管組織從 Pro 或試用版遷移到 Free 時,Kanera 會暫停每個啟用的 webhook 端點,而不是刪除任何內容。 它還會撤銷工作區 API 金鑰(私鑰仍然有效 ), 並禁用超出 Free 限制的自動化。
| 主機上 Free | 行為 |
|---|---|
| 現有終點 | 自動暫停,並且當組織處於 Free 狀態時無法重新啟用它們。 |
| 新活動 | 未到達您的端點。 |
| 還有什麼正在等待傳送 | 記錄為失敗 webhooks are unavailable on the current plan 而不是遲到。 |
| 交付失敗 | 無法手動重試。 |
| 配置和歷史記錄 | 保留。 端點、事件選擇、秘密和交付歷史記錄都仍然存在。 |
升級回 Pro 版本會重新啟用因降級而暫停的端點,並恢復其撤銷的工作區 API 金鑰,因此無需重新構建恢復的整合。
透過程式設計註冊端點
公共 API 會公開 Workspace 設定所使用的相同通用端點生命週期。
/api/v1/workspaces/{workspaceId}/webhooks
| 方法 | 路徑 | 使用 |
|---|---|---|
GET | (基本路徑) | 列出呼叫者可見的端點。 |
POST | (基本路徑) | 建立一個端點並返回其簽名秘密一次。 |
PATCH | /{endpointId} | 更改其名稱、URL、事件過濾器或啟用狀態。 |
POST | /{endpointId}/secret | 旋轉簽名秘密。 |
DELETE | /{endpointId} | 刪除端點。 |
GET | /{endpointId}/deliveries | 閱讀最近的交付嘗試。 |
POST | /{endpointId}/deliveries/{deliveryId}/retry | 重試一次失敗的交付。 |
使用 TypeScript SDK,使用 kanera.webhookEndpoints.create(), .list(), .update(), .rotateSecret(), .deliveries(), .retryDelivery(), 和 .delete()。返回的端點有 scope: "workspace" 用於管理員管理的端點或 scope: "connection" 當非管理員憑據註冊它時。 secret 透過建立或旋轉立即返回;後續讀取從不暴露它。
Webhook 事件型別
公共 API 將當前列表公開於:
https://api.kanera.app/webhook-event-types
| 面積 | 事件型別 |
|---|---|
| 列表 | list:created, list:updated, list:moved, list:rebalanced, list:deleted |
| 板隔板 | separator:created, separator:updated, separator:moved, separator:rebalanced, separator:deleted |
| 自定義欄位 | customField:created, customField:updated, customField:moved, customField:rebalanced, customField:deleted |
| 自定義欄位選項 | customFieldOption:created, customFieldOption:updated, customFieldOption:moved, customFieldOption:rebalanced, customFieldOption:deleted |
| 卡片 | card:created, card:updated, card:moved, card:rebalanced, card:deleted |
| 卡片自定義欄位 | card:customFieldValue:set, card:customFieldValue:cleared |
| 卡片標籤和受讓人 | card:labels:set, card:assignees:set |
| 卡片附件 | card:attachment:created, card:attachment:deleted |
| 卡片清單 | card:checklist:created, card:checklist:updated, card:checklist:moved, card:checklist:rebalanced, card:checklist:deleted |
| 卡清單專案 | card:checklistItem:created, card:checklistItem:updated, card:checklistItem:moved, card:checklistItem:rebalanced, card:checklistItem:deleted |
| 工作區卡片標籤 | cardLabel:created, cardLabel:updated, cardLabel:moved, cardLabel:rebalanced, cardLabel:deleted |
| 評論意見 | comment:created, comment:updated, comment:deleted |
| 評論反應 | comment:reaction:added, comment:reaction:removed |
| 卡片進給 | card:feedItem:created, card:feedItem:updated, card:feedItem:deleted |
| 看板 | board:created, board:updated, board:moved, board:rebalanced, board:deleted |
| 看板成員 | board:member:added, board:member:removed |
| 工作區 | workspace:updated, workspace:deleted |
| 工作區成員 | workspace:member:added, workspace:member:updated, workspace:member:removed |
| 使用者 | user:profile:updated |
| 筆記 | note:created, note:updated, note:moved, note:rebalanced, note:deleted, note:locked, note:unlocked |
| 代理執行 | agentRun:started, agentRun:updated |
有效載荷格式
Kanera 將每個 webhook 作為 JSON 物件傳送,具有穩定的信封和事件特定性 data.
| 外地 | 意義 |
|---|---|
id | 唯一的 webhook 事件 ID。 X-Kanera-Event-Id. |
type | 事件型別,如 card:created. |
workspaceId | 事件發生的工作區。 |
boardId | 當事件屬於看板範圍時,事件發生的看板。 |
cardId | 當事件有效負載識別出卡時,卡參與事件。 |
occurredAt | 事件發生時的 ISO 8601 時間戳。 |
data | 形狀取決於: type 並匹配相應的實時事件有效載荷。 |
代理執行的事件攜帶 { boardId, cardId, run } 在 data, 其中 run 是什麼原因導致了這種情況? agentName, 的 userId 它為什麼會這樣做? status, title, summary, externalUrl,及其 startedAt, heartbeatAt, 和 endedAt 時間戳。 agentRun:updated 對於每一個心跳以及狀態變化都會發射,所以請將您的處理器鍵入 run.status 一個停止心跳的執行作為最後一個步驟到達,而不是將每個交付作為新的工作。 agentRun:updated 與 status: "stalled".
例 子 card:created 有效載荷:
{
"id": "7e5c982a-e6c9-4ea7-9647-b14fe73ed50a",
"type": "card:created",
"workspaceId": "a069fd45-3bb3-4928-bac9-cb574a050d20",
"boardId": "48a52a55-763e-4c64-8a76-51ac56247f5c",
"cardId": "70c39cef-1aec-44e6-a8f5-f36f765a818d",
"occurredAt": "2026-05-28T12:34:56.000Z",
"data": {
"boardId": "48a52a55-763e-4c64-8a76-51ac56247f5c",
"card": {
"id": "70c39cef-1aec-44e6-a8f5-f36f765a818d",
"boardId": "48a52a55-763e-4c64-8a76-51ac56247f5c",
"listId": "3bb3c1b3-8fc7-4807-854d-2d5ff62df147",
"title": "Prepare kickoff agenda",
"position": "1000.0000000000"
}
}
}
網路鉤傳送
Kanera 會以 JSON 形式傳送 webhook 交付,幷包含您可以用來驗證請求的報頭:
| 頭條 | 意義 |
|---|---|
X-Kanera-Event-Id | 唯一事件 ID。 |
X-Kanera-Timestamp | Unix 時間戳(秒 ) 。 |
X-Kanera-Signature | sha256= 加上一個 HMAC-SHA256 簽名。 |
簽名是根據以下因素計算的:
${timestamp}.${rawBody}
使用 webhook 端點秘密。
當端點返回非 2xx 狀態或無法訪問時,Webhook 交付將重試。 Kanera 記錄交付歷史記錄,以便您可以檢視失敗、重試交付、暫停目標和從工作區 API 設定中輪換秘密。
驗證 webhook 簽名
在信任有效載荷之前驗證簽名。
該 TypeScript SDK 為你做這件事,並且丟擲而不是返回一個布林值,這樣處理器就不會忘記檢查:
import { parseWebhook } from "@kanera/sdk";
app.post("/kanera/webhook", express.raw({ type: "application/json" }), async (req, res) => {
const event = await parseWebhook({
secret: process.env.KANERA_WEBHOOK_SECRET!,
payload: req.body, // the raw bytes
headers: req.headers,
});
// Handle event.type, event.workspaceId, event.boardId, event.cardId, and event.data.
res.sendStatus(204);
});
要自己驗證,請比較以上的 HMAC {timestamp}.{body} 注意長度檢查: crypto.timingSafeEqual 當兩個緩衝區的長度不同時,攻擊者會控制它們。
import crypto from "node:crypto";
import express from "express";
const app = express();
app.post("/kanera/webhook", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.header("X-Kanera-Timestamp") ?? "";
const signature = req.header("X-Kanera-Signature") ?? "";
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.KANERA_WEBHOOK_SECRET!)
.update(`${timestamp}.${req.body.toString("utf8")}`)
.digest("hex");
const received = Buffer.from(signature);
const digest = Buffer.from(expected);
if (received.length !== digest.length || !crypto.timingSafeEqual(received, digest)) {
return res.status(401).send("invalid signature");
}
const event = JSON.parse(req.body.toString("utf8"));
// Handle event.type, event.workspaceId, event.boardId, event.cardId, and event.data.
res.sendStatus(204);
});
始終根據原始請求正文進行驗證。如果您的框架在驗證之前解析 JSON,則簽名檢查可能會失敗或變得不可靠。
Kanera也傳送 X-Kanera-Timestamp 以及 X-Kanera-Event-Id。拒絕時間戳超出現在幾分鐘時間的交付以繫結重播,並使用事件 ID 使處理程式冪等,因為交付可能會重試。
相關出境選項
Webhook 是通用路徑。 針對特定受眾群體,有兩種更狹窄的選項:
| 路徑 | 觀眾 | 有效載荷 | 設定位置 | 訂閱方案 |
|---|---|---|---|---|
| Webhook | 另一個系統 | 整個事件目錄中已簽名的 JSON | 工作區設定 -> API | Pro或試用 |
| 聊天目的地 | 團隊頻道 | 格式化的訊息,適用於 Slack、Discord、Telegram 或 Zulip | 工作區設定 -> 整合 | Pro或試用 |
| 個人交付渠道 | 1人 | ntfy、Gotify 或已簽名的 JSON 用於您自己的通知 | 個人資料設定 -> 通知 | Pro或試用 |
| MCP 活動訂閱 | AI代理的主機 | 簽名卡和評論發生到回撥代理人驗證 | 代理透過 MCP 訂閱;無需在 Kanera 中設定 | 每個計劃 |
聊天目標與 Webhook 端點分開管理,並且不會出現在 Webhook 列表中,因此輪換 Webhook 機密絕不會影響聊天目標。
自動化的 呼叫網路鉤子 行動是有針對性的,而不是基於訂閱。 automation:webhook:called 只對規則中選擇的端點執行,而不管該端點的普通 eventTypes 過濾器,同時保持相同的簽名、重試和遞送歷史記錄行為。 自動化.
