Kanban Boards
Full-featured kanban boards with stages, cards, drag-and-drop reordering, and scoped views.
Board Endpoints
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| GET | /kanban/boards | List boards | JWT | 60/min |
| GET | /kanban/boards/global | Global kanban view | JWT | 30/min |
| GET | /kanban/boards/chainy/:id | Chainy-scoped kanban view | JWT | 30/min |
| GET | /kanban/boards/:id | Get a board | JWT | 60/min |
| GET | /kanban/boards/:id/view | Get board with cards | JWT | 30/min |
| POST | /kanban/boards | Create a board | JWT + Entitlement | 10/min |
| PATCH | /kanban/boards/:id | Update a board | JWT | 30/min |
| POST | /kanban/boards/:id/archive | Archive a board | JWT | 30/min |
| DELETE | /kanban/boards/:id | Delete a board | JWT | 30/min |
Stage Endpoints
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| GET | /kanban/boards/:boardId/stages | List stages | JWT | 60/min |
| GET | /kanban/boards/:boardId/stages/:stageId/cards | List cards in stage (cursor-paginated) | JWT | 60/min |
| POST | /kanban/boards/:boardId/stages | Create a stage | JWT | 30/min |
| POST | /kanban/boards/:boardId/stages/reorder | Reorder stages | JWT | 30/min |
| PATCH | /kanban/stages/:id | Update a stage | JWT | 30/min |
| DELETE | /kanban/stages/:id | Delete a stage | JWT | 30/min |
Card Endpoints
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| GET | /kanban/boards/:boardId/cards | List cards (supports ?stageId) | JWT | 60/min |
| POST | /kanban/boards/:boardId/cards | Create a card | JWT | 30/min |
| PATCH | /kanban/cards/:id | Update a card | JWT | 30/min |
| DELETE | /kanban/cards/:id | Delete a card | JWT | 30/min |
| POST | /kanban/cards/move | Move card to stage/position | JWT | 60/min |
| POST | /kanban/stages/:stageId/cards/reorder | Reorder cards within a stage | JWT | 60/min |
List Kanban Boards
Request
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
scope | string | Yes | Board scope. One of global or chainy. |
chainyId | string (uuid) | Conditional | Required when scope=chainy. The Chainy to list boards for. |
archived | boolean | No | Include archived boards. Defaults to false. |
scope is mandatory — omitting it returns 400 bad_request (scope must be one of the following values: global, chainy).
Use the id from Create Chainy's response (data.id) as $CHAINY_ID when scoping to a chainy.
Response
{
"data": [
{
"id": "cm5board01",
"title": "Language Learning Board",
"chainyId": "cm5abc123",
"stageCount": 4,
"cardCount": 12,
"archived": false,
"createdAt": "2026-03-17T10:00:00.000Z"
}
],
"meta": { "total": 1 }
}Code Examples
# Global boards
curl "https://api.chainabit.com/api/v1/kanban/boards?scope=global" \
-H "Authorization: Bearer $TOKEN"
# Chainy-scoped boards
curl "https://api.chainabit.com/api/v1/kanban/boards?scope=chainy&chainyId=$CHAINY_ID" \
-H "Authorization: Bearer $TOKEN"const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const response = await fetch(`${BASE_URL}/kanban/boards?scope=global`, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await response.json();import requests, os
response = requests.get(
f"{os.environ['BASE_URL']}/kanban/boards",
params={"scope": "global"},
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
)
data = response.json()["data"]Create Board
Request
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Board title |
description | string | No | Board description |
chainyId | string | No | Scope board to a chainy |
Response
{
"data": {
"id": "cm5board01",
"title": "Language Learning Board",
"description": "Track Spanish learning progress",
"chainyId": "cm5abc123",
"archived": false,
"createdAt": "2026-03-17T10:00:00.000Z"
}
}Code Examples
curl -X POST https://api.chainabit.com/api/v1/kanban/boards \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Language Learning Board",
"description": "Track Spanish learning progress",
"chainyId": "'$CHAINY_ID'"
}'const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const chainyId = process.env.CHAINY_ID; // optional — from Create Chainy's response
const response = await fetch(`${BASE_URL}/kanban/boards`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "Language Learning Board",
description: "Track Spanish learning progress",
chainyId,
}),
});
const { data } = await response.json();import requests, os
chainy_id = os.environ["CHAINY_ID"] # optional — from Create Chainy's response
response = requests.post(
f"{os.environ['BASE_URL']}/kanban/boards",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={
"title": "Language Learning Board",
"description": "Track Spanish learning progress",
"chainyId": chainy_id,
},
)
data = response.json()["data"]Create Stage
Request
Use the id from Create Board's response (data.id) as $BOARD_ID.
Response
{
"data": {
"id": "cm5stage02",
"boardId": "cm5board01",
"title": "In Progress",
"color": "#F39C12",
"position": 1,
"createdAt": "2026-03-17T10:05:00.000Z"
}
}Code Examples
curl -X POST https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/stages \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "In Progress",
"color": "#F39C12",
"position": 1
}'const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const boardId = process.env.BOARD_ID; // from Create Board's response
const response = await fetch(`${BASE_URL}/kanban/boards/${boardId}/stages`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ title: "In Progress", color: "#F39C12", position: 1 }),
});
const { data } = await response.json();import requests, os
board_id = os.environ["BOARD_ID"] # from Create Board's response
response = requests.post(
f"{os.environ['BASE_URL']}/kanban/boards/{board_id}/stages",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={"title": "In Progress", "color": "#F39C12", "position": 1},
)
data = response.json()["data"]Create Card
Request
Use the id from Create Board's response as $BOARD_ID, the id from Create Stage's response as $STAGE_ID, and the id of one of your own Bits as $BIT_ID.
Response
{
"data": {
"id": "cm5card01",
"boardId": "cm5board01",
"stageId": "cm5stage01",
"title": "Master present tense conjugations",
"bitId": "cm5bit001",
"position": 0,
"createdAt": "2026-03-17T10:10:00.000Z"
}
}Code Examples
curl -X POST https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/cards \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Master present tense conjugations",
"stageId": "'$STAGE_ID'",
"bitId": "'$BIT_ID'",
"position": 0
}'const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const boardId = process.env.BOARD_ID; // from Create Board's response
const stageId = process.env.STAGE_ID; // from Create Stage's response
const bitId = process.env.BIT_ID; // your own Bit's id, from Create Bit's response
const response = await fetch(`${BASE_URL}/kanban/boards/${boardId}/cards`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "Master present tense conjugations",
stageId,
bitId,
position: 0,
}),
});
const { data } = await response.json();import requests, os
board_id = os.environ["BOARD_ID"] # from Create Board's response
stage_id = os.environ["STAGE_ID"] # from Create Stage's response
bit_id = os.environ["BIT_ID"] # your own Bit's id, from Create Bit's response
response = requests.post(
f"{os.environ['BASE_URL']}/kanban/boards/{board_id}/cards",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={
"title": "Master present tense conjugations",
"stageId": stage_id,
"bitId": bit_id,
"position": 0,
},
)
data = response.json()["data"]Move Card
Request
Use the id from Create Card's response as $CARD_ID, and the id of the destination stage (from Create Stage's response) as $TARGET_STAGE_ID.
Response
{
"data": {
"id": "cm5card01",
"stageId": "cm5stage02",
"position": 0,
"updatedAt": "2026-03-17T12:00:00.000Z"
}
}Code Examples
curl -X POST https://api.chainabit.com/api/v1/kanban/cards/move \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardId": "'$CARD_ID'",
"targetStageId": "'$TARGET_STAGE_ID'",
"position": 0
}'const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const cardId = process.env.CARD_ID; // from Create Card's response
const targetStageId = process.env.TARGET_STAGE_ID; // destination stage's id, from Create Stage's response
const response = await fetch(`${BASE_URL}/kanban/cards/move`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
cardId,
targetStageId,
position: 0,
}),
});
const { data } = await response.json();import requests, os
card_id = os.environ["CARD_ID"] # from Create Card's response
target_stage_id = os.environ["TARGET_STAGE_ID"] # destination stage's id, from Create Stage's response
response = requests.post(
f"{os.environ['BASE_URL']}/kanban/cards/move",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={"cardId": card_id, "targetStageId": target_stage_id, "position": 0},
)
data = response.json()["data"]Reorder Stages
Request
Use $BOARD_ID from Create Board as before. stageIds must list every stage id on the board (each from Create Stage's response) in the desired order — replace $STAGE_ID_1..$STAGE_ID_4 below with your own.
Response
{
"data": { "reordered": true }
}Code Examples
curl -X POST https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/stages/reorder \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"stageIds": ["'$STAGE_ID_1'", "'$STAGE_ID_2'", "'$STAGE_ID_3'", "'$STAGE_ID_4'"]
}'const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const boardId = process.env.BOARD_ID; // from Create Board's response
const stageIds = [
process.env.STAGE_ID_1,
process.env.STAGE_ID_2,
process.env.STAGE_ID_3,
process.env.STAGE_ID_4,
]; // every stage id on the board, in the new order
const response = await fetch(`${BASE_URL}/kanban/boards/${boardId}/stages/reorder`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ stageIds }),
});
const { data } = await response.json();import requests, os
board_id = os.environ["BOARD_ID"] # from Create Board's response
stage_ids = [
os.environ["STAGE_ID_1"],
os.environ["STAGE_ID_2"],
os.environ["STAGE_ID_3"],
os.environ["STAGE_ID_4"],
] # every stage id on the board, in the new order
response = requests.post(
f"{os.environ['BASE_URL']}/kanban/boards/{board_id}/stages/reorder",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={"stageIds": stage_ids},
)
data = response.json()["data"]Reorder Cards
Request
Use the id from Create Stage's response as $STAGE_ID. cardIds must list every card id in that stage (each from Create Card's response) in the desired order — replace $CARD_ID_1..$CARD_ID_3 below with your own.
Response
{
"data": { "reordered": true }
}Code Examples
curl -X POST https://api.chainabit.com/api/v1/kanban/stages/$STAGE_ID/cards/reorder \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardIds": ["'$CARD_ID_1'", "'$CARD_ID_2'", "'$CARD_ID_3'"]
}'const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const stageId = process.env.STAGE_ID; // from Create Stage's response
const cardIds = [
process.env.CARD_ID_1,
process.env.CARD_ID_2,
process.env.CARD_ID_3,
]; // every card id in the stage, in the new order
const response = await fetch(`${BASE_URL}/kanban/stages/${stageId}/cards/reorder`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ cardIds }),
});
const { data } = await response.json();import requests, os
stage_id = os.environ["STAGE_ID"] # from Create Stage's response
card_ids = [
os.environ["CARD_ID_1"],
os.environ["CARD_ID_2"],
os.environ["CARD_ID_3"],
] # every card id in the stage, in the new order
response = requests.post(
f"{os.environ['BASE_URL']}/kanban/stages/{stage_id}/cards/reorder",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={"cardIds": card_ids},
)
data = response.json()["data"]Board with Cards View
Request
Use the id from Create Board's response as $BOARD_ID.
Response
{
"data": {
"id": "cm5board01",
"title": "Language Learning Board",
"stages": [
{
"id": "cm5stage01",
"title": "Backlog",
"position": 0,
"cards": [
{ "id": "cm5card02", "title": "Learn subjunctive mood", "position": 0 }
]
},
{
"id": "cm5stage02",
"title": "In Progress",
"position": 1,
"cards": [
{ "id": "cm5card01", "title": "Master present tense conjugations", "position": 0 }
]
}
]
}
}Code Examples
curl https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/view \
-H "Authorization: Bearer $TOKEN"const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const boardId = process.env.BOARD_ID; // from Create Board's response
const response = await fetch(`${BASE_URL}/kanban/boards/${boardId}/view`, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await response.json();import requests, os
board_id = os.environ["BOARD_ID"] # from Create Board's response
response = requests.get(
f"{os.environ['BASE_URL']}/kanban/boards/{board_id}/view",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
)
data = response.json()["data"]Cards in Stage (Cursor Paginated)
Request
Use the id from Create Board's response as $BOARD_ID and the id from Create Stage's response as $STAGE_ID.
Response
{
"data": [
{ "id": "cm5card05", "title": "Practice reading comprehension", "position": 0 }
],
"meta": {
"cursor": "eyJpZCI6IjIwIn0",
"hasMore": false,
"total": 5
}
}Code Examples
curl "https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/stages/$STAGE_ID/cards?cursor=eyJpZCI6IjEwIn0&limit=20" \
-H "Authorization: Bearer $TOKEN"const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const boardId = process.env.BOARD_ID; // from Create Board's response
const stageId = process.env.STAGE_ID; // from Create Stage's response
const response = await fetch(
`${BASE_URL}/kanban/boards/${boardId}/stages/${stageId}/cards?cursor=eyJpZCI6IjEwIn0&limit=20`,
{ headers: { Authorization: `Bearer ${TOKEN}` } }
);
const { data, meta } = await response.json();import requests, os
board_id = os.environ["BOARD_ID"] # from Create Board's response
stage_id = os.environ["STAGE_ID"] # from Create Stage's response
response = requests.get(
f"{os.environ['BASE_URL']}/kanban/boards/{board_id}/stages/{stage_id}/cards",
params={"cursor": "eyJpZCI6IjEwIn0", "limit": 20},
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
)
result = response.json()
data, meta = result["data"], result["meta"]