Create a board and first card
This guide takes the happy path from nothing to a populated board: create a
board, discover its default columns, and create a card. It assumes you have
a token with kanban:write — see Getting started
if you don't.
All examples use:
export KANBAN_API_BASE_URL="https://app.filetag.ai/api/kanban/v1"
export KANBAN_API_TOKEN="<your-personal-access-token>"
The flow
POST /boards— creates the board and its default To Do, Doing, and Done columns in one transaction. Requires anIdempotency-Key; returns201, aLocationheader, and the board's firstETag.GET /boards/{board_id}/columns— lists the columns in board order so you can pick a targetcolumn_id.POST /columns/{column_id}/cards— creates the card at the start of that column. AlsoIdempotency-Key; onlytitleis required.
cURL
# 1. Create the board.
curl -sS -i -X POST "$KANBAN_API_BASE_URL/boards" \
-H "Authorization: Bearer $KANBAN_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: guide-create-board-001" \
-d '{
"name": "Launch plan",
"description": "Tasks for the Q3 launch",
"cover_color": "#0a8357"
}'
# Note the board id in the response body (data.id).
BOARD_ID="<board-id-from-the-response>"
# 2. List the default columns (To Do, Doing, Done).
curl -sS "$KANBAN_API_BASE_URL/boards/$BOARD_ID/columns" \
-H "Authorization: Bearer $KANBAN_API_TOKEN"
COLUMN_ID="<the-to-do-column-id>"
# 3. Create the first card in that column.
curl -sS -i -X POST "$KANBAN_API_BASE_URL/columns/$COLUMN_ID/cards" \
-H "Authorization: Bearer $KANBAN_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: guide-create-card-001" \
-d '{
"title": "Draft the announcement",
"description": "One page, link the launch checklist"
}'
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 api(path, options = {}) {
const response = await fetch(`${baseUrl}${path}`, {
...options,
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
...options.headers,
},
});
const payload = response.status === 204 ? null : await response.json();
if (!response.ok) {
throw new Error(
`${payload.error.code}: ${payload.error.message} (request ${payload.error.request_id})`
);
}
return { response, payload };
}
async function main() {
// 1. Create the board (default columns come with it).
const board = await api("/boards", {
method: "POST",
headers: { "Idempotency-Key": "guide-create-board-001" },
body: JSON.stringify({
name: "Launch plan",
description: "Tasks for the Q3 launch",
cover_color: "#0a8357",
}),
});
const boardId = board.payload.data.id;
console.log("board:", boardId);
console.log("location:", board.response.headers.get("location"));
// 2. Discover the default columns.
const columns = await api(`/boards/${boardId}/columns`);
const todo = columns.payload.data[0];
console.log(
"columns:",
columns.payload.data.map((column) => column.name).join(", ")
);
// 3. Create the first card at the start of the first column.
const card = await api(`/columns/${todo.id}/cards`, {
method: "POST",
headers: { "Idempotency-Key": "guide-create-card-001" },
body: JSON.stringify({
title: "Draft the announcement",
description: "One page, link the launch checklist",
}),
});
console.log("card:", card.payload.data.id);
console.log("card version:", card.response.headers.get("etag"));
}
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"]
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:
payload = json.load(response) if response.status != 204 else None
return response.headers, payload
# 1. Create the board (default columns come with it).
board_headers, board = api(
"/boards",
method="POST",
body={
"name": "Launch plan",
"description": "Tasks for the Q3 launch",
"cover_color": "#0a8357",
},
headers={"Idempotency-Key": "guide-create-board-001"},
)
board_id = board["data"]["id"]
print("board:", board_id)
print("location:", board_headers.get("Location"))
# 2. Discover the default columns.
_, columns = api(f"/boards/{board_id}/columns")
todo = columns["data"][0]
print("columns:", ", ".join(column["name"] for column in columns["data"]))
# 3. Create the first card at the start of the first column.
card_headers, card = api(
f"/columns/{todo['id']}/cards",
method="POST",
body={
"title": "Draft the announcement",
"description": "One page, link the launch checklist",
},
headers={"Idempotency-Key": "guide-create-card-001"},
)
print("card:", card["data"]["id"])
print("card version:", card_headers.get("ETag"))
What you should see
- The board create returns
201withLocation: /api/kanban/v1/boards/<id>andETag: "1". - The column list returns three active columns ordered by position — the default To Do, Doing, and Done.
- The card create returns
201with the card at the start of the column,version: 1, and empty collaboration state (assignee_user_ids: [],subtask_total: 0, ...).
If a create times out, retry it with the same Idempotency-Key and
body — you get the original response replayed instead of a duplicate. See
Idempotency.
Next steps
- Move cards safely to move that card through the columns.
- Manage assignees, labels, and subtasks to enrich it.
- Boards and visibility if the board should be private.