Collaboration resources
Assignees, labels, subtasks, comments, and attachments hang off a card. They come in two API styles, and knowing which is which tells you how to call them safely:
- Desired-state relationships (assignees, applied labels, attachments,
covers, dependencies, subtask linked files): a
PUTensures the relationship exists, aDELETEensures it is absent. Both are naturally retryable — repeating a call is a no-op — so they need neitherIdempotency-KeynorIf-Match. - Owned resources (labels, subtasks, comments): they have their own
idandversion; creation is a POST with anIdempotency-Key, and updates or deletes are guarded byIf-Match.
Assignees
| Operation | Endpoint |
|---|---|
| Assign a user | PUT /cards/{card_id}/assignees/{user_id} |
| Unassign a user | DELETE /cards/{card_id}/assignees/{user_id} |
The user must be an active member of the workspace with access to the board. Discover valid IDs through the workspace member directory or the board member list. Assignment sends the same notification the web app sends, filtered to authorized recipients.
Labels
Labels are defined per board — a palette of name + color pairs — and then
applied to cards as a relationship:
| Operation | Endpoint |
|---|---|
| List the board palette | GET /boards/{board_id}/labels |
| Create a label | POST /boards/{board_id}/labels |
| Delete a label | DELETE /labels/{label_id} |
| Apply to a card | PUT /cards/{card_id}/labels/{label_id} |
| Remove from a card | DELETE /cards/{card_id}/labels/{label_id} |
The card and the label must belong to the same board; applying a label from
another board fails with 422 validation_error. Deleting a label removes it
from every card that carried it. Colors are six-digit #RRGGBB hex, accepted
case-insensitively and normalized to lowercase.
Subtasks
Subtasks are a card's checklist, ordered by position:
| Operation | Endpoint |
|---|---|
| List | GET /cards/{card_id}/subtasks |
| Create (appends at the end) | POST /cards/{card_id}/subtasks |
| Set completion | PATCH /subtasks/{subtask_id} |
| Delete | DELETE /subtasks/{subtask_id} |
| Link a file | PUT /subtasks/{subtask_id}/linked-file/{file_id} |
| Clear the linked file | DELETE /subtasks/{subtask_id}/linked-file |
The completion PATCH takes { "completed": true } or
{ "completed": false } — a desired state, not a toggle, so retries are
deterministic.
A subtask can carry at most one linked file — a desired-state relationship
like attachments: the PUT sets it (replacing any previous link), the DELETE
clears it, both are idempotent, and neither takes If-Match. SubtaskView
always carries a linked_file field: null when none is linked, or a
SubtaskLinkedFile object (file_id, name, broken) when one is. If the
linked file is later deleted or stops being visible to you, linked_file
flips to broken: true with the file's last known name and file_id: null
— it never exposes an invisible file's UUID. Setting or clearing the linked
file is a change to the subtask, so — like completion — it bumps the
subtask's own version and, transitively, the parent card's version (see
below).
Comments
| Operation | Endpoint |
|---|---|
| List (cursor-paginated, oldest first) | GET /cards/{card_id}/comments |
| Create | POST /cards/{card_id}/comments |
| Edit | PATCH /comments/{comment_id} |
| Delete | DELETE /comments/{comment_id} |
Editing and deleting are author-only: you can only modify comments your
token's user wrote, regardless of scope. Comment bodies support @-mentions
that notify board members — see
Comment and mention users.
Every effective change bumps the parent card version
The card representation embeds assignee_user_ids, label_ids,
subtask_total, subtask_completed, and comment_count. To keep the card's
ETag a truthful validator of that whole representation, any actual
change to an assignee, applied label, subtask, or comment increments the
parent card's version atomically. Deleting a label bumps every card that
carried it.
Two practical consequences:
- A card
ETagyou are holding can go stale because a teammate commented. When a card write fails with412 precondition_failed, re-read the card and retry — see Optimistic concurrency. - Desired-state calls that change nothing (assigning an already-assigned user, removing an absent label) do not bump any version.
Attachments and covers are the exception: they are separately authorized subresources whose visibility can change with file permissions, so they are not part of the card representation and do not affect the card version. See File attachments.
Example: desired-state assignment
Assigning a user twice is safe — both calls return 200 with the same
relationship:
cURL:
CARD_ID="<card-id>"
USER_ID="<workspace-user-id>"
curl -sS -X PUT "$KANBAN_API_BASE_URL/cards/$CARD_ID/assignees/$USER_ID" \
-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;
const cardId = process.env.KANBAN_CARD_ID;
const userId = process.env.KANBAN_ASSIGNEE_ID;
async function main() {
const response = await fetch(
`${baseUrl}/cards/${cardId}/assignees/${userId}`,
{
method: "PUT",
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("assigned at", payload.data.assigned_at);
}
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"]
CARD_ID = os.environ["KANBAN_CARD_ID"]
USER_ID = os.environ["KANBAN_ASSIGNEE_ID"]
request = urllib.request.Request(
f"{BASE_URL}/cards/{CARD_ID}/assignees/{USER_ID}",
method="PUT",
headers={"Authorization": f"Bearer {TOKEN}"},
)
with urllib.request.urlopen(request) as response:
payload = json.load(response)
print("assigned at", payload["data"]["assigned_at"])
For a complete walkthrough of these endpoints, see Manage assignees, labels, and subtasks.