Skip to main content

Project Boards

Every project has one Kanban board, created with the project: columns in order, cards in each column, and a set of labels. Cards are never deleted, only archived; archived cards keep their content and can be restored.

All endpoints require authentication via X-API-Key header and the appropriate scope: projects:read to read a board, projects:write to change it. You need access to the project.


Reading the board​

GET /api/v1/projects/:id/board​

The board: its columns in order, the cards in each column, and its labels. The card, column and label ids returned here are the ones the other endpoints take.

Scope: projects:read

{
"data": {
"boardId": "brd_abc",
"projectId": "proj_abc123",
"labels": [{ "labelId": "lbl_1", "name": "bug", "color": "#ff3366" }],
"columns": [
{
"columnId": "col_backlog",
"name": "Backlog",
"cards": [
{
"cardId": "card_1",
"title": "Retrain the churn model",
"description": "Use the October data.",
"labelIds": ["lbl_1"],
"assigneeIds": ["user_456"],
"dueDate": "2026-10-20T00:00:00Z"
}
]
}
]
}
}

Creating a card returns its id: { "data": { "cardId": "card_2" } }.

GET /api/v1/projects/:id/board/archived-cards​

The board's archived cards, newest first, each with who archived it and the column it came from. Paged.

Scope: projects:read

Query parameterTypeDescription
qstringText in the card's title, description, or the name of the column it was archived from
skipnumberCards to skip, for paging (default 0)
limitnumberPage size, up to 100 (default 25)

GET /api/v1/projects/:id/board/members​

The people on the board, with their user ids and names. Only entries with assignable: true (the project's owner and collaborators) can be assigned to a card.

Scope: projects:read


Columns​

A new board starts with Ice Box, Backlog, Work In Progress, Ready For Review and Complete.

POST /api/v1/projects/:id/board/columns​

Add a column at the end of the board.

Scope: projects:write

FieldTypeRequiredDescription
namestringYesColumn name, up to 60 characters

Response 201 Created: the new column, { columnId, name, position }, last on the board.

POST /api/v1/projects/:id/board/columns/reorder​

Set the left-to-right order of the columns. columnIds must list every column on the board, in the order you want; a partial list is refused.

Scope: projects:write

FieldTypeRequiredDescription
columnIdsstring[]YesEvery column id on the board, in order

Response 200 OK: the columns in their new order, each { columnId, name, position }.

PATCH /api/v1/projects/:projectId/board/columns/:id​

Rename a column. Its cards stay where they are.

Scope: projects:write

FieldTypeRequiredDescription
namestringYesNew column name

Response 200 OK: the column, { columnId, name, position }.

DELETE /api/v1/projects/:projectId/board/columns/:id​

Remove a column. Any cards still in it are archived, not deleted: they keep their content, appear in the archive labelled with the column they came from, and can be restored into any remaining column. The response says how many were archived.

Scope: projects:write


Labels​

Response 200 OK: { archivedCount }, how many of its cards were archived.

POST /api/v1/projects/:id/board/labels​

Create a label, or update one by passing its labelId.

Scope: projects:write

FieldTypeRequiredDescription
namestringYesLabel name, up to 40 characters
colorstringYesOne of the board palette: #6571ff (blue), #05a34a (green), #fbbc06 (yellow), #ff3366 (red), #0dcaf0 (teal), #7987a1 (gray). Any other value is refused.
labelIdstringNoAn existing label to update; omit to create one

Response 201 Created: the label, { labelId, name, color } (the same labelId when it updated one).

DELETE /api/v1/projects/:projectId/board/labels/:id​

Delete a label. It is also removed from every card that carries it, archived cards included.

Scope: projects:write

Response 204 No Content


Cards​

POST /api/v1/projects/:id/board/cards​

Add a card at the end of a column.

Scope: projects:write

FieldTypeRequiredDescription
columnIdstringYesThe column to add the card to
titlestringYesCard title, up to 200 characters
descriptionstringNoMarkdown description

Response 201 Created: the new card, as the board shows it (cardId, title, description, labelIds, assigneeIds, dueDate, dueComplete, position, createdBy, createdAt, updatedAt).

PATCH /api/v1/projects/-/board/cards/:id​

Update a card. Send only the fields you are changing: anything you leave out is kept, so two people changing different fields do not overwrite each other.

Scope: projects:write

FieldTypeDescription
titlestringNew title
descriptionstringNew markdown description
labelIdsstring[]The card's complete set of labels (each must exist on the board)
assigneeIdsstring[]The card's complete set of assignees (assignable board members)
dueDatestring | nullISO date, or null to clear it
dueCompletebooleanWhether the due date is met

Response 200 OK: the updated card, as the board shows it.

POST /api/v1/projects/-/board/cards/:id/move​

Move a card to a column. With only toColumnId it goes to the end of that column. To place it exactly, also pass the card that should end up directly above it (prevCardId) and/or below it (nextCardId), both already in the target column.

Scope: projects:write

FieldTypeRequiredDescription
toColumnIdstringYesDestination column
prevCardIdstringNoThe card that should end up directly above this one
nextCardIdstringNoThe card that should end up directly below this one

Response 200 OK: the card, as the board shows it, in its new column and position.

POST /api/v1/projects/-/board/cards/:id/archive​

Archive a card. This is how a card leaves the board; there is no delete. It keeps its content and the column it came from, and can be restored.

Scope: projects:write

Response 200 OK: the card, archived.

POST /api/v1/projects/-/board/cards/:id/restore​

Bring an archived card back, at the end of the column you name. The column is always explicit because the one it came from may have been removed since.

Scope: projects:write

FieldTypeRequiredDescription
toColumnIdstringYesThe column to restore the card into

Example​

# Add a card to the Backlog and assign it
BOARD=$(curl -s -H "X-API-Key: $KEY" "$BASE/api/v1/projects/$PROJECT/board")
BACKLOG=$(echo "$BOARD" | jq -r '.data.columns[] | select(.name=="Backlog") | .columnId')
CARD=$(curl -s -X POST -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d "{\"columnId\":\"$BACKLOG\",\"title\":\"Retrain the churn model\"}" \
"$BASE/api/v1/projects/$PROJECT/board/cards" | jq -r '.data.cardId')
curl -s -X PATCH -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"assigneeIds":["<user id from /board/members>"]}' "$BASE/api/v1/projects/-/board/cards/$CARD"

Response 200 OK: the card, as the board shows it, at the end of the column.