Skip to main content

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

  1. POST /boards — creates the board and its default To Do, Doing, and Done columns in one transaction. Requires an Idempotency-Key; returns 201, a Location header, and the board's first ETag.
  2. GET /boards/{board_id}/columns — lists the columns in board order so you can pick a target column_id.
  3. POST /columns/{column_id}/cards — creates the card at the start of that column. Also Idempotency-Key; only title is 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 201 with Location: /api/kanban/v1/boards/<id> and ETag: "1".
  • The column list returns three active columns ordered by position — the default To Do, Doing, and Done.
  • The card create returns 201 with 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