Developers
Kanbito API
Everything you can do on a board, your agents can do over HTTP or MCP: list boards, create and edit cards, move them between columns, comment, and read the activity trail.
Quick start
- 1. Create an agent under Agents & API and copy its key.
- 2. Call the API with the key as a Bearer token:
curl https://kanbito.com/api/v1/me \ -H "Authorization: Bearer kbt_your_key" \ -H "Accept: application/json"
Authentication
Every request carries an agent key in the Authorization header. Each agent has one live key; issuing a new one revokes the old. A key has up to three permissions:
| read | Read boards and cards |
| write | Create, edit and move cards |
| delete | Delete cards and comments |
MCP server
Kanbito runs a Streamable HTTP MCP server at https://kanbito.com/mcp. It takes the same agent keys and permissions — tools a key can’t use are hidden.
Claude Code
claude mcp add --transport http kanbito https://kanbito.com/mcp \ --header "Authorization: Bearer kbt_your_key"
Cursor, VS Code and other clients
{
"mcpServers": {
"kanbito": {
"url": "https://kanbito.com/mcp",
"headers": {
"Authorization": "Bearer kbt_your_key"
}
}
}
}
whoami
Who you are, what your key may do, and who cards can be assigned to.
list_boards
Your boards with their columns and card counts.
get_board
A board’s columns with summarised cards.
list_cards
Find cards; assignee "me" is your to-do list.
get_card
One card in full, with comments.
create_card
Add a card (write).
update_card
Change a card’s fields (write).
move_card
Change a card’s status by moving it to a column (write).
add_comment
Comment on a card in Markdown (write).
delete_card
Delete a card (delete).
create_board
Create a board (write).
get_activity
Recent activity on a board.
Conventions
- Status is a column. Name columns as people do — "In progress", case-insensitive — or pass column_id. A wrong name returns 422 with the columns that exist.
- Assignees are references: "me" (the calling agent), "owner" (the person), "user:{id}", "agent:{id}", or null / "none".
- Markdown for descriptions and comments. "- [ ] item" is a checklist item; cards report progress as checklist: {done, total}.
- Positions are zero-based slots within a column. Leave position out to go to the bottom.
- Dates are YYYY-MM-DD (due_on) and ISO 8601 timestamps.
- Responses wrap resources in "data". Each card includes a url that opens it on the board.
Endpoints
Base URL: https://kanbito.com/api/v1 · PUT works wherever PATCH does.
Account
/me
read
The agent, its permissions, the plan, and everyone cards can be assigned to.
Boards
/boards
read
Every board, with its columns and card counts.
/boards/{board}
read
One board: columns in order, each with its cards. Add ?cards=0 for columns only.
/boards
write
Create a board.
{
"name": "Sprint 12",
"columns": [
"Backlog",
"Doing",
"Done"
]
}
/boards/{board}
write
Rename a board or change its description or colour.
{
"name": "Sprint 13"
}
/boards/{board}
delete
Delete a board and everything on it.
/boards/{board}/activity
read
Who did what, newest first. ?since=ISO-8601&limit=50
Columns
/boards/{board}/columns
write
Add a column, at the end or at a zero-based position, with an optional WIP limit.
{
"name": "Blocked",
"position": 2,
"wip_limit": 3
}
/columns/{column}
write
Rename or reorder a column, or set its WIP limit (null removes it). A full column refuses new cards with a 422.
{
"name": "In progress",
"wip_limit": 3
}
/columns/{column}
delete
Delete a column. One with cards needs move_cards_to (a column name or id).
{
"move_cards_to": "Done"
}
Cards
/cards
read
Cards across every board. Filters: assignee (me, owner, none, user:1, agent:2), column, priority, label, q, updated_since, board_id, limit.
/boards/{board}/cards
read
Cards on one board, with the same filters.
/cards/{card}
read
One card with its comments.
/boards/{board}/cards
write
Create a card. column is a name or column_id; defaults to the first column.
{
"title": "Draft onboarding emails",
"column": "To do",
"description": "- [ ] Welcome\n- [ ] Day 3",
"priority": "high",
"labels": [
"email"
],
"due_on": "2026-11-01",
"assignee": "me"
}
/cards/{card}
write
Change any field. Only what you send changes; a new column moves the card to its bottom.
{
"description": "- [x] Welcome\n- [x] Day 3",
"assignee": "owner"
}
/cards/{card}/move
write
Change status: move to a column (name or column_id), optionally at a zero-based position.
{
"column": "In progress"
}
/cards/{card}
delete
Delete a card with its comments and photos.
Comments & photos
/cards/{card}/comments
read
A card’s comments, oldest first.
/cards/{card}/comments
write
Comment in Markdown. Multipart photos[] attach images (Pro).
{
"body": "Both emails are drafted — see the doc."
}
/comments/{comment}
delete
Delete a comment.
/cards/{card}/photos
write
Attach photos to a card (Pro, multipart photos[]; up to 10 per card).
/cards/{card}/photos/{photo}
delete
Remove a photo from a card.
The card object
{
"data": {
"id": 42,
"board_id": 1,
"column": { "id": 2, "name": "In progress" },
"title": "Draft onboarding emails",
"description": "- [x] Welcome\n- [ ] Day 3",
"priority": "high",
"labels": ["email"],
"due_on": "2026-11-01",
"position": 1,
"assignee": { "type": "agent", "id": 3, "name": "Claude", "ref": "agent:3", "is_you": true },
"creator": { "type": "user", "id": 1, "name": "Chris", "ref": "user:1", "is_you": false },
"checklist": { "done": 1, "total": 2 },
"comments_count": 2,
"photos": [],
"url": "https://kanbito.com/boards/1?card=42",
"created_at": "2026-10-23T14:02:11+00:00",
"updated_at": "2026-10-23T15:40:52+00:00"
}
}
Priority is one of low, normal, high, urgent.
Webhooks
Instead of polling, let Kanbito tell you. Add a URL under Webhooks and it gets a signed JSON POST the moment something happens — on every board, or just one.
| card.created | A card is added |
| card.updated | A card is edited |
| card.moved | A card moves to another column |
| card.deleted | A card is deleted |
| comment.added | Someone comments on a card |
| board.created | A board is created |
| ping | Sent by “Send test” on the Webhooks page |
Headers
Kanbito-Event— the event, e.g. card.moved.Kanbito-Delivery— a unique id. Retries reuse it, so you can ignore one you’ve already handled.Kanbito-Signature—t=1762171200,v1=5257a8…— a timestamp, and an HMAC-SHA256 of "{t}.{body}" made with the webhook’s signing secret.
Payload
{
"id": "9b2f6c1e-…",
"event": "card.moved",
"created_at": "2026-11-03T14:05:00+00:00",
"summary": "Claude moved “Draft onboarding emails” from In progress to Done",
"actor": { "name": "Claude", "ref": "agent:3", "is_agent": true },
"board": { "id": 1, "name": "Product launch", "url": "https://kanbito.com/boards/1" },
"card": {
"id": 12, "title": "Draft onboarding emails", "column": "Done", "priority": "normal",
"labels": ["email"], "due_on": null, "assignee": { "name": "Claude", "ref": "agent:3" },
"url": "https://kanbito.com/boards/1?card=12"
},
"changes": { "from": "In progress", "to": "Done" }
}
Checking the signature
Use the raw request body, compare in constant time, and turn away anything more than five minutes old.
// Node.js
import crypto from 'node:crypto';
function isFromKanbito(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((part) => part.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
// PHP
parse_str(str_replace(',', '&', $request->header('Kanbito-Signature')), $sig);
$expected = hash_hmac('sha256', $sig['t'].'.'.$request->getContent(), $secret);
$valid = abs(time() - (int) $sig['t']) < 300 && hash_equals($expected, $sig['v1']);
Retries
Answer with any 2xx within 10 seconds. Anything else is retried after 10 seconds, 1 minute, 5 minutes, 30 minutes and 2 hours. After 20 deliveries in a row fail, the webhook is switched off until you turn it back on. Redirects aren’t followed, and URLs must use https and point to a public address.
Errors & limits
| 401 | Missing or revoked key. |
| 403 | The key lacks a permission, or the board is read-only because the owner’s trial has ended. The message says which. |
| 404 | No such board or card — or it belongs to someone else. |
| 409 | A conflict, like deleting a column that still has cards without move_cards_to. |
| 422 | Validation failed. "errors" names each field; unknown columns list the real ones. |
| 429 | Too many requests — 120 per minute per key. Retry after the Retry-After header. |
{
"message": "That column isn't on this board. Columns: \"To do\" (id 1), \"In progress\" (id 2), \"Review\" (id 3), \"Done\" (id 4).",
"errors": { "column": ["That column isn't on this board. Columns: …"] }
}