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 サーバー (Elastic License 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 エラー。起動時に 1 回確認するほうが、移行の途中で発見するよりもコストがかかりません。
クライアントにあるもの
| 名前空間 | カバー |
|---|---|
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。キーはクライアントごとに 1 回解決され、キャッシュされるため、一括操作全体でキーを繰り返すと 1 回の検索にコストがかかります。
キー プレフィックスは組織内でのみ一意であり、個人の資格情報では複数のプレフィックスを参照できます。複数のキーで表示されるキーは、 拒否されました、推測されません — 曖昧さを解消するために正規 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 の通常の送信 Webhook ヘルパーとは別のホストされたプロトコルです。
カード関係
読み取り、追加、削除 依存関係 型付きのカードメソッドを使用します。参照にはキー、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 アプリのブロックされた作業の警告は表示されません。
依存関係グラフ
cards.dependencyGraph Web ビューと同じ有界グラフを読み取ります。ボード、ワークスペース、またはカード参照のセットを 1 つ選択します。
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。リマインダーは非公開で、カードごとに最大 1 つ、最大 8 週間先まで送信されます。
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 1 回のリクエストで 1 つのボードに最大 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 シングルカードの場合。
チェックリスト
1 つのリクエストで内容全体を含むチェックリストを作成します。説明、担当者、期限を含む項目と、トップレベルの項目の下に 1 レベルのサブチェックリストが含まれます。サーバーはすべての 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、項目は独自のグループ内、つまりトップレベルのチェックリスト間、または 1 つの親項目のサブチェックリスト間でのみ移動します。バッチは、何かが書き込まれる前に完全に検証されます。拒否されたリクエストの 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.
15 分間ハートビートのないライブ実行がマークされます。 stalled そのため、クラッシュしたエージェントが「動作中」と表示されたカードを永久に残すことはできません。停止した実行は終了しません。エージェントは戻ってきて実行を閉じるか、実行を終了することができます。 status: "running" 再開します。
使用する kanera.runs.list(card) 2 つのエージェントが同じカードを実行しないように開始する前に、 { includeEnded: true }
完全な歴史については、 kanera.runs.get(runId) 1件の実行を再取得します。
Webhook を登録する
管理者は、ワークスペース内のすべてのエンドポイントを管理します。管理者以外の書き込み可能な資格情報または対話型 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 ペイロードの検証と配信セマンティクスのため。
ページネーション
すべてのリストのエンドポイントは制限されています。 「ボード全体を私にください」という要求はありません。一般的な統合バグは、ページ 1 を読み取り、それをセット全体として扱うことです。そのため、反復がデフォルトの形状になります。
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が送ってくれると光栄です。
冪等キーを指定しない限り、ミューテーションは再試行されません曖昧な失敗の後に再試行すると、2 番目のカードが作成されるか、2 番目のコメントが投稿されるためです。 1 つを指定すると、API は書き込みを繰り返す代わりに最初の結果を再生します。
await kanera.comments.create("MKT-42", { body: "Deployed." }, {
idempotencyKey: crypto.randomUUID(),
});
Webhook を検証する
parseWebhook 署名を検証して解析済みイベントを返すか、例外を投げます。真偽値ではなく例外にすることで、ハンドラーが確認を忘れたままペイロードを使うことを防ぎます:
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}、タイムスタンプは 5 分間のウィンドウ (構成可能) に対してチェックされ、リプレイが制限されます。 Web Crypto を使用するため、Node だけでなくワーカーやエッジ ランタイムでも動作します。参照 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を呼び出してください。