Skip to content

Kanban Boards

Full-featured kanban boards with stages, cards, drag-and-drop reordering, and scoped views.

Board Endpoints

MethodPathDescriptionAuthRate Limit
GET/kanban/boardsList boardsJWT60/min
GET/kanban/boards/globalGlobal kanban viewJWT30/min
GET/kanban/boards/chainy/:idChainy-scoped kanban viewJWT30/min
GET/kanban/boards/:idGet a boardJWT60/min
GET/kanban/boards/:id/viewGet board with cardsJWT30/min
POST/kanban/boardsCreate a boardJWT + Entitlement10/min
PATCH/kanban/boards/:idUpdate a boardJWT30/min
POST/kanban/boards/:id/archiveArchive a boardJWT30/min
DELETE/kanban/boards/:idDelete a boardJWT30/min

Stage Endpoints

MethodPathDescriptionAuthRate Limit
GET/kanban/boards/:boardId/stagesList stagesJWT60/min
GET/kanban/boards/:boardId/stages/:stageId/cardsList cards in stageJWT60/min
POST/kanban/boards/:boardId/stagesCreate a stageJWT30/min
POST/kanban/boards/:boardId/stages/reorderReorder stagesJWT30/min
PATCH/kanban/stages/:idUpdate a stageJWT30/min
DELETE/kanban/stages/:idDelete a stageJWT30/min

Card Endpoints

MethodPathDescriptionAuthRate Limit
GET/kanban/boards/:boardId/cardsList cards (supports ?stageId)JWT60/min
POST/kanban/boards/:boardId/cardsCreate a cardJWT30/min
PATCH/kanban/cards/:idUpdate a cardJWT30/min
DELETE/kanban/cards/:idDelete a cardJWT30/min
POST/kanban/cards/moveMove card to stage/positionJWT60/min
POST/kanban/stages/:stageId/cards/reorderReorder cards within a stageJWT60/min

List Kanban Boards

Request

Query Parameters

ParameterTypeRequiredDescription
scopestringYesBoard scope. One of global or chainy.
chainyIdstringConditionalRequired when scope=chainy. The Chainy to list boards for.
archivedbooleanNoInclude 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

json
{
  "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

bash
# 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"
javascript
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();
python
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

FieldTypeRequiredDescription
titlestringYesBoard title
descriptionstringNoBoard description
chainyIdstringNoScope board to a chainy

Response

json
{
  "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

bash
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'"
  }'
javascript
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();
python
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

json
{
  "data": {
    "id": "cm5stage02",
    "boardId": "cm5board01",
    "title": "In Progress",
    "color": "#F39C12",
    "position": 1,
    "createdAt": "2026-03-17T10:05:00.000Z"
  }
}

Code Examples

bash
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
  }'
javascript
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();
python
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

json
{
  "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

bash
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
  }'
javascript
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();
python
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

json
{
  "data": {
    "id": "cm5card01",
    "stageId": "cm5stage02",
    "position": 0,
    "updatedAt": "2026-03-17T12:00:00.000Z"
  }
}

Code Examples

bash
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
  }'
javascript
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();
python
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

json
{
  "data": { "reordered": true }
}

Code Examples

bash
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'"]
  }'
javascript
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();
python
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

json
{
  "data": { "reordered": true }
}

Code Examples

bash
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'"]
  }'
javascript
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();
python
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

json
{
  "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

bash
curl https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/view \
  -H "Authorization: Bearer $TOKEN"
javascript
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();
python
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

Request

Use the id from Create Board's response as $BOARD_ID and the id from Create Stage's response as $STAGE_ID.

Response

json
{
  "data": [
    { "id": "cm5card05", "title": "Practice reading comprehension", "position": 0 }
  ],
  "meta": {
    "cursor": "eyJpZCI6IjIwIn0",
    "hasMore": false,
    "total": 5
  }
}

Code Examples

bash
curl "https://api.chainabit.com/api/v1/kanban/boards/$BOARD_ID/stages/$STAGE_ID/cards?cursor=eyJpZCI6IjEwIn0&limit=20" \
  -H "Authorization: Bearer $TOKEN"
javascript
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();
python
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"]

Built with purpose.