Skip to main content

Boards and visibility

A board is the top-level Kanban resource: it belongs to exactly one workspace, owns its columns and cards, and defines who can see and change everything inside it.

The board representation is documented in the API reference. The fields that drive access are is_public, created_by, and version; archived_at is set when a board is archived.

Public and private boards

Board access follows the same rules as the FileTag web app:

  • Public boards (is_public: true, the default at creation) are visible and writable to every active member of the workspace.
  • Private boards (is_public: false) are visible only to the board creator and users with an explicit board membership.

Visibility is changed with PUT /boards/{board_id}/visibility, which requires the kanban:admin scope, the board-creator permission, and an If-Match header. Explicit members are managed with the board member endpoints (also creator-only for writes); the workspace member directory gives you the valid user_id values to add. The creator cannot be removed through the membership endpoint.

Inaccessible means 404, not 403

A board you cannot access — because it is private, belongs to another workspace, or simply does not exist — always returns 404 not_found. The API never answers 403 for a resource-access failure, so an integration can never probe whether a given board ID exists. Treat 404 as "not available to this token" and nothing more. (403 is reserved for scope and management-permission failures — see Authentication.)

Listing and reading boards

GET /boards returns the boards visible to the represented user, cursor-paginated, active boards by default. Pass archived=true to list archived boards instead. Board detail is GET /boards/{board_id}.

cURL:

curl -sS "$KANBAN_API_BASE_URL/boards?archived=false&limit=50" \
-H "Authorization: Bearer $KANBAN_API_TOKEN"

JavaScript:

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

async function main() {
const response = await fetch(`${baseUrl}/boards?archived=false&limit=50`, {
headers: { Authorization: `Bearer ${token}` },
});
const payload = await response.json();
if (!response.ok) {
throw new Error(
`${payload.error.code}: ${payload.error.message} (request ${payload.error.request_id})`
);
}
for (const board of payload.data) {
console.log(board.id, board.name, board.is_public ? "public" : "private");
}
}

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"]

request = urllib.request.Request(
f"{BASE_URL}/boards?archived=false&limit=50",
headers={"Authorization": f"Bearer {TOKEN}"},
)
with urllib.request.urlopen(request) as response:
payload = json.load(response)

for board in payload["data"]:
kind = "public" if board["is_public"] else "private"
print(board["id"], board["name"], kind)

Creating and changing boards

  • POST /boards creates the board and its default To Do, Doing, and Done columns in a single transaction. It requires kanban:write and an Idempotency-Key.
  • PATCH /boards/{board_id} updates name, description, or cover_color (at least one field), guarded by If-Match.
  • DELETE /boards/{board_id} permanently deletes the board and everything in it. It requires kanban:admin, is version-guarded with If-Match, and cannot be undone.

The board version moves with its content

The board's version (returned in the ETag header on reads) covers more than the board's own metadata: activity on the board's descendants also bumps it. A PATCH to board metadata can therefore fail with 412 precondition_failed right after unrelated card activity — re-read the board, take the fresh ETag, and retry. See Optimistic concurrency.