NoteBugsDocs

API reference

Cards

Create, read, edit, move and delete, plus the two batch routes.

The card is the first-class entity: tenantId is required at creation and immutable from then on. Before sending columnId, check which column set holds for the card.

POST/api/cards
Role: MemberWorkspace

Creates a card, from scratch or from a template.

title and columnId stop being required once templateId arrives: the composition is “whatever came in the request wins, the template fills the rest”. Whatever is still empty after that is refused with 422.

Request body

FieldTypeDescription
tenantIdrequiredcuidRequired and IMMUTABLE: the card is born inside a workspace and never leaves it.
templateIdcuid | nullThe template that fills whatever the body did not bring. It must be from the same workspace.
titlestring
descriptionstring
columnIdcuidMust belong to the set in effect for this card; see columnScope.
projectIdcuid | nullnull leaves the card explicitly without a project.
epicIdcuid | nullMust be an epic of the SAME project as the card.
branchstring | nullA free field. An empty string is normalised to null.
dueAtISO 8601 | nullISO 8601 instant. Without dueAtZone, the due date does not carry the zone it was declared in.
dueAtZoneIANA | nullThe zone the due date was DECLARED in (America/Sao_Paulo). It is what makes the same day hold for every reader.
tagIdscuid[]The FINAL list of labels, all from the same workspace.

Request

curl -s -X POST http://localhost:3000/api/cards \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "cmsp4djx60002p801o7ybpkv7",
    "title": "Publicar a documentação",
    "description": "Site estático com a referência da API.",
    "columnId": "cmttg1vdl000tlf01uuqhz8ad",
    "projectId": "cmttg1vjy000zlf015g097nhi",
    "branch": "feature/api-documentation",
    "tagIds": ["cmttg1vnk0011lf01q0eswms5"]
  }'

Response201

