Agentic Board
Drop a live agent Kanban board into your app. Agents pick up cards, work them live with a streaming execution view, and advance lanes automatically — no polling glue code required.
Install
npm install @cadraos/sdkFull example
A self-contained page that creates the adapter from environment variables and renders a live board. Cards show their agent execution stream automatically — keyed on card.executionId.
import { useMemo } from 'react';
import {
KanbanBoard,
createDefaultKanbanAdapter,
} from '@cadraos/sdk/kanban';
export default function AgentBoardPage() {
const adapter = useMemo(
() =>
createDefaultKanbanAdapter({
baseUrl: import.meta.env.VITE_CADRA_API_URL as string,
apiKey: import.meta.env.VITE_CADRA_API_KEY as string,
orgId: Number(import.meta.env.VITE_CADRA_ORG_ID),
}),
[],
);
return (
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
<KanbanBoard
adapter={adapter}
pollIntervalMs={10000}
enableCreate
enableEdit
enableAdvance
onCreateCard={async () => {
const title = window.prompt('Card title');
if (!title) return null;
return { title, type: 'generate_campaign_plan' };
}}
onRetryCard={(card) => {
adapter.transitionCard({ id: card.id, targetLane: 'handler' });
}}
onError={(message) => {
console.error('[KanbanBoard]', message);
}}
/>
</div>
);
}Environment variables
Prefer a short-lived token or tokenProvider in the browser over a raw apiKey. The adapter accepts all three — swap apiKey for tokenProvider: async () => (await fetch("/api/token")).json().then(r => r.token) when you have a server-side token endpoint.
# Vite
VITE_CADRA_API_URL=https://your-instance.cadraos.com
VITE_CADRA_ORG_ID=123
VITE_CADRA_API_KEY=sk-cad-xxxxxxxxxxxx
# Next.js (public)
NEXT_PUBLIC_CADRA_API_URL=https://your-instance.cadraos.com
NEXT_PUBLIC_CADRA_ORG_ID=123
# Keep the raw API key server-side only — use a /api/token route in the browserHost-owned data (AgentBoard)
For multi-tenant deployments where your backend owns the card data, use AgentBoard with a BoardConfigClient and a custom AgentBoardCardSource. The source interface gives you full control over every read and write — the board component stays purely presentational.
import { useMemo } from 'react';
import {
AgentBoard,
AgentBoardCardSource,
BoardConfigClient,
} from '@cadraos/sdk/kanban';
// Implement the card source against your own API
const myCardSource: AgentBoardCardSource = {
async listCards(boardConfigUuid, input) {
const res = await fetch(`/api/boards/${boardConfigUuid}/cards`, {
method: 'POST',
body: JSON.stringify(input ?? {}),
});
return res.json();
},
async getCard(cardId) {
const res = await fetch(`/api/cards/${cardId}`);
return res.json();
},
async createCard(boardConfigUuid, input) {
const res = await fetch(`/api/boards/${boardConfigUuid}/cards`, {
method: 'POST',
body: JSON.stringify(input),
});
return res.json();
},
async updateCard(cardId, patch) {
const res = await fetch(`/api/cards/${cardId}`, {
method: 'PATCH',
body: JSON.stringify(patch),
});
return res.json();
},
async transitionCard(input) {
const res = await fetch(`/api/cards/${input.id}/transition`, {
method: 'POST',
body: JSON.stringify(input),
});
return res.json();
},
// Optional: stream live execution output for a card
async createRunStream(executionId) {
return new EventSource(`/api/executions/${executionId}/stream`);
},
};
export default function HostOwnedBoardPage() {
const configClient = useMemo(
() =>
new BoardConfigClient({
http: {
baseUrl: import.meta.env.VITE_CADRA_API_URL as string,
apiKey: import.meta.env.VITE_CADRA_API_KEY as string,
},
}),
[],
);
return (
<AgentBoard
configClient={configClient}
boardConfigUuid={import.meta.env.VITE_BOARD_CONFIG_UUID as string}
cardSource={myCardSource}
/>
);
}Next steps
Read the full /docs/guides/kanban-boards guide for lane configuration, card type schemas, agent assignment, and permission rules. The complete prop reference is at /docs/api/kanban.