Skip to main content

Columns and ordering

Columns are the vertical lanes of a board. Every card lives in exactly one column, and both columns and cards carry a server-computed position that defines their canonical order.

The column representation is documented in the API reference: id, type, version, board_id, name, position, archived_at, and created_at.

Position is read-only

position is a number the server maintains; you never write it. To reorder, call the move endpoint with identifiers, not numbers:

The server computes the resulting position atomically, so two concurrent moves can never interleave into a corrupted order.

Listing order

GET /boards/{board_id}/columns returns active columns by default, ordered by (position ASC, id ASC) — exactly the left-to-right order users see on the board. With archived=true, archived columns are returned ordered by most recently archived first.

Creating, renaming, moving, archiving

Example — create a column and move it to the front of the board:

cURL:

BOARD_ID="<board-id>"
FIRST_COLUMN_ID="<current-first-column-id>"

curl -sS -i -X POST "$KANBAN_API_BASE_URL/boards/$BOARD_ID/columns" \
-H "Authorization: Bearer $KANBAN_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: docs-create-column-001" \
-d '{"name": "Triage"}'

NEW_COLUMN_ID="<id-from-the-create-response>"

curl -sS -i -X POST "$KANBAN_API_BASE_URL/columns/$NEW_COLUMN_ID/move" \
-H "Authorization: Bearer $KANBAN_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: docs-move-column-001" \
-H 'If-Match: "1"' \
-d "{\"before_column_id\": \"$FIRST_COLUMN_ID\"}"

JavaScript:

const baseUrl =
process.env.KANBAN_API_BASE_URL ?? "https://app.filetag.ai/api/kanban/v1";
const token = process.env.KANBAN_API_TOKEN;
const boardId = process.env.KANBAN_BOARD_ID;

async function main() {
const listResponse = await fetch(`${baseUrl}/boards/${boardId}/columns`, {
headers: { Authorization: `Bearer ${token}` },
});
const columns = (await listResponse.json()).data;

const createResponse = await fetch(`${baseUrl}/boards/${boardId}/columns`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"Idempotency-Key": "docs-create-column-001",
},
body: JSON.stringify({ name: "Triage" }),
});
const created = (await createResponse.json()).data;
const etag = createResponse.headers.get("etag");

const moveResponse = await fetch(`${baseUrl}/columns/${created.id}/move`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"Idempotency-Key": "docs-move-column-001",
"If-Match": etag,
},
body: JSON.stringify({ before_column_id: columns[0].id }),
});
const moved = (await moveResponse.json()).data;
console.log("column", moved.id, "now at position", moved.position);
}

main().catch((error) => {
console.error(error);
process.exit(1);
});

Python:

import json
import os
import urllib.request

BASE_URL = os.environ.get(
"KANBAN_API_BASE_URL", "https://app.filetag.ai/api/kanban/v1"
)
TOKEN = os.environ["KANBAN_API_TOKEN"]
BOARD_ID = os.environ["KANBAN_BOARD_ID"]


def api(path, method="GET", body=None, headers=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
request = urllib.request.Request(
f"{BASE_URL}{path}",
data=data,
method=method,
headers={
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
**(headers or {}),
},
)
with urllib.request.urlopen(request) as response:
return response.headers, json.load(response)


_, columns = api(f"/boards/{BOARD_ID}/columns")
first_column_id = columns["data"][0]["id"]

created_headers, created = api(
f"/boards/{BOARD_ID}/columns",
method="POST",
body={"name": "Triage"},
headers={"Idempotency-Key": "docs-create-column-001"},
)

_, moved = api(
f"/columns/{created['data']['id']}/move",
method="POST",
body={"before_column_id": first_column_id},
headers={
"Idempotency-Key": "docs-move-column-001",
"If-Match": created_headers.get("ETag"),
},
)
print("column", moved["data"]["id"], "now at position", moved["data"]["position"])

Archiving a column does not archive its cards

POST /columns/{column_id}/archive sets the column's archived_at and leaves every card in it untouched. Cards keep their own independent archive state: they remain queryable through GET /boards/{board_id}/cards and can be moved out of the archived column by ID. If you want the cards archived too, archive them individually.

There is no column restore or delete endpoint in v1; list archived columns with archived=true to inspect them.