Cards API
REST API reference for cards and every sub-resource (members, labels, checklist, comments, attachments, content links and statuses). For the product overview and UI walkthrough, see Cards.
Cards and every sub-resource are available via the Flokan public API. You can build custom intake forms, sync external tools, or automate moves and status updates from your own scripts.
Authentication
All requests require a workspace API key minted in Workspace Settings → API Keys. Pass the key in the Authorization header:
Authorization: Bearer flk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxThe key must include the boards scope, and the workspace's plan must include the Boards & Sheets feature.
Base URL
https://app.flokan.com/api/v1Resolving cards by short ID
Every endpoint that accepts a :cardId in the path also accepts the card's short ID (e.g. MKT-42) in place of the UUID. The same URL works for both human-readable links and machine-generated UUIDs.
Listing and creating cards
Cards live under a board, so list and create go through the board path:
GET /boards/:boardId/cards — List cards on a board
Query params
| Param | Type | Notes |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Default 50, max 100 |
list_id | uuid | Filter to one list |
archived | boolean | Default false. Pass true to fetch archived cards. |
search | string | Case-insensitive title match |
due_before | ISO date | Cards due on or before this date |
due_after | ISO date | Cards due on or after this date |
created_by | uuid | Filter by author |
assigned_to | uuid | Filter to cards assigned to one user |
label_id | uuid | Filter to cards with one label |
POST /boards/:boardId/cards — Create a card
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
title | string | yes | 1–500 characters |
list_id | uuid | yes | Must belong to the board |
description | string | no | Up to 50000 characters |
due_date | ISO timestamp | no | |
started_at | ISO timestamp | no | |
estimated_hours | integer | no | 0–99999 |
actual_hours | integer | no | 0–99999 |
member_ids | uuid[] | no | Assign these users on creation |
label_ids | uuid[] | no | Apply these board labels on creation |
checklist | object[] | no | [{ "text": "...", "completed": false }] — created in the given order |
Returns the new card row (sub-resources are created but not echoed in the response — fetch the card detail to see them).
Single card
GET /cards/:cardId — Card detail
Returns the card plus enriched sub-resources in one payload:
{
"success": true,
"data": {
"id": "...",
"short_id": "MKT-42",
"title": "Q2 launch trailer",
"description": "...",
"list_id": "...",
"board_id": "...",
"position": 3,
"due_date": "2026-06-01T00:00:00.000Z",
"archived": false,
"completed_at": null,
"completed_by": null,
"started_at": null,
"estimated_hours": 12,
"actual_hours": null,
"member_ids": ["..."],
"label_ids": ["..."],
"checklist": [
{ "id": "...", "text": "Storyboard", "completed": true, "position": 0 }
],
"attachments": [],
"content_links": [],
"content_statuses": { "script": "approved" },
"comment_count": 4,
"created_at": "...",
"updated_at": "..."
}
}PUT /cards/:cardId — Update a card
All fields optional. Allowed: title, description, list_id, due_date, started_at, estimated_hours, actual_hours, position, archived, completed.
- Setting
archived: truestampsarchived_atandarchived_by. Setting it tofalseclears both. - Setting
completed: truestampscompleted_atandcompleted_by. Setting it tofalseclears both. - Pass
nullfordescription,estimated_hours, oractual_hoursto clear them.
DELETE /cards/:cardId — Delete a card
Hard-deletes the card and all its sub-resources.
POST /cards/:cardId/move — Move a card
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
list_id | uuid | yes | Target list. Cross-board moves are allowed when the target list belongs to a board in the same workspace. |
position | integer | no | Omit to drop the card at the bottom of the target list. |
Members
GET /cards/:cardId/members
Returns each assigned user with their username and avatar_url.
POST /cards/:cardId/members
Body: { "user_id": "uuid" }. Returns 409 if already assigned.
DELETE /cards/:cardId/members/:userId
Labels
GET /cards/:cardId/labels
Returns each label as { id, name, color }.
POST /cards/:cardId/labels
Body: { "label_id": "uuid" }. The label must belong to the card's board. Returns 409 if already assigned.
DELETE /cards/:cardId/labels/:labelId
Checklist
GET /cards/:cardId/checklist
Returns items ordered by position.
POST /cards/:cardId/checklist
Body: { "text": "...", "completed": false }. Appended to the bottom.
PUT /cards/:cardId/checklist/:itemId
Allowed fields: text, completed, position.
DELETE /cards/:cardId/checklist/:itemId
Comments
Threaded one level deep — replies attach to root comments only.
GET /cards/:cardId/comments
Each comment is enriched with an author object (username, avatar_url).
POST /cards/:cardId/comments
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
content | string | yes | 1–2000 characters |
parent_id | uuid | no | Must be a root comment on the same card |
PUT /cards/:cardId/comments/:commentId
Body: { "content": "..." }. Only the user who created the API key can edit comments authored by the same user — returns 403 otherwise.
DELETE /cards/:cardId/comments/:commentId
Attachments
GET /cards/:cardId/attachments
POST /cards/:cardId/attachments
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | 1–500 characters |
url | string | yes | 1–2000 characters |
type | string | no | url (default) or file |
DELETE /cards/:cardId/attachments/:attachmentId
Content links and status
Content links group thumbnails, scripts, voiceovers, and other production artifacts on the card. Each content_type (e.g. script, thumbnail, video) carries its own review status.
GET /cards/:cardId/content
Returns links flat, grouped by type, and the status map:
{
"success": true,
"data": {
"links": [
{ "id": "...", "content_type": "script", "link_url": "...", "name": "Draft v2", "created_by": "...", "created_at": "..." }
],
"links_by_type": { "script": [ { "id": "...", "content_type": "script", "...": "..." } ] },
"statuses": { "script": "approved" }
}
}POST /cards/:cardId/content
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
content_type | string | yes | 1–100 characters (e.g. script, thumbnail, voiceover) |
link_url | string | yes | 1–2000 characters |
name | string | no | 1–500 characters |
Adding the first link of a given content_type automatically sets the status to needs_review.
DELETE /cards/:cardId/content?link_id=:linkId
Pass the link UUID via the link_id query parameter. If you remove the last link of a given content_type, its status row is also cleared.
PUT /cards/:cardId/content/status
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
content_type | string | yes | The content type whose status you're setting |
status | string | yes | none, needs_review, approved, or needs_revisions. Setting none clears the row. |
Errors
All error responses use the envelope { "success": false, "error": "..." }. Common status codes:
| Code | Meaning |
|---|---|
400 | Validation error (bad UUID, missing required field, status not in the allowed set, …) |
401 | Missing or invalid API key |
403 | Missing boards scope, feature not enabled on the workspace plan, or editing another user's comment |
404 | Card, list, label, member, attachment, or comment not found in this workspace |
409 | Member or label already assigned to the card |
429 | Rate limit exceeded — see Retry-After header and X-RateLimit-* headers |
500 | Unexpected server error |
Rate limits
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. The per-minute limit is determined by the workspace's billing plan.