TypeScript SDK
@kanera/sdk 一個型別化的客戶端 Kanera 公共 API。它沒有執行時依賴性,並且構建在全域性之上 fetch,因此它在 Node 18+、Bun、Deno、Cloudflare Workers 和瀏覽器上執行不變。
npm install @kanera/sdk
import { Kanera } from "@kanera/sdk";
const kanera = new Kanera({ apiKey: process.env.KANERA_API_KEY! });
const session = await kanera.session();
if (session.scope === "read") throw new Error("this credential cannot write");
for await (const card of kanera.work.iterateCards()) {
console.log(card.key, card.title);
}
await kanera.cards.setCompletion("MKT-42", true);
await kanera.comments.create("MKT-42", { body: "Shipped in 1.4.0." });
該SDK是根據以下許可釋出的: 麻省理工學院 許可證,故意比Kanera伺服器(彈性許可證2.0)更寬鬆,因此將其嵌入到您自己的應用程式中不會引起許可證政策問題。
認證
建立一個 API 金鑰 配置檔案設定 -> API 金鑰 或 工作區設定 -> API,並從環境中讀取它而不是提交它。看 選擇 API 金鑰型別 哪種適合你的融合。
const kanera = new Kanera({ apiKey: process.env.KANERA_API_KEY! });
指向客戶端您自己的部署:
const kanera = new Kanera({ apiKey, baseUrl: "https://api.your-kanera.example" });
首先檢查範圍
const { scope } = await kanera.session();
read 意味著每個突變都會被拒絕 FORBIDDEN 在啟動時檢查一次比在遷移過程中發現它要便宜。
客戶端上有什麼
| 名稱空間 | 封面 |
|---|---|
kanera.workspaces | 工作區、其看板、成員和自動化 |
kanera.boards | 無障礙看板、開啟看板、看看板和活動; watch 以及 unwatch |
kanera.lists | 列表級卡片操作:存檔、移動、整合完成 |
kanera.separators | 建立、樣式、位置、移動和刪除板列表分隔符 |
kanera.cards | 卡片,加上 .attachments, .checklists, 和 .bulk;觀看和提醒(watch, unwatch, setReminder, clearReminder, listReminders, followState); pollNotifications |
kanera.comments | 卡片評論和反應 |
kanera.runs | 代理執行:宣佈、檢測訊號並完成卡上的飛行工作 |
kanera.notes | 筆記、反向連結和筆記附件 |
kanera.search | 跨無障礙內容搜尋 |
kanera.work | 跨板工作、歷史記錄、時間表、累積摘要和下一步佇列 |
kanera.automations | 工作區自動化規則和型別化執行歷史記錄 |
kanera.webhookEndpoints | 註冊和管理出站 webhook,檢查交付,並重試失敗 |
kanera.http | 原始請求客戶端,對於任何尚未包裝的內容 |
卡片參考
任何地方有一張卡被命名,傳遞一個 UUID,一個人類金鑰,例如 MKT-42,或規範的卡 URL。每個客戶端都會解析一次金鑰並快取,因此在批次操作中重複一個金鑰會花費一次查詢。
金鑰字首僅在組織內部是唯一的,而個人憑證可以看到多個。多個可見的關鍵是 拒絕,不是猜測 — 傳遞規範 URL 或 UUID 來消除歧義。
時間表
讀取與下面相同的工作日時間表 時間軸檢視:
const schedule = await kanera.work.schedule({ boardId }); // or { workspaceId }, never both
console.log(schedule.summary.projectedFinish, schedule.summary.slipDays);
const critical = schedule.cards.filter(card => card.critical);
const conflicts = schedule.cards.filter(card => card.conflicts.length > 0);
// This changes the card's saved dates; it requires write access.
await kanera.cards.update("MKT-42", {
startDateLocalDate: "2026-11-02",
dueDateLocalDate: "2026-11-06",
});
const after = await kanera.work.schedule({ boardId });
期限 slackDays, 衝突 days, 和 summary.slipDays 計算週一至週五工作日。 plannedStart / plannedEnd 時間表遵循跨板可訪問的連線鏈,並省略已完成和不可訪問的卡片。 它不會自動將日期應用到繼承者。
個人簡報, kanera.work.scheduleAttention({ limit: 10 }) 返回分配的衝突、關鍵和遲到部分。 kanera.work.portfolio() 返回帶有每個看板排程執行狀況的受支援的跨看板累積。 MCP 活動 是與 SDK 的普通出站 webhooks 助手分開的託管協議。
卡牌關係
讀取、新增和刪除 依賴性 引用可以是金鑰、UUID 或標準卡片 URL。
// MKT-42 blocks WEB-18. Both cards must belong to the same workspace.
const relation = await kanera.cards.addRelation("MKT-42", "WEB-18", "blocks");
await kanera.cards.addRelation("WEB-18", "MKT-43", "blocked_by");
await kanera.cards.addRelation("WEB-18", "MKT-44", "relates_to");
const { relations, hiddenBlockerCount } = await kanera.cards.listRelations("WEB-18");
const detail = await kanera.cards.get("WEB-18"); // also includes relations
const waiting = await kanera.work.cards({ filters: { blockedOnly: true } });
await kanera.cards.removeRelation("MKT-42", relation.id);
listRelations 返回的僅是憑據可以開啟的卡片。 隱藏的活躍阻止器有助於 hiddenBlockerCount。新增是冪等的;迴圈和完成的卡片作為新的阻擋者被拒絕。工作查詢過濾器還支援 blockedOnly: true 找到在可訪問的看板上被封鎖的開放卡片,以及 atRiskOnly: true 透過 SDK 完成卡片不會顯示 Web App 的"工作被阻止"警告,因為您的可見阻止程式已經過期或到期時間晚於該卡片。
依賴圖
cards.dependencyGraph 讀取與 Web 檢視相同的有界圖形。 選擇一個看板、工作區或一組卡片引用:
const aroundCard = await kanera.cards.dependencyGraph({ cards: ["MKT-42"] });
const boardGraph = await kanera.cards.dependencyGraph({ boardId });
const workspaceGraph = await kanera.cards.dependencyGraph({ workspaceId });
for (const edge of aroundCard.edges) {
console.log(edge.sourceCardId, edge.kind, edge.targetCardId);
}
結果包括卡片、邊緣、查詢、卡片面自定義欄位、板 canEdit 旗幟,以及 truncated. cards 接受金鑰、UUID 或規範URL 並將其解析為 API cardIds。棋盤圖可以包括其他可讀棋盤上連線的卡片; inScope 隱藏的活性阻滯劑仍然透過 blockedByCount,圖表停在 400 張卡片處。參見 圖形 API 詳細資訊請參閱
板隔板
分隔符是看板所擁有的專案,與卡片共享列表的排序軌道。 它們的型別錨點可以針對任何型別的專案,因此整合可以建立一個精確的部分,然後在其周圍放置卡片,而無需計算數值位置。
const separator = await kanera.separators.create(boardId, listId, {
title: "This week",
color: "blue",
anchor: { side: "before", item: { type: "card", id: cardId } },
});
await kanera.cards.move("MKT-42", {
listId,
anchor: { side: "after", item: { type: "separator", id: separator.id } },
});
await kanera.separators.update(separator.id, { title: "Next up", color: "green" });
使用 kanera.separators.move 可將分隔線移動到某張卡片、另一條分隔線或列表邊緣附近。刪除分隔線不會移動任何卡片。
提醒和觀看
觀看和提醒屬於連線的人員,因此他們需要個人金鑰或 OAuth 連線;工作區金鑰接收 403。提醒是私人的,每張卡最多一次,最多提前八週。
await kanera.cards.watch("MKT-42");
const state = await kanera.cards.setReminder("MKT-42", new Date("2026-10-16T15:00:00+01:00"));
state.reminder?.remindAt; // "2026-10-16T14:00:00.000Z"
const pending = await kanera.cards.listReminders(); // soonest first, with each card's key, title and url
await kanera.cards.clearReminder("MKT-42");
setReminder 替換卡片上的任何現有提醒,包括已經觸發的提醒。 參見 提醒事項.
要在不接收 webhook 的情況下了解回覆和提及資訊,請投票您的通知收件箱。 cursor 並將其作為 since:
let cursor: string | undefined;
const page = await kanera.cards.pollNotifications({ since: cursor });
for (const item of page.items) console.log(item.summary, item.card?.url);
cursor = page.cursor; // repeat while page.hasMore
建立多張卡片
kanera.cards.bulk.create 一個請求最多可以在一個板上建立50張卡片,每張卡片都有自己的列表、標籤、受讓人和到期日期。 每張卡片在建立之前都會被驗證,因此請先透過一個驗證。 idempotencyKey 整個批次可以安全地重試。
const { cards } = await kanera.cards.bulk.create(boardId, [
{ listId, title: "Draft the brief", labelIds: [labelId] },
{ listId, title: "Review with legal", assigneeIds: [userId], dueDateLocalDate: "2026-11-02" },
], { idempotencyKey: crypto.randomUUID() });
kanera.cards.create 採取相同的 labelIds, assigneeIds, dueDateLocalDate, 和 dueDateSlot 一個單一的卡。
檢查清單
透過一個請求建立一個包含其全部內容的清單:專案與描述、分配者和到期日期,以及頂級專案下的一個子清單級別。 伺服器生成並原子化連線每個 ID,並返回它們,因此之後不需要查詢。
const plan = await kanera.cards.checklists.create("MKT-42", {
title: "Launch",
items: [
{
text: "Prepare release",
description: "Coordinate the release.",
assigneeId: userId,
dueDateLocalDate: "2026-12-01",
subChecklists: [{ title: "Verification", items: [{ text: "Run smoke tests" }, { text: "Confirm rollback" }] }],
},
{ text: "Announce launch" },
],
});
// Add items in order at a position (omit the anchor to append).
await kanera.cards.checklists.addItems("MKT-42", plan.id, [{ text: "Kickoff" }], { afterItemId: null });
// Different changes for chosen items, applied atomically.
const [verification] = plan.subChecklists;
await kanera.cards.checklists.updateItems("MKT-42", verification.items.map((item) => ({ itemId: item.id, changes: { completed: true } })));
// The same assignee or due date on every item of one checklist.
await kanera.cards.checklists.updateAllItems("MKT-42", plan.id, { assigneeId: userId });
// Read the card's checklists without the rest of card detail.
const { checklists } = await kanera.cards.checklists.list("MKT-42");
分項清單專案只能採取 text 以及 completed,並且專案僅在其自己的組內移動:頂級清單之間,或一個父專案的子清單之間。在寫入任何內容之前,都會對批次進行完整驗證;被拒絕的請求 error.options.body.issues 確切的欄位名稱,如 items[0].subChecklists[0].items[1].text,未知欄位會被拒絕而不是被忽略。
代理執行
當您的整合是代理人正在處理卡片時,請告訴 Kanera 該卡片已開始處理。 卡片圖塊隨後會顯示一個實時晶片,其中標明代理人和作業的名稱,卡片詳細資訊顯示在飛行中執行的情況,終端結果會移動到活動源中,無需任何人猜測作業是否仍在執行。
const run = await kanera.runs.start("MKT-42", {
title: "Drafting launch copy",
summary: "Reading the brief and the last two campaigns.",
externalUrl: "https://github.com/acme/site/pull/318", // optional; where a person can watch it
});
// Every update is a heartbeat. Send one at least every 10 minutes on a long job.
await kanera.runs.heartbeat(run.id);
// Waiting on a person? Say so, and the card's chip turns amber.
await kanera.runs.update(run.id, { status: "blocked", summary: "Need sign-off on the pricing line." });
await kanera.runs.finish(run.id, "succeeded", { summary: "Draft posted as a comment." });
finish 採取 succeeded, failed, 或 cancelled。終端狀態結束執行並使其不可變,因此稍後 update 上面的圖片 CONFLICT 錯誤 — 檢查 error.status === 409
如果重試可以在你自己的後面著陸 finish.
標記為十五分鐘沒有心跳的實時執行 stalled 由 Kanera停頓執行並不會結束:代理仍然可以返回並關閉它,或者設定一個新的執行狀態,以便它繼續執行。 status: "running" 恢復。
使用 kanera.runs.list(card) 開始之前,這樣兩個代理不會執行相同的卡, { includeEnded: true }
為了完整的歷史,和 kanera.runs.get(runId) 重讀一次。
註冊網鉤
管理員管理工作區中的每個端點。非管理員可寫入的憑據或互動式 OAuth 代理也可以註冊端點,但只能看到其自己建立的連線。當代理需要事件回撥而無需工作區管理員許可權時,這非常有用。
const endpoint = await kanera.webhookEndpoints.create(workspaceId, {
name: "Release agent",
url: "https://agent.example.com/kanera",
eventTypes: ["card:updated", "comment:created"], // [] means every event
});
// The secret is returned only now (and after rotateSecret).
await saveSecret(endpoint.secret);
const deliveries = await kanera.webhookEndpoints.deliveries(workspaceId, endpoint.id);
連線範圍端點返回 scope: "connection";管理員建立的端點返回
scope: "workspace"。該資源還提供 list, update, rotateSecret, retryDelivery, 和 delete。參見 Webhook 用於有效載荷驗證和交付語義。
分頁
每個列表端點都有邊界;沒有"給我整個看板"呼叫。 常見的整合錯誤是讀取第一頁並將其視為整個集合,因此迭代是預設形式:
for await (const board of kanera.boards.iterate()) { /* … */ }
const firstFifty = await kanera.comments.iterate("MKT-42").all(50);
// Or drive the pages yourself.
let cursor: string | undefined;
do {
const page = await kanera.comments.list("MKT-42", { cursor });
cursor = page.nextCursor ?? undefined;
} while (cursor);
錯誤
import { KaneraApiError } from "@kanera/sdk";
try {
await kanera.cards.update("MKT-42", { title: "New" });
} catch (error) {
if (error instanceof KaneraApiError && error.isForbidden) {
// A read-only credential, or no Editor access to this board. Retrying will not help.
}
}
KaneraApiError 帶有 HTTP status,Kanera的穩定誤差 code,以及值得分支的案例的守衛: isUnauthenticated, isForbidden, isNotFound, isRateLimited, isRetryable。運輸故障增加 KaneraConnectionError.
重複和同等效力
讀取和 DELETE/PUT 對速率限制和瞬態上游故障自動重試,具有指數回退、全抖動和 Retry-After 當 Kanera 是的,是的。
除非您提供等效金鑰,否則不會重試突變,因為在模糊失敗後重試會建立第二張卡片或釋出第二條評論。提供一個,API 重放第一個結果而不是重複寫入:
await kanera.comments.create("MKT-42", { body: "Deployed." }, {
idempotencyKey: crypto.randomUUID(),
});
驗證 webhook
parseWebhook 驗證簽名並返回解析事件,或者 throws。 它會丟擲而不是返回布林值,這樣處理器就不會忘記在使用有效載荷之前進行檢查:
import { parseWebhook } from "@kanera/sdk";
app.post("/kanera", async (req, res) => {
const event = await parseWebhook({
secret: process.env.KANERA_WEBHOOK_SECRET!,
payload: req.rawBody, // the exact bytes; re-serialised JSON will not verify
headers: req.headers,
});
// event.type, event.workspaceId, event.data
res.sendStatus(204);
});
簽名封面 {timestamp}.{body},並根據五分鐘視窗(可配置)檢查時間戳以繫結重播。它使用 Web Crypto,因此它可以在工作執行緒和邊緣執行時以及 Node.js 中執行。參見 Webhook 用於端點設定和事件目錄。
備選方案
new Kanera({
apiKey,
baseUrl: "https://api.kanera.app", // your own origin when self-hosting
timeoutMs: 30_000,
maxRetries: 2,
organisationId, // pin an identity-wide personal credential to one organisation
userAgent: "acme-sync/2.1", // appended to the SDK's own User-Agent
onRetry: ({ attempt, delayMs }) => log.warn({ attempt, delayMs }),
fetch: myFetch, // supply your own agent or proxy
});
逃生閘
API 的移動速度比型別表面快,並且沒有任何東西被封裝在封裝器後面:
const result = await kanera.http.post<MyType>("/api/v1/some/new/endpoint", body);
關於瀏覽器的說明
該軟體包在瀏覽器中執行,但在瀏覽器中使用它意味著向客戶端傳送 API 金鑰,只能在受信任的第一方上下文中進行。 對於面向使用者的任何內容,請從伺服器呼叫 Kanera。