{
  "id": "cmttg1vv00017lf01ol5bnlx2",
  "number": 198,
  "title": "Publicar a documentação",
  "description": "Site estático com a referência da API.",
  "columnId": "cmttg1vdl000tlf01uuqhz8ad",
  "projectId": "cmttg1vjy000zlf015g097nhi",
  "epicId": "cmttg1vs50015lf010l6k38uq",
  "branch": "feature/api-documentation",
  "dueAt": "2026-11-01T23:58:59.999Z",
  "dueAtZone": "America/Sao_Paulo",
  "favorite": false,
  "position": 0,
  "createdAt": "2026-09-09T01:52:58.764Z",
  "updatedAt": "2026-09-09T01:52:58.764Z",
  "tags": [{ "id": "cmttg1vnk0011lf01q0eswms5", "name": "api", "color": "mint" }],
  "attachments": [],
  "commentCount": 0
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.
422The given epic does not belong to the card's project. The rule holds in both directions: changing the card's project is checked too.
422The body did not pass the schema. The details field carries zod's fieldErrors and formErrors, field by field.

The card is born in the first column of the set and at the top, with a sequential number that is never reused, and creation already writes the genesis of the history. Entering an epic with a date target may inherit its due date.

GET/api/cards/[id]
Role: ViewerWorkspace

One card, with labels, attachments and the comment count.

Path parameters

FieldTypeDescription
idrequiredcuid

Request

curl -s http://localhost:3000/api/cards/cmttg1vv00017lf01ol5bnlx2 \
  -H "Authorization: Bearer $TOKEN"

Response200

{
  "id": "cmttg1vv00017lf01ol5bnlx2",
  "number": 198,
  "title": "Publicar a documentação",
  "description": "Site estático com a referência da API.",
  "columnId": "cmttg1vdl000tlf01uuqhz8ad",
  "projectId": "cmttg1vjy000zlf015g097nhi",
  "epicId": "cmttg1vs50015lf010l6k38uq",
  "branch": "feature/api-documentation",
  "dueAt": "2026-11-01T23:58:59.999Z",
  "dueAtZone": "America/Sao_Paulo",
  "favorite": false,
  "position": 0,
  "createdAt": "2026-09-09T01:52:58.764Z",
  "updatedAt": "2026-09-09T01:52:58.764Z",
  "tags": [{ "id": "cmttg1vnk0011lf01q0eswms5", "name": "api", "color": "mint" }],
  "attachments": [],
  "commentCount": 0
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.

favorite is the answer for WHOEVER ASKED: the mark is personal and lives on a row of its own, so two readers get different values for the same card.

PATCH/api/cards/[id]
Role: MemberWorkspace

Edits a card. An absent field is not touched.

Takes { dueAt } or { favorite } on their own: it is what the star and the due-date picker use, with no dedicated route for each.

Path parameters

FieldTypeDescription
idrequiredcuid

Request body

FieldTypeDescription
titlestring
descriptionstring
columnIdcuidChanging column here writes history, just like dragging.
projectIdcuid | null
epicIdcuid | nullnull takes the card out of the epic; the card stays in the project.
branchstring | null
dueAtISO 8601 | null
dueAtZoneIANA | nullAbsent PRESERVES the stored zone: it does not fall back to the caller's zone.
favoritebooleanA personal mark. No epic, due-date or history rule is triggered by this field.
tagIdscuid[]The FINAL list: whatever does not come here is removed from the card.

Request

# the star in the interface sends only this
curl -s -X PATCH http://localhost:3000/api/cards/cmttg1vv00017lf01ol5bnlx2 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "favorite": true }'

Response200

{
  "id": "cmttg1vv00017lf01ol5bnlx2",
  "number": 198,
  "title": "Publicar a documentação",
  "description": "Site estático com a referência da API.",
  "columnId": "cmttg1vdl000tlf01uuqhz8ad",
  "projectId": "cmttg1vjy000zlf015g097nhi",
  "epicId": "cmttg1vs50015lf010l6k38uq",
  "branch": "feature/api-documentation",
  "dueAt": "2026-11-01T23:58:59.999Z",
  "dueAtZone": "America/Sao_Paulo",
  "favorite": true,
  "position": 0,
  "createdAt": "2026-09-09T01:52:58.764Z",
  "updatedAt": "2026-09-09T01:52:58.764Z",
  "tags": [{ "id": "cmttg1vnk0011lf01q0eswms5", "name": "api", "color": "mint" }],
  "attachments": [],
  "commentCount": 0
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.
422The body came empty. An absent field means do not touch, so a PATCH with no field would have no effect at all.
422The given epic does not belong to the card's project. The rule holds in both directions: changing the card's project is checked too.

Clearing a field takes an explicit value (null). Absent means “do not touch”, and that is what lets you send a single field.

DELETE/api/cards/[id]
Role: MemberWorkspace

Deletes a card, with its comments, links, history and the files on the volume.

Path parameters

FieldTypeDescription
idrequiredcuid

Request

curl -s -X DELETE http://localhost:3000/api/cards/cmttg1vv00017lf01ol5bnlx2 \
  -H "Authorization: Bearer $TOKEN"

Response200

{ "deleted": true }

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.
POST/api/cards/[id]/move
Role: MemberWorkspace

Moves the card to a column and a position: this is where dragging goes through.

The position arrives as afterCardId, and never as an index: with a filter on, the visible index does not match the column's real index.

Path parameters

FieldTypeDescription
idrequiredcuid

Request body

FieldTypeDescription
columnIdrequiredcuidThe destination column, from the set in effect for this card.
afterCardIdrequiredcuid | nullThe card this one goes after. null drops it at the top of the column.

Request

# afterCardId null drops the card at the TOP of the column
curl -s -X POST http://localhost:3000/api/cards/cmttg1vv00017lf01ol5bnlx2/move \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "columnId": "cmttg1vdl000ulf01nhqhd23w",
    "afterCardId": null
  }'

Response200

{
  "id": "cmttg1vv00017lf01ol5bnlx2",
  "number": 198,
  "title": "Publicar a documentação",
  "description": "Site estático com a referência da API.",
  "columnId": "cmttg1vdl000ulf01nhqhd23w",
  "projectId": "cmttg1vjy000zlf015g097nhi",
  "epicId": "cmttg1vs50015lf010l6k38uq",
  "branch": "feature/api-documentation",
  "dueAt": "2026-11-01T23:58:59.999Z",
  "dueAtZone": "America/Sao_Paulo",
  "favorite": false,
  "position": 0,
  "createdAt": "2026-09-09T01:52:58.764Z",
  "updatedAt": "2026-09-09T01:52:58.764Z",
  "tags": [{ "id": "cmttg1vnk0011lf01q0eswms5", "name": "api", "color": "mint" }],
  "attachments": [],
  "commentCount": 0
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.
422The column does not belong to the set in effect for this card. Which set holds is decided by columnScope.

Changing column writes a history row; reordering within the same column writes nothing.

PATCH/api/cards/bulk
Role: MemberWorkspace

Edits the details of several cards at once.

Labels travel as ADD/REMOVE rather than as the final list a single card's form sends: replacing the list would erase the labels each card already had.

Request body

FieldTypeDescription
cardIdsrequiredcuid[]From 1 to 200 cards. Every workspace involved is checked.
branchstring | null
dueAtISO 8601 | nullnull clears the due date of the cards in the batch.
dueAtZoneIANA | null
addTagIdscuid[]Labels to add. An id cannot be here and in removeTagIds.
removeTagIdscuid[]Labels to take off.

Request

curl -s -X PATCH http://localhost:3000/api/cards/bulk \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cardIds": ["cmttg1vv00017lf01ol5bnlx2", "cmttg1vyj001blf01888i4wn8"],
    "branch": "feature/api-documentation",
    "addTagIds": ["cmttg1vnk0011lf01q0eswms5"]
  }'

Response200

{ "updated": 2 }

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.
422The batch picked no field to change, or asked to add and remove the same label.

The batch refuses as a whole: if one card breaks a rule, none is changed. Title and column are left out: those are card-by-card decisions.

PATCH/api/cards/epic
Role: MemberWorkspace

Moves cards into, out of and between epics.

The three are the same operation: writing epicId (or null) on a set of cards.

Request body

FieldTypeDescription
cardIdsrequiredcuid[]
epicIdrequiredcuid | nullnull takes the cards out of the epic; they stay in the project.
alignDueAtbooleanAn EXPLICIT opt-in to overwrite the date of whoever is due after the epic's target. Without it the batch refuses instead of clearing a chosen date.When absent: false

Request

curl -s -X PATCH http://localhost:3000/api/cards/epic \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cardIds": ["cmttg1vyj001blf01888i4wn8"],
    "epicId": "cmttg1vs50015lf010l6k38uq",
    "alignDueAt": true
  }'

Response200

{ "updated": 1 }

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account does not reach the resource's workspace. It is 403 and not 404 on purpose: the answer reveals no workspace.
404The resource does not exist, or has already been deleted.
422The epic is locked and takes no new cards. Unlock it first, or pick another one.
422Some card in the batch is due after the epic's target. Send alignDueAt: true to overwrite the dates: the server never clears a chosen date unasked.