Typescript SDK
@kanera/sdk is een getypte client voor de Openbare API van Kanera. Het heeft geen runtime-afhankelijkheden en is gebouwd op global fetch, dus het werkt ongewijzigd op Node 18+, Bun, Deno, Cloudflare Workers en browsers.
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." });
De SDK wordt gepubliceerd onder de MIT licentie, opzettelijk toleranter dan de Kanera-server (Elastic License 2.0), dus het inbedden ervan in uw eigen applicatie roept geen vragen op over het licentiebeleid.
Authenticeren
Maak een API-sleutel aan Profielinstellingen -> API-sleutels of Werkruimte-instellingen -> API, en lees het uit de omgeving in plaats van het te plegen. Zien kies een sleuteltype API welke soort past bij uw inburgering.
const kanera = new Kanera({ apiKey: process.env.KANERA_API_KEY! });
Zelf hosten? Wijs de client op uw eigen implementatie:
const kanera = new Kanera({ apiKey, baseUrl: "https://api.your-kanera.example" });
Controleer eerst de reikwijdte
const { scope } = await kanera.session();
read betekent dat elke mutatie wordt geweigerd met een FORBIDDEN fout. Eén keer controleren bij het opstarten is goedkoper dan het halverwege een migratie ontdekken.
Wat staat er op de cliënt
| Naamruimte | Hoezen |
|---|---|
kanera.workspaces | Werkruimten, hun borden, leden en automatiseringen |
kanera.boards | Toegankelijke borden, een bord openen, bordkaarten en activiteit; watch en unwatch |
kanera.lists | Kaartbewerkingen op lijstniveau: archiveren, verplaatsen, voltooien van sets |
kanera.separators | Scheidingstekens voor bordlijsten maken, stylen, positioneren, verplaatsen en verwijderen |
kanera.cards | Kaarten, plus .attachments, .checklists, en .bulk; kijken en herinneringen (watch, unwatch, setReminder, clearReminder, listReminders, followState); pollNotifications |
kanera.comments | Kaartopmerkingen en reacties |
kanera.runs | Agent loopt: aankondiging, hartslag en voltooiing van het werk tijdens de vlucht op een kaart |
kanera.notes | Notities, backlinks en notitiebijlagen |
kanera.search | Zoek in toegankelijke inhoud |
kanera.work | Grensoverschrijdend werk, geschiedenis, planningen, samenvattende samenvattingen en wachtrijen voor Volgende |
kanera.automations | Regels voor automatisering van de werkruimte en getypte uitvoeringsgeschiedenis |
kanera.webhookEndpoints | Registreer en beheer uitgaande webhooks, inspecteer leveringen en mislukte pogingen |
kanera.http | De onbewerkte verzoekclient, voor alles wat nog niet is ingepakt |
Kaartreferenties
Overal waar een kaart een naam heeft, geeft u een UUID door, een menselijke sleutel zoals MKT-42of een canonieke kaart-URL. Sleutels worden één keer per client opgelost en in de cache opgeslagen, dus het herhalen van één sleutel in een bulkbewerking kost één keer opzoeken.
Een sleutelvoorvoegsel is alleen uniek binnen een organisatie, en een persoonlijke identificatie kan er meerdere zien. Een sleutel zichtbaar in meer dan één is afgewezen, niet geraden: geef de canonieke URL of de UUID door om het ondubbelzinnig te maken.
Tijdlijnschema's
Lees hetzelfde werkdagschema als de Tijdlijnweergave:
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 });
Duur, slackDays, conflict days, en summary.slipDays tel de werkdagen van maandag tot en met vrijdag. plannedStart / plannedEnd beschrijf het plan; vroegste/laatste datums beschrijven de afhankelijkheidsberekening. Het schema volgt toegankelijke, verbonden ketens over de planken heen en laat voltooide en ontoegankelijke kaarten weg. Data worden niet automatisch toegepast op opvolgers.
Voor een persoonlijke briefing kunt u kanera.work.scheduleAttention({ limit: 10 }) retourneert toegewezen conflict-, kritieke en laat-aan-start-secties. kanera.work.portfolio() retourneert de ondersteunde cross-board rollup met planning per board. MCP-evenementen zijn een afzonderlijk gehost protocol dan de gewone uitgaande webhook-helpers van de SDK.
Kaartrelaties
Lezen, toevoegen en verwijderen afhankelijkheden met de getypte kaartmethoden. Verwijzingen kunnen sleutels, UUID's of canonieke kaart-URL's zijn.
// 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 retourneert alleen kaarten die door de inloggegevens kunnen worden geopend. Verborgen actieve blokkers dragen hieraan bij hiddenBlockerCount. Toevoegingen zijn idempotent; cycli en voltooide kaarten als nieuwe blokkers worden afgewezen. De werkqueryfilters ondersteunen ook blockedOnly: true om geblokkeerde open kaarten op toegankelijke borden te vinden, en atRiskOnly: true voor open kaarten waarvan de zichtbare blokkeerder te laat is of erna komt. Als u een kaart voltooit via de SDK, wordt de waarschuwing voor geblokkeerd werk van de webapp niet weergegeven.
Afhankelijkheidsgrafieken
cards.dependencyGraph leest dezelfde begrensde grafiek als de webweergave. Kies één bord, werkruimte of set kaartreferenties:
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);
}
Het resultaat omvat kaarten, randen, opzoekingen, aangepaste velden op de kaart, bord canEdit vlaggen, en truncated. cards accepteert sleutels, UUID's of canonieke URL's en zet deze om naar de API's cardIds. Een bordgrafiek kan verbonden kaarten op andere leesbare borden bevatten; inScope onderscheidt ze. Verborgen actieve blokkers blijven doorgeteld blockedByCount, en de grafiek stopt bij 400 kaarten. Zie de grafiek API voor reisdetails.
Bordscheiders
Separators zijn items die eigendom zijn van het bord en die de bestelstrook van een lijst met kaarten delen. Hun getypte ankers kunnen zich op elk soort item richten, dus een integratie kan een exacte sectie creëren en er vervolgens kaarten omheen plaatsen zonder numerieke posities te berekenen.
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" });
Gebruik kanera.separators.move om een scheidingsteken te verplaatsen ten opzichte van een kaart, een ander scheidingsteken of een lijstrand. Als u een scheidingsteken verwijdert, blijft elke kaart op zijn plaats.
Herinneringen en kijken
Kijken en herinneringen zijn eigendom van de verbonden persoon, dus deze heeft een persoonlijke sleutel of een OAuth-verbinding nodig; werkruimtesleutels ontvangen 403. Een herinnering is privé, maximaal één per kaart, en maximaal acht weken vooruit.
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 vervangt elke bestaande herinnering op de kaart, inclusief een herinnering die al is geactiveerd. Zie Herinneringen.
Als u meer wilt weten over antwoorden en vermeldingen zonder dat u webhooks ontvangt, kunt u uw meldingeninbox raadplegen. Bewaar het geretourneerde cursor en geef het terug als 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
Maak meerdere kaarten
kanera.cards.bulk.create creëert in één verzoek maximaal 50 kaarten op één bord, elk met zijn eigen lijst, labels, toegewezen personen en vervaldatum. Elke kaart wordt gevalideerd voordat er een wordt aangemaakt, dus geef een idempotencyKey en de hele batch kan veilig opnieuw worden geprobeerd.
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 neemt hetzelfde labelIds, assigneeIds, dueDateLocalDate, en dueDateSlot voor een enkele kaart.
Controlelijsten
Maak een checklist met de volledige inhoud in één verzoek: items met beschrijvingen, toegewezen personen en vervaldatums, en één niveau met subchecklists onder items op het hoogste niveau. De server genereert en verbindt elke ID atomair en retourneert deze, zodat u achteraf niets meer hoeft op te zoeken.
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");
Er zijn alleen items op de subchecklist nodig text en completed, en items verplaatsen zich alleen binnen hun eigen groep: tussen checklists op het hoogste niveau, of tussen de subchecklists van één bovenliggend item. Een batch wordt volledig gevalideerd voordat er iets wordt geschreven; een afgewezen verzoek error.options.body.issues benoemt het exacte veld, zoals items[0].subChecklists[0].items[1].texten onbekende velden worden afgewezen in plaats van genegeerd.
Agent vlucht
Wanneer uw integratie een agent is die aan een kaart werkt, vertel dan aan Kanera dat deze is gestart. De kaarttegel toont vervolgens een live-chip met de naam van de agent en de taak, de kaartdetails tonen de run tijdens de vlucht en de uiteindelijke uitkomst wordt verplaatst naar de activiteitenfeed. Niemand hoeft te raden of een klus nog doorgaat.
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 neemt succeeded, failed, of cancelled. Een terminalstatus beëindigt de run en maakt deze onveranderlijk, dus later update daarop mislukt met a CONFLICT fout - controleer error.status === 409
als een nieuwe poging na de jouwe zou kunnen landen finish.
Een live run zonder hartslag gedurende vijftien minuten wordt gemarkeerd stalled door Kanera, dus een gecrashte agent kan een kaart waarop 'werkend' staat niet voor altijd achterlaten. Een vastgelopen run wordt niet beëindigd: de agent kan nog steeds terugkomen en deze afsluiten of instellen status: "running" hervatten.
Gebruik kanera.runs.list(card) voordat u begint, zodat twee agenten niet dezelfde kaart gebruiken, { includeEnded: true }
voor de volledige geschiedenis, en kanera.runs.get(runId) om één run opnieuw te lezen.
Registreer webhooks
Een beheerder beheert elk eindpunt in een werkruimte. Een inloggegevens of een interactieve OAuth-agent die geen beheerder is en die geen beheerder is, kan ook eindpunten registreren, maar ziet alleen de eindpunten die door zijn eigen verbinding zijn gemaakt. Dit is handig wanneer een agent gebeurteniscallbacks nodig heeft zonder machtiging voor de werkruimtebeheerder.
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);
Verbindingsgerichte eindpunten retourneren scope: "connection"; door de beheerder gemaakte eindpunten retourneren
scope: "workspace". De bron biedt ook list, update, rotateSecret, retryDelivery, en delete. Zie Webhaken voor payloadverificatie en leveringssemantiek.
Paginering
Elk lijsteindpunt is begrensd; er is geen oproep "geef mij het hele bord". De veel voorkomende integratiebug is het lezen van pagina één en deze als de hele set behandelen, dus iteratie is de standaardvorm:
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);
Fouten
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 draagt de HTTP status, Kanera's stabiele fout code, en bewakers voor de zaken die de moeite waard zijn om verder te gaan: isUnauthenticated, isForbidden, isNotFound, isRateLimited, isRetryable. Het aantal transportstoringen neemt toe KaneraConnectionError.
Nieuwe pogingen en idempotentie
Leest en DELETE/PUT worden automatisch opnieuw geprobeerd op basis van snelheidslimieten en tijdelijke stroomopwaartse fouten, met exponentiële uitstel, volledige jitter en Retry-After vereerd wanneer Kanera het verzendt.
Mutaties worden niet opnieuw geprobeerd, tenzij u een idempotentiesleutel opgeeft, omdat een nieuwe poging na een dubbelzinnige fout een tweede kaart zou creëren of een tweede opmerking zou plaatsen. Geef er één op en de API speelt de eerste uitkomst opnieuw af in plaats van het schrijven te herhalen:
await kanera.comments.create("MKT-42", { body: "Deployed." }, {
idempotencyKey: crypto.randomUUID(),
});
Controleer webhooks
parseWebhook verifieert de handtekening en retourneert de geparseerde gebeurtenis of gooit. Het gooit in plaats van een booleaanse waarde terug te geven, zodat een handler niet kan vergeten dit te controleren voordat hij de payload gebruikt:
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);
});
De handtekening omvat {timestamp}.{body}, en de tijdstempel wordt gecontroleerd aan de hand van een venster van vijf minuten (configureerbaar) om herhaling te beperken. Het maakt gebruik van Web Crypto, dus het werkt zowel in worker- en edge-runtimes als in Node. Zie Webhaken voor het instellen van eindpunten en de evenementencatalogus.
Opties
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
});
Ontsnappingsluik
De API beweegt sneller dan het getypte oppervlak en er zit niets achter een wrapper:
const result = await kanera.http.post<MyType>("/api/v1/some/new/endpoint", body);
Een opmerking over browsers
Het pakket draait in een browser, maar als je het daar gebruikt, wordt er een API-sleutel naar de klant verzonden. Doe dat alleen in een vertrouwde first-party context. Voor alles wat met de gebruiker te maken heeft, belt u Kanera vanaf uw server.