Scopes
Every operation in the Kanban API requires one of three token scopes. Scopes
are chosen when the token is created and are cumulative: kanban:write
includes everything kanban:read allows, and kanban:admin includes
everything kanban:write allows.
| Scope | Capability |
|---|---|
kanban:read | List and read visible Kanban resources |
kanban:write | Includes read; create and mutate boards, columns, cards, and collaboration resources |
kanban:admin | Includes write; manage board membership and visibility and perform permanent deletion |
A request whose token lacks the required scope fails with
403 insufficient_scope before any resource is touched.
Scopes gate the token, not the user. A scope never grants more than the
represented user's own permissions in the web app — a kanban:admin token
held by a user who did not create a board still cannot change that board's
visibility. The application permission column below is checked in addition
to the scope.
Which scope does an operation need?
Every operation's required scope is listed in the API reference. As an orientation:
| Operation | Token scope | Application permission |
|---|---|---|
| Read account or the workspace member directory | kanban:read | active workspace membership |
| List or read a board and its columns, cards, labels, members, subtasks, comments, attachments | kanban:read | board access |
| Create a workspace board | kanban:write | active workspace membership |
| Edit board name, description, or cover color | kanban:write | board access |
| Change board visibility | kanban:admin | board creator |
| Add or remove explicit board members | kanban:admin | board creator |
| Permanently delete a board | kanban:admin | board access and either board creator or workspace owner/admin |
| Create, rename, move, or archive a column | kanban:write | board access |
| Create, edit, move, complete, archive, or restore a card | kanban:write | board access |
| Permanently delete a card | kanban:admin | board access |
| Create or delete a label | kanban:write | board access |
| Assign users, apply labels, manage subtasks | kanban:write | board access; target user/label must belong to the board scope |
| Create comments | kanban:write | board access |
| Edit or delete comments | kanban:write | board access and comment author |
| Attach/detach files, set/clear cover | kanban:write | board access and current access to the file |
kanban:admin is deliberately reserved for high-impact operations: board
lifecycle, membership, visibility, and permanent deletion. It is not
required for every HTTP DELETE — deleting your own comment, a label, or a
subtask is a normal kanban:write capability.
Choosing scopes for an integration
- A dashboard, reporting job, or read-only bot needs only
kanban:read. - Most integrations that create and move cards need
kanban:write. - Reserve
kanban:adminfor administrative tooling that manages board membership, visibility, or deletion — and issue it as a separate token so the day-to-day integration does not carry destructive capability.
You can always inspect a token's effective scopes at runtime with
GET /account:
cURL:
curl -sS "$KANBAN_API_BASE_URL/account" \
-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}/account`, {
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})`
);
}
console.log("scopes:", payload.data.scopes.join(", "));
}
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}/account",
headers={"Authorization": f"Bearer {TOKEN}"},
)
with urllib.request.urlopen(request) as response:
payload = json.load(response)
print("scopes:", ", ".join(payload["data"]["scopes"]))