# Api Reference

> The Codecks API is heavily inspired by [GraphQL](https://graphql.org/). When Codecks was started,
> only the idea of GraphQL has been around, but no implementation. So Codecks developed its own
> JSON-based querying language.

This page lists every [model](#models) you can read and every [action](#actions) you can call. See the [Quick Guide to Codecks API](https://manual.codecks.io/api/) for tokens and first examples.

Everything listed here is covered by the [stability promise](https://manual.codecks.io/api/#stability). Items marked **preview** may change in any release, and each change is listed in the [API changelog](https://manual.codecks.io/api-changelog/). Models, fields and actions that aren't listed here are internal: they may work today, but can change or disappear without notice.

## Basics

simple syntax example:

```
{
  "_root": [{
    "relname($query)": [...fields and relations],
    "relname($query2)": [...fields and relations],
  }]
}
```

curl example:

```
curl 'https://api.codecks.io/' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer [TOKEN]' \
  --data-binary '{"query":{"_root":[{"account":["name"]}]}}'
```

Owners and admins create a token under Organization Settings → Integrations → API Tokens; see [the API guide](https://manual.codecks.io/api/) for what a token may reach.

## Query syntax

here's the syntax for what a `$query` looks like.

```
{fieldName1: $val, fieldName2: {"op": $op, "value": $val}}
```

Note that `{fieldName1: $val}` is a shortcut for `{fieldName1: {"op": "eq", "value": $val}}`

The `$query` portion needs to be sent to `JSON.stringify()` or similar. For readiablity's sake we display the non-stringified versions here.

If you want to fetch all related items you can leave out the `$query` portion like this:

```
...
"deck": [{
  "cards": [...fields and relations],
}]
...
```

### Possible values for `$op`

- `eq`: value | null
- `neq`: value | null
- `in`: array
- `notIn`: array
- `gt`: ordinal value
- `gte`: ordinal value
- `lt`: ordinal value
- `lte`: ordinal value
- `inOrNull`: array
- `contains`: string (if field is of type string)
- `has`: value (if field is of type array)
- `overlaps`: array (if field is of type array)
- `search`: string if field is searchable (so far only available for the `content` field within the `card` model)

### Filtering by values of relation fields

`fieldName` can also be used to access a relation's properties. For a `card` this could be e.g `{resolvables: {context: ["block", "review"], isClosed: false}}`.
This would fetch all cards that have at least one non-closed resolvable with the `block`or`review` context

**Negation:**

you can also do `{!resolvables: {context: ["block", "review"], isClosed: false}}` to say only show cards with _no_ such resolvables.

### "in" - shortcut

```
relname({"projectId": [123, 234]})
```

is equivalent to

```
relname({"projectId": {"op": "in", value: [123, 234]}})
```

### special fields

- `$or`
  combines multiple queries via `or`, sub queries may not contain special fields (beside more $or and $and)

  ```
  relname({"$or": [{"isArchived": true}, {"status": "done"}]})
  ```

- `$and`
  combines multiple queries via `and`, sub queries may not contain special fields (beside more $or and $and)

  ```
  relname({"$and": [
    {"effort": {"op": "gt", value: 0}},
    {"effort": {"op": "lte", value: 5}}
  ]})
  ```

- `$order`: OrderExpression

  ```
  relname({"deckId": 123, "$order": "createdAt"})
  ```

  Full OrderExpression is `{"field": fieldName, "dir": "desc"|"asc"}[]`

  Shortcuts are

  - `{"field": fieldName, "dir": "desc"|"asc"}` -> \[`{"field": fieldName, "dir": "desc"|"asc"}`]
  - `fieldName` -> \[`{"field": fieldName, "dir": "asc"}`]
  - `-fieldName` -> \[`{"field": fieldName, "dir": "desc"}`]

- `$first`: true

  only works when order is provided, return singleton

  ```
  relname({"deckId": 123, "$order": "createdAt", "$first": true})
  ```

- `$limit`: number

  only works when order is provided, there's always a maximum $limit of 3000

  ```
  relname({"deckId": 123, "$order": "createdAt", "$limit": 10})
  ```

- `$offset`: number

  only works when limit (and thus order) is provided

  ```
  relname({"deckId": 123, "$order": "createdAt", "$limit": 10, "$offset": 20})
  ```

### Checking for `null`

`null` works as a value for `eq` and `neq`, and with the shortcut:

```
cards({"assigneeId": null})
cards({"milestoneId": {"op": "neq", "value": null}})
```

## Counting rows

Put `count:` or `exists:` in front of a list relation in a field list to get a number or a boolean instead of the rows:

```
{"_root": [{"account": ["count:cards", "exists:projects"]}]}
```

A query works the same as on the relation itself:

```
"count:cards({\"status\": \"started\"})"
```

The result is stored on the row under exactly the key you sent, e.g. `"count:cards({\"status\":\"started\"})": 12`. Only relations that return a list can be counted: `count:deck` on a card fails with `invalid_aggregate`.

## Repeated relations

Each key in a field list is its own relation, so you can ask for the same relation several times with different queries, next to its counts:

```
{
  "deck([DECK_ID])": [
    "count:cards",
    {"cards({\"status\": \"started\"})": ["title"]},
    {"cards({\"$order\": \"-createdAt\", \"$first\": true})": ["title"]}
  ]
}
```

The response stores each list under its key as you wrote it. The `$first` one holds a single id, or `null` if nothing matched. The cards themselves are listed once under `card`, whichever of the three returned them.

## Id-list relations

A few list relations hold a plain list of ids on the row itself, e.g. a card's `childCards`, `attachments`, `inDeps` and `outDeps`. They don't take a `$query`: you can select their fields and use `count:` and `exists:`, but you can't filter, order or limit them. Filter on the related model instead, e.g. `cards({"parentCardId": "[CARD_ID]"})` for a card's sub cards.

## Root queries by id

root level queries look like this:

```
{
  "modelname($id)": [...fields and relations]
}
```

`$id` is either a string for single id models or a JSON array for compound ids.

## Relations as fields

A relation that points to a single row can also be named in a field list without a nested list. `"deck"` instead of `{"deck": [...]}` returns the deck's id.

To filter by such a relation, use its id: `cards({"deckId": "[DECK_ID]"})`.

## Writing data

Every change goes through an action, sent as a `POST` with a JSON body:

```
curl 'https://api.codecks.io/dispatch/cards/create' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer [TOKEN]' \
  --data-binary '{"content": "My card", "deckId": "[DECK_ID]"}'
```

- Params marked **required** have to be sent. All others can be left out, and leaving them out never changes anything else.
- Params the action doesn't know are ignored without an error. Check the spelling of a param when an action seems to have no effect.
- The response is listed with each action. Most actions answer without a body.
- A token that lacks a scope the action requires gets a `403` naming it, e.g. `requires card:write`.

## Scopes

Every action lists the scopes it requires, like `card:write`. Reading a field needs one too: most fields need `<model>:read`, a few need another scope, like `account.billingEmail`, which needs `account:readAdmin`.

A scope is a model and a verb. No scope includes another one: `card:write` doesn't allow reading cards, so a permission that allows both includes `card:read` as well.

| Verb          | Allows                                                                              |
| ------------- | ----------------------------------------------------------------------------------- |
| `read`        | reading the model's fields                                                          |
| `readPublic`  | reading the fields an Open Decks visitor sees too                                   |
| `readAdmin`   | reading fields only admins see, like `account.billingEmail`                         |
| `readSelf`    | reading your own settings on `user`, like `timezone`                                |
| `write`       | creating and changing rows                                                          |
| `writeOwn`    | changing rows that belong to the token's user. For a personal token, that's you     |
| `writeAny`    | changing rows that belong to someone else                                           |
| `create`      | creating a project                                                                  |
| `delete`      | deleting a project, a deck or a stored file. Removing a file from a card is `write` |
| `writeAccess` | changing a project's `defaultUserAccess`, i.e. whether the whole team sees it       |

The **Permission** you pick when creating a token decides which scopes it holds. Each permission includes everything of the one before it:

**Read**: `_root:read`, `account:read`, `account:readPublic`, `accountRole:read`, `activity:read`, `assigneeAssignment:read`, `assigneeDeckAssignment:read`, `attachment:read`, `card:read`, `cardHistory:read`, `cardOrder:read`, `cardOrderInDeck:read`, `cardPreset:read`, `cardSubscription:read`, `cardUpvote:read`, `cardsEffortHistory:read`, `cardsFinishedHistory:read`, `cardsStatusHistory:read`, `cardsTimeToFinished:read`, `deck:read`, `deckAssignment:read`, `deckGuardian:read`, `deckSubscription:read`, `dueCard:read`, `file:read`, `forecastOverwrite:read`, `handCard:read`, `milestone:read`, `milestoneProgress:read`, `milestoneProject:read`, `pinnedMilestone:read`, `project:read`, `projectOrder:read`, `projectTag:read`, `projectUser:read`, `projectUserSetting:read`, `publicProjectInfo:read`, `queueEntry:read`, `release:read`, `resolvable:read`, `resolvableEntry:read`, `resolvableEntryHistory:read`, `resolvableEntryReaction:read`, `resolvableParticipant:read`, `resolvableParticipantHistory:read`, `sprint:read`, `sprintConfig:read`, `sprintConfigProgress:read`, `sprintProgress:read`, `sprintProject:read`, `timeTrackingSegment:read`, `timeTrackingSum:read`, `user:read`, `userTag:read`, `visionBoard:read`, `visionBoardQuery:read`, `workflowItem:read`, `workflowItemHistory:read`, `workflowTemplate:read`, `workflowTemplateItem:read`

- Personal tokens also hold these, for your own data: `accountUserSetting:read`, `cardDiffNotification:read`, `lastSeenCardUpvote:read`, `projectSelection:read`, `queueSelection:read`, `resolvableNotification:read`, `savedSearch:read`, `user:readSelf`, `userNotificationChannel:read`

**Read & write**: everything in Read, plus `attachment:write`, `card:write`, `cardOrder:write`, `cardPreset:write`, `cardSubscription:writeOwn`, `cardUpvote:writeOwn`, `deckSubscription:writeOwn`, `file:write`, `handCard:writeOwn`, `pinnedMilestone:writeOwn`, `queueEntry:write`, `resolvable:write`, `resolvableEntry:writeOwn`, `resolvableEntryReaction:writeOwn`, `resolvableParticipant:writeOwn`, `timeTrackingSegment:writeOwn`, `userTag:writeOwn`, `visionBoard:write`, `visionBoardQuery:write`

- Organization tokens also hold `handCard:writeAny`
- Personal tokens also hold these, for your own data: `accountUserSetting:writeOwn`, `cardDiffNotification:writeOwn`, `lastSeenCardUpvote:write`, `projectOrder:writeOwn`, `projectSelection:writeOwn`, `queueSelection:writeOwn`, `resolvableNotification:writeOwn`, `savedSearch:writeOwn`, `user:writeOwn`, `userNotificationChannel:writeOwn`

**Producer**: everything in Read & write, plus `deck:delete`, `deck:write`, `deckGuardian:write`, `forecastOverwrite:write`, `milestone:write`, `project:write`, `projectTag:write`, `projectUser:write`, `sprint:write`, `sprintConfig:write`, `timeTrackingSegment:writeAny`, `workflowItem:write`, `workflowTemplate:write`

**Admin**: everything in Producer, plus `account:readAdmin`, `account:write`, `accountRole:write`, `affiliateCode:read`, `file:delete`, `invoice:read`, `project:create`, `project:delete`, `project:writeAccess`

A personal token never holds more than your role in the organization allows. If your role is below the token's permission, the token acts with the permission your role allows, see [Personal tokens](https://manual.codecks.io/api/#personal-tokens).

## Reading this reference

- `| null` means the value may be `null`.
- A `timestamp` is an ISO 8601 string in UTC, like `"2026-09-25T13:37:00.000Z"`. A `day` is a date without a time, like `"2026-09-25"`.
- `int32` is a whole number. `any` is a JSON value the reference doesn't describe further.
- A type like `"a" | "b"` lists the values known today. New values may be added at any time, so your code needs to handle values it doesn't know.


## Models

### _root

The starting point of every query.

Relations:

- `account`: `account | null`. The organization the request is for.
- `loggedInUser`: `user | null`. The user the request acts as. For an organization's API token it's the token's own user. `null` when nobody is signed in.
- `releases`: `release[]`. The entries of the Codecks changelog.

### account

An organization and its settings.

Fields:

- `id`: `account id`
- `allowInheritHeroCover`: `boolean` _(preview)_. `true` makes the web app show a hero card's cover image on its sub cards, in place of their own.
- `attachmentCoverMode`: `"none" | "first" | "last"` _(preview)_. Which uploaded image attachment becomes the card's `coverFile`. `first` only sets it if the card has no cover yet, `last` always uses the newest image, `none` never sets it.
- `createdAt`: `timestamp`
- `dependenciesEnabled`: `boolean`
- `dueDateEnabled`: `boolean`
- `effortScale`: `int32[]`. The effort values the web app offers for a card. A card's `effort` may still be any other number.
- `fallbackEffort`: `int32` _(preview)_. The effort the web app counts for cards without an `effort`, e.g. in charts, hero cards and run capacity.
- `hideCompletedCardCountForDecks`: `boolean` _(preview)_. `true` hides the number of done cards on decks in the web app.
- `maxHandSlotCount`: `int32` _(preview)_. How many cards that aren't done each user may have in their Hand, between 7 and 28. `handQueue/addCardsToHand`, `handQueue/setCardOrders` and `cards/create` with `putInQueue` fail beyond it. Starting a card and hand sync can still go beyond it.
- `milestonesEnabled`: `boolean`
- `name`: `string`
- `priorityLabels`: `{a: string; b: string; c: string}`. The names the web app shows for the card priorities `a`, `b` and `c`.
- `sprintsEnabled`: `boolean`. Whether the organization uses runs.
- `startWeekday`: `"monday" | "saturday" | "sunday"`. The first day of the week in the calendar and timeline. Users can override it for themselves.
- `statusChangeDurations`: `{snooze: int32; archive: int32 | null}` _(preview)_. In seconds. A started card without changes for `snooze` seconds becomes `snoozing`. A done card without changes and open comment threads for `archive` seconds gets archived. `archive` is `null` when done cards never get archived.
- `subdomain`: `string`. The part before `.codecks.io` in the organization's web address.
- `timeTrackingMode`: `"none" | "manual" | "strict"` _(preview)_. `none` turns time tracking off. `manual` allows only manual time entries. `strict` also adds a timer that users start with the play button on a card.
- `timelineScaleType`: `"day" | "week"` _(preview)_. Whether the timeline shows days or weeks. Users can override it for themselves.
- `visionBoardEnabled`: `boolean` _(preview)_
- `workdays`: `{monday: boolean; tuesday: boolean; wednesday: boolean; thursday: boolean; friday: boolean; saturday: boolean; sunday: boolean}`. The days the team works. The web app only counts these days, e.g. for the days left until a milestone.
- `workflowMode`: `"none" | "only_parent_cards" | "journeys"` _(preview)_. `journeys` when the organization uses journeys, `only_parent_cards` when not.

Relations:

- `archivedProjects`: `project[]`
- `attachments`: `attachment[]`
- `cards`: `card[]`
- `decks`: `deck[]`. Without deleted decks.
- `files`: `file[]`
- `handCards`: `handCard[]`. The bookmarked cards of all users.
- `milestones`: `milestone[]`
- `projects`: `project[]`. Projects that are neither archived nor deleted.
- `queueEntries`: `queueEntry[]`. The cards in the Card Hand of all users.
- `resolvables`: `resolvable[]`. The comment threads of all cards.
- `sprintConfigs`: `sprintConfig[]`. The organization's run configs.
- `sprints`: `sprint[]`. The organization's runs.
- `workflowItems`: `workflowItem[]` _(preview)_. The organization's journey steps.

### attachment

A file attached to a card.

Fields:

- `id`: `attachment id`
- `accountId`: `account id`
- `cardId`: `card id`
- `content`: `string`. The attachment's label as markdown. It starts as the file name.
- `createdAt`: `timestamp`
- `creatorId`: `user id | null`
- `fileId`: `file id`
- `title`: `string`. The first line of `content` as plain text, cut to 80 characters.

Relations:

- `account`: `account`
- `card`: `card`
- `creator`: `user | null`
- `file`: `file`

### card

A task or a document card when `isDoc` is set or a hero card if it contains sub cards.
The card's color is derived from `status`, `visibility`, `isDoc` and whether it has sub cards or open block or review conversations.

Fields:

- `cardId`: `card id`
- `accountId`: `account id`
- `accountSeq`: `int32`. The card's number within the organization. It's stored as a plain number starting at 1. The web app shows it encoded as a short code after a `$`, like `$12a`. Check [this gist](https://gist.github.com/danielberndt/19857421171dbb07a3681f0ea6634049) for how to translate the number into the shown string and vice versa.
- `assigneeId`: `user id | null`. The card's owner.
- `checkboxInfo`: `{label: string; checked: boolean}[]` _(preview)_. The checkboxes in `content`, in order.
- `checkboxStats`: `{total: int32; checked: int32}`. How many checkboxes `content` has, and how many of them are checked.
- `content`: `string`. The card's text as markdown. Its first line is the `title`. Tags, mentions, checkboxes and card references in it fill `tags`, `masterTags`, `mentionedUsers`, `checkboxStats`, `checkboxInfo` and `cardReferences`.
- `coverFileId`: `file id | null`. The image shown on the card's front.
- `createdAt`: `timestamp`
- `creatorId`: `user id | null`. `null` for cards an import created.
- `deckId`: `deck id | null`. `null` for a private card, which only its creator sees.
- `derivedStatus`: `"deleted" | "archived" | "archivedDone" | "archivedHeroDone" | "doc" | "blocked" | "review" | "hero" | "heroDone" | "heroDoc" | "assigned" | "unassigned" | "started" | "done" | "snoozing" | "archivedHero"` _(preview)_. The state the web app shows, combining `status`, `visibility`, `isDoc`, open review and blocker threads, the assignee and whether it's a hero card: e.g. `blocked` for an open blocker thread, `unassigned` for a not started card without an assignee.
- `dueDate`: `day | null`
- `effort`: `int32 | null`. On the organization's scale, `account.effortScale`.
- `hasBlockingDeps`: `boolean` _(preview)_. One of `inDeps` isn't done yet.
- `isBlockingDep`: `boolean` _(preview)_. The card isn't done yet, and one of its `outDeps` isn't either.
- `isDoc`: `boolean | null`. `true` for a document card: it holds information instead of a task, so it's never started or done.
- `lastUpdatedAt`: `timestamp`. When the card last changed.
- `masterTags`: `string[]`. The card's project tags: the tags from the project's tag list, whether written as `#tag` in `content` or set directly, plus the deck's auto-tag. Writing them may change `content`.
- `mentionedUsers`: `user id[]` _(preview)_. The users mentioned in `content`.
- `milestoneId`: `milestone id | null`
- `parentCardId`: `card id | null`. The hero card this card is a sub card of.
- `priority`: `"a" | "b" | "c" | null`. `a`, `b` or `c`: High, Medium and Low, unless the organization renamed them (`account.priorityLabels`).
- `sourceWorkflowItemId`: `workflowItem id | null` _(preview)_. The journey step the card was created from.
- `sprintId`: `sprint id | null`. The run the card is planned into.
- `status`: `"not_started" | "started" | "snoozing" | "done"`. `snoozing` is only set on a `started` card that nobody changed for the organization's snooze time (`account.statusChangeDurations`). It can't be written. `derivedStatus` is the state the web app shows.
- `tags`: `string[]`. Every tag on the card: the `#tags` in `content` and the `masterTags`, each once.
- `title`: `string`. The first line of `content` as plain text, without markdown and cut to 80 characters. Write `content` to change it.
- `version`: `int32`. Goes up with every change to the card.
- `visibility`: `"default" | "archived" | "deleted"`. `archived` cards are put away, `deleted` ones are in the trash. Both stay readable.

Relations:

- `account`: `account`
- `assignee`: `user | null`. The card's owner.
- `attachments`: `attachment[]`. In the order the card shows them.
- `cardReferences`: `card[]` _(preview)_. The cards that `content` links to.
- `childCards`: `card[]`. A hero card's sub cards, in order.
- `coverFile`: `file | null`. The image shown on the card's front.
- `creator`: `user | null`. `null` for cards an import created.
- `deck`: `deck | null`. `null` for a private card, which only its creator sees.
- `diffs`: `cardHistory[]` _(preview)_. The card's change history.
- `handCards`: `handCard[]`. One entry per user who bookmarked the card.
- `inDeps`: `card[]` _(preview)_. The cards that have to be done before this one.
- `milestone`: `milestone | null`
- `outDeps`: `card[]` _(preview)_. The cards that wait for this one. The counterpart of their `inDeps`.
- `parentCard`: `card | null`. The hero card this card is a sub card of.
- `queueEntries`: `queueEntry[]`. One entry per user who has the card in their Card Hand.
- `resolvableEntries`: `resolvableEntry[]`. Every comment on the card, across its threads.
- `resolvables`: `resolvable[]`. The card's comment threads, including reviews and blockers (`context`).
- `sourceWorkflowItem`: `workflowItem | null` _(preview)_. The journey step the card was created from.
- `sprint`: `sprint | null`. The run the card is planned into.

### cardHistory _(preview)_

One version of a card and what changed in it. The web app shows these in the card's history.

Identified by `cardId, version`.

Fields:

- `cardId`: `card id`
- `version`: `int32`. The card's `version` after this change.
- `accountId`: `account id`
- `changerId`: `user id | null`
- `diff`: `{[key: string]: any}`. The changed fields: `[old, new]` for single values, `{"+": added, "-": removed}` for lists, and an encoded text diff for `content` and `title`.
- `versionCreatedAt`: `timestamp`

Relations:

- `account`: `account`
- `card`: `card`
- `changer`: `user | null`

### deck

A list of cards within a project. `deckType` says which kind of cards it is meant for.

Fields:

- `id`: `deck id`
- `accountId`: `account id`
- `accountSeq`: `int32`. The deck's number within the organization. The web app uses it in the deck's URL, like `/decks/12-my-deck`.
- `coverColor`: `string | null` _(preview)_. A hex color like `#ff8800`.
- `coverFileId`: `file id | null` _(preview)_
- `createdAt`: `timestamp`
- `creatorId`: `user id`
- `deckType`: `"task" | "hero" | "doc" | "mixed"`. The web app calls these Task (`task`), Asset (`hero`), Knowledge (`doc`) and Mixed (`mixed`) decks. Cards in a `hero` deck count as hero cards unless they are docs or sub cards. A started card that moves into a `hero` or `doc` deck goes back to `not_started`.
- `defaultCard`: `{content?: string; assigneeId?: user id | null; priority?: "a" | "b" | "c" | null; effort?: int32 | null; masterTags?: string[]}` _(preview)_. The deck's Template Card. The web app fills in these values when you create a card in this deck. `cards/create` doesn't use them.
- `defaultProjectTagId`: `projectTag id | null`. The deck's auto tag. Cards that are created in or moved into the deck get this project tag.
- `description`: `string`
- `handSyncEnabled`: `boolean` _(preview)_. Cards in this deck that have an assignee go into the assignee's Hand. This happens when a card is created, moved into the deck or gets a new assignee.
- `hasGuardians`: `boolean` _(preview)_. The deck has [guardians](https://manual.codecks.io/guardians/). Then only guardians, producers and admins may mark its cards as done or not done, archive, delete, restore or move them.
- `isDeleted`: `boolean`
- `manualOrderLabels`: `(string | null)[]` _(preview)_. The names of the zones in the deck's manual order, in order. `null` is a zone without a name. A card's zone is the `label` of its `cardOrder` in the `deck` context.
- `milestoneId`: `milestone id | null`. Setting it also puts the deck's cards into this milestone.
- `projectId`: `project id`
- `sortValue`: `string` _(preview)_. Decks within a space are sorted by it. Change it with `decks/addToSpaceAfter` or `decks/addToSpaceBefore`.
- `spaceId`: `int32 | null` _(preview)_. The space of the project the deck is in, one of the ids in `project.spaces`.
- `title`: `string`

Relations:

- `account`: `account`
- `cards`: `card[]`
- `coverFile`: `file | null` _(preview)_
- `creator`: `user`
- `defaultProjectTag`: `projectTag | null`. The deck's auto tag. Cards that are created in or moved into the deck get this project tag.
- `milestone`: `milestone | null`. Setting it also puts the deck's cards into this milestone.
- `project`: `project`
- `workflowItems`: `workflowItem[]` _(preview)_. The steps of the deck's journey.

### file

An uploaded file, like a card attachment or a cover image.

Fields:

- `id`: `file id`
- `accountId`: `account id | null`
- `createdAt`: `timestamp`
- `name`: `string`
- `size`: `int32`. In bytes. `0` once the file is deleted.
- `uploaderId`: `user id | null`
- `url`: `string`. Where to download the file. `""` once the file is deleted.

Relations:

- `account`: `account | null`
- `uploader`: `user | null`

### handCard

A card a user bookmarked. Deleting the card removes its bookmarks.

Identified by `cardId, userId, accountId`.

Fields:

- `cardId`: `card id`
- `userId`: `user id`
- `accountId`: `account id`
- `sortIndex`: `int32`. Bookmarks are listed by ascending `sortIndex`. There may be gaps.

Relations:

- `account`: `account`
- `card`: `card`
- `user`: `user`

### milestone

A date that cards are planned towards. It belongs to a list of projects, or with `isGlobal` to all projects of the organization.

Fields:

- `id`: `milestone id`
- `accountId`: `account id`
- `accountSeq`: `int32`. The milestone's number within the organization. The web app uses it in the milestone's URL: `/milestones/<accountSeq>`.
- `color`: `"gray" | "brown" | "yellow" | "red" | "pink" | "blue" | "green"`
- `coverFileId`: `file id | null` _(preview)_
- `createdAt`: `timestamp`
- `creatorId`: `user id`
- `date`: `day`
- `description`: `string | null`
- `handSyncEnabled`: `boolean` _(preview)_. Its cards that are not done and have an assignee are added to the assignee's Hand.
- `isDeleted`: `boolean`
- `isGlobal`: `boolean`. The milestone belongs to all projects of the organization, also to projects created later.
- `manualOrderLabels`: `(string | null)[]` _(preview)_. The names of the zones the web app groups the cards into when they are ordered by hand, in order. `null` is the zone without a name.
- `name`: `string`
- `startDate`: `day | null`
- `userCapacities`: `{[userId: user id]: int32}` _(preview)_. The planned effort per user, keyed by user id. The web app compares it with the effort of the user's cards, where a card without `effort` counts as `account.fallbackEffort`.

Relations:

- `account`: `account`
- `cards`: `card[]`
- `coverFile`: `file | null` _(preview)_
- `creator`: `user`
- `milestoneProjects`: `milestoneProject[]`
- `progress`: `milestoneProgress[]` _(preview)_. The daily progress history.

### milestoneProgress _(preview)_

The state of a milestone's cards on a day (UTC). A row is only written on days when the numbers change, so a day without a row has the numbers of the row before it.

Identified by `milestoneId, date`.

Fields:

- `milestoneId`: `milestone id`
- `date`: `day`
- `progress`: `any`. Counts the cards in decks of active projects by their `derivedStatus`, with all done states counted as `done`. `cards` and `effort` have the card count and the effort sum for each state (`done`, `started`, `review`, `blocked`, `snoozing`, `assigned`, `unassigned`, `hero`), `stats` has `[count, effort sum, cards without effort]` for each state and `noEffortCards` counts the cards without effort. States without cards are left out.

Relations:

- `milestone`: `milestone`

### milestoneProject

Links a milestone to a project whose cards and decks can use it. A milestone with `isGlobal` is linked to every project.

Identified by `milestoneId, projectId`.

Fields:

- `milestoneId`: `milestone id`
- `projectId`: `project id`
- `accountId`: `account id`

Relations:

- `account`: `account`
- `milestone`: `milestone`
- `project`: `project`

### project

Groups decks. A card belongs to the project of its deck.

Fields:

- `id`: `project id`
- `accountId`: `account id`
- `coverFileId`: `file id | null`
- `createdAt`: `timestamp`
- `name`: `string`
- `spaces`: `any` _(preview)_. The spaces that group the project's decks. Removing a space moves its decks into the first remaining one.
- `visibility`: `"default" | "archived" | "deleted"`. Setting `archived` or `deleted` closes all open comment threads on the project's cards.

Relations:

- `account`: `account`
- `coverFile`: `file | null`
- `decks`: `deck[]`
- `milestoneProjects`: `milestoneProject[]`. Links to the milestones this project can use.
- `sprintProjects`: `sprintProject[]`. Links to the run configs this project can use.
- `tags`: `projectTag[]`. The project's tag list. A card in the project with one of these tags lists it in `masterTags`.

### projectTag

A tag in a project's tag list. A card in the project with this tag lists it in `masterTags`.

Fields:

- `id`: `projectTag id`
- `color`: `string | null`. A hex color like `#ff0000` for cards with this tag. The web app sets either `color` or `emoji`, not both.
- `createdAt`: `timestamp`
- `description`: `string | null`. What the tag is for, as markdown.
- `emoji`: `string | null`
- `projectId`: `project id`
- `tag`: `string`. Without the `#`. Renaming it also renames the tag in the `content` and `masterTags` of the project's cards.

Relations:

- `project`: `project`

### queueEntry

A card in a user's Hand. When a card is done, its owner gets an entry in their Hand's done pile, even if the card wasn't in their Hand.

Fields:

- `id`: `queueEntry id`
- `accountId`: `account id`
- `cardDoneAt`: `timestamp | null`. When the card was done. Other users' entries for the card are removed then. Setting the card back to not done moves the entry to the end of the Hand.
- `cardId`: `card id`
- `createdAt`: `timestamp`
- `sortIndex`: `int32 | null`. The position in the Hand, `0` is the first card. `null` once the card is done.
- `userId`: `user id`

Relations:

- `account`: `account`
- `card`: `card`
- `user`: `user`

### release

An entry in the Codecks changelog. It's the same for every organization.

Fields:

- `id`: `release id`
- `content`: `string`. The release notes as markdown.
- `createdAt`: `timestamp`
- `title`: `string`
- `version`: `string`

### resolvable

A comment thread on a card. Its `context` says if it's a plain conversation or if it marks the card as blocked or in review.

Fields:

- `id`: `resolvable id`
- `accountId`: `account id`
- `cardId`: `card id`
- `closedAt`: `timestamp | null`
- `context`: `"block" | "review" | "comment"`. `block` marks the card as blocked and `review` marks it as in review while the thread is open (see `card.derivedStatus`). `comment` is a plain conversation.
- `createdAt`: `timestamp`
- `creatorId`: `user id`
- `isClosed`: `boolean`. Archiving or deleting the card closes its open threads.
- `reopenedAt`: `timestamp | null`. Only set while the thread is open again after being closed. Closing it clears the value.

Relations:

- `account`: `account`
- `card`: `card`
- `closedBy`: `user | null`. `null` while the thread is open, or when it was closed automatically.
- `creator`: `user`
- `entries`: `resolvableEntry[]`
- `reopenedBy`: `user | null`. Only set while the thread is open again after being closed.

### resolvableEntry

A comment in a comment thread.

Fields:

- `entryId`: `resolvableEntry id`
- `authorId`: `user id`
- `cardId`: `card id`
- `content`: `string`. Markdown. Mentions are stored as `@[userId:<id>]`.
- `createdAt`: `timestamp`
- `lastChangedAt`: `timestamp`. When the comment was last edited. Same as `createdAt` if it never was.
- `resolvableId`: `resolvable id`
- `version`: `int32` _(preview)_. Starts at 1 and goes up with every edit.

Relations:

- `author`: `user`
- `card`: `card`
- `reactions`: `resolvableEntryReaction[]` _(preview)_
- `resolvable`: `resolvable`

### resolvableEntryReaction _(preview)_

An emoji reaction to a comment. A user can add each emoji only once per comment.

Fields:

- `id`: `resolvableEntryReaction id`
- `accountId`: `account id`
- `createdAt`: `timestamp`
- `entryId`: `resolvableEntry id`
- `resolvableId`: `resolvable id`
- `userId`: `user id`
- `value`: `{type: "emoji"; value: string}`. `{"type": "emoji", "value": "👍"}`. `value` is the emoji character itself.

Relations:

- `account`: `account`
- `resolvable`: `resolvable`
- `resolvableEntry`: `resolvableEntry`
- `user`: `user`

### sprint

A run: one time box of a Run Config, from `startDate` to `endDate`. Codecks creates the upcoming runs ahead of time and completes a run when it ends.

Fields:

- `id`: `sprint id`
- `accountId`: `account id`
- `accountSeq`: `int32`. The run's number within the organization. The web app uses it in the run's URL: `/milestones/run/<accountSeq>`.
- `autoMilestoneId`: `milestone id | null` _(preview)_. Cards that are added to this run get this milestone, unless they already have one.
- `completedAt`: `timestamp | null`. When the run was completed, at its end or with `complete` in `sprints/updateSprint`.
- `coverFileId`: `file id | null` _(preview)_
- `createdAt`: `timestamp`
- `description`: `string | null`
- `endDate`: `day`. The last day of the run.
- `handSyncEnabled`: `boolean` _(preview)_. A card that is created in this run with an assignee is added to the assignee's Hand.
- `index`: `int32`. The position of the run within its Run Config, starting at 0. The web app shows `index + 1` as the run's number.
- `isDeleted`: `boolean`
- `lockedAt`: `timestamp | null` _(preview)_. When Beast Mode started for this run. In Beast Mode the run's cards are locked in, and moving one of them to another run raises its Beast level. `null` while it is off.
- `manualOrderLabels`: `(string | null)[]` _(preview)_. The names of the zones the web app groups the cards into when they are ordered by hand, in order. `null` is the zone without a name. A new run starts with the zones of the run before it.
- `name`: `string | null`. `null` unless set by hand. The web app then shows a label from the Run Config's `runLabelTemplate`, or `Run <index + 1>`.
- `sprintConfigId`: `sprintConfig id`
- `startDate`: `day`
- `userCapacities`: `{[userId: user id]: int32}` _(preview)_. The planned effort per user, keyed by user id. The web app compares it with the effort of the user's cards, where a card without `effort` counts as `account.fallbackEffort`. A new run starts with the capacities of the run before it.

Relations:

- `account`: `account`
- `autoMilestone`: `milestone | null` _(preview)_. Cards that are added to this run get this milestone, unless they already have one.
- `cards`: `card[]`
- `coverFile`: `file | null` _(preview)_
- `progress`: `sprintProgress[]` _(preview)_. The daily progress history.
- `sprintConfig`: `sprintConfig`

### sprintConfig

A Run Config. It creates runs of the same length one after the other, like sprints, for its projects.

Fields:

- `id`: `sprintConfig id`
- `accountId`: `account id`
- `autoAssignNewCard`: `boolean` _(preview)_. The web app puts new cards in its projects into the current run. Journeys do the same, `cards/create` doesn't.
- `autoAssignStartedCard`: `boolean` _(preview)_. A card in one of its projects that is started without a run is added to the current run.
- `autoBeastModeDurationHours`: `int32 | null` _(preview)_. Beast Mode starts on its own this many hours after the start of a run, counted from midnight in the Run Config's time zone. `0` starts it right away, `null` turns it off.
- `beastGracePeriodHours`: `int32` _(preview)_. In Beast Mode, a card added to the run is only locked in after this many hours. Until then it can move to another run without raising its Beast level.
- `color`: `"gray" | "brown" | "yellow" | "red" | "pink" | "blue" | "green"`
- `createdAt`: `timestamp`
- `creatorId`: `user id`
- `isGlobal`: `boolean`. The Run Config belongs to all projects of the organization, also to projects created later.
- `moveOnFinish`: `("undone" | "review" | "blocked")[]` _(preview)_. Which cards move to the next run when a run ends: `review` for cards with an open review, `blocked` for cards with an open blocker and `undone` for all other cards that aren't done. Archived cards and docs stay.
- `name`: `string`
- `runLabelTemplate`: `string | null` _(preview)_. The label for runs without a `name`. `%RUN_NR%` becomes the run's `index + 1` and `%CALENDAR_WEEK%` the ISO week of its `startDate`. `null` means `Run %RUN_NR%`.

Relations:

- `account`: `account`
- `creator`: `user`
- `progress`: `sprintConfigProgress[]` _(preview)_. The daily history of the done cards across all runs.
- `sprintProjects`: `sprintProject[]`
- `sprints`: `sprint[]`

### sprintConfigProgress _(preview)_

The done cards of all runs of a Run Config on a day (UTC). A row is only written on days when the numbers change, so a day without a row has the numbers of the row before it.

Identified by `sprintConfigId, date`.

Fields:

- `sprintConfigId`: `sprintConfig id`
- `date`: `day`
- `progress`: `any`. `stats.done` is `[count, effort sum, cards without effort]` of the done cards in all runs, counting only cards in decks of active projects.

Relations:

- `sprintConfig`: `sprintConfig`

### sprintProgress _(preview)_

The state of a run's cards on a day (UTC). A row is only written on days when the numbers change, so a day without a row has the numbers of the row before it.

Identified by `sprintId, date`.

Fields:

- `sprintId`: `sprint id`
- `date`: `day`
- `progress`: `any`. Counts the cards in decks of active projects by their `derivedStatus`, with all done states counted as `done`. `cards` and `effort` have the card count and the effort sum for each state (`done`, `started`, `review`, `blocked`, `snoozing`, `assigned`, `unassigned`, `hero`), `stats` has `[count, effort sum, cards without effort]` for each state and `noEffortCards` counts the cards without effort. States without cards are left out.

Relations:

- `sprint`: `sprint`

### sprintProject

Links a run config to a project whose cards can be planned into its runs. A run config with `isGlobal` is linked to every project.

Identified by `sprintConfigId, projectId`.

Fields:

- `sprintConfigId`: `sprintConfig id`
- `projectId`: `project id`
- `accountId`: `account id`

Relations:

- `account`: `account`
- `project`: `project`
- `sprintConfig`: `sprintConfig`

### user

A person, or an integration or API token that acts in an organization. `kind` tells them apart.

Fields:

- `id`: `user id`
- `fullName`: `string | null`. `null` unless the user entered one.
- `isIntegration`: `boolean`. `true` for every `kind` other than `human`.
- `kind`: `"human" | "integration" | "api_token"`. `integration` is the user an integration like GitHub or Slack acts as, `api_token` the one an organization's API token acts as.
- `name`: `string`. The username. `""` when none is set. An API token's user carries the token's label.
- `profileImageId`: `file id | null`
- `timezone`: `string | null` _(preview)_. An IANA time zone like `Europe/Berlin`. Only readable on your own user: `null` on everyone else's.

Relations:

- `profileImage`: `file | null`

### workflowItem _(preview)_

A Journey Step: a template for a sub card. Starting a deck's journey on a card creates a sub card from each of its steps.

Fields:

- `itemId`: `workflowItem id`
- `accountId`: `account id`
- `accountSeq`: `int32`. The step's number within the organization. It comes from the same counter as `card.accountSeq`.
- `assigneeId`: `user id | null`. The owner of the created card.
- `checkboxInfo`: `{label: string; checked: boolean}[]`
- `checkboxStats`: `{total: int32; checked: int32}`
- `content`: `string`. Markdown for the created card's `content`, see `card.content`. `%PARENT_TITLE%` is replaced by the title of the card the journey is started on.
- `createdAt`: `timestamp`
- `creatorId`: `user id`
- `deckId`: `deck id`. The deck whose journey the step belongs to.
- `effort`: `int32 | null`
- `label`: `string | null`. The name of the group the step is in. `null` for the default group.
- `lastUpdatedAt`: `timestamp`
- `masterTags`: `string[]`
- `mentionedUsers`: `user id[]`
- `priority`: `"a" | "b" | "c" | null`
- `sortValue`: `string`. Steps are ordered by ascending `sortValue`, compared as strings, within their `label` group.
- `tags`: `string[]`
- `targetDeckId`: `deck id | null`. The deck the created card goes to. Without one, it goes to `deck`.
- `title`: `string`
- `version`: `int32`. Goes up with every change to the step.
- `visibility`: `"default" | "archived" | "deleted"`. Only `default` steps are used when a journey is started. Changing an archived or deleted step sets it back to `default`.

Relations:

- `account`: `account`
- `assignee`: `user | null`. The owner of the created card.
- `creator`: `user`
- `deck`: `deck`. The deck whose journey the step belongs to.
- `inDeps`: `workflowItem[]`. Steps of the same journey that have to be done before this one. The created cards get the same dependencies.
- `outDeps`: `workflowItem[]`. Steps of the same journey that are blocked by this one.
- `targetDeck`: `deck | null`. The deck the created card goes to. Without one, it goes to `deck`.

## Actions

### bookmarks/addCards

Bookmarks cards for a user, at the end of their bookmarks. Cards that are already bookmarked move to the end.

`POST /dispatch/bookmarks/addCards`

Requires `handCard:writeOwn` or `handCard:writeAny`, depending on who the call acts on.

Params:

- `ids`: `card id[]`, required
- `userId`: `user id`, required

Response:

No body.

### bookmarks/removeCards

Removes cards from a user's bookmarks.

`POST /dispatch/bookmarks/removeCards`

Requires `handCard:writeOwn` or `handCard:writeAny`, depending on who the call acts on.

Params:

- `ids`: `card id[]`, required
- `userId`: `user id`, required

Response:

No body.

### bookmarks/setOrders

Moves cards to the top of a user's bookmarks, in the given order. The other bookmarks follow in their current order. Cards that aren't bookmarked yet get bookmarked.

`POST /dispatch/bookmarks/setOrders`

Requires `handCard:writeOwn` or `handCard:writeAny`, depending on who the call acts on.

Params:

- `cardIds`: `card id[]`, required
- `userId`: `user id`, required

Response:

No body.

### cardOrders/addAfter _(preview)_

Puts cards into a manual order right after `targetId`, in the order of `cardIds`, and into the zone `label`.

`POST /dispatch/cardOrders/addAfter`

Requires `cardOrder:write`.

Params:

- `targetId`: `card id | null`, required. `null` puts the cards at the end.
- `cardIds`: `card id[]`, required
- `context`: `"milestone" | "deck" | "sprint"`, required. Which manual order to change: the one of the cards' deck, milestone or run. A card has one position in each.
- `label`: `string | null`, required. The zone, one of the `manualOrderLabels` of the deck, milestone or run. `null` is the zone without a name.

Response:

No body.

### cardOrders/addBefore _(preview)_

Puts cards into a manual order right before `targetId`, in the order of `cardIds`, and into the zone `label`.

`POST /dispatch/cardOrders/addBefore`

Requires `cardOrder:write`.

Params:

- `targetId`: `card id | null`, required. `null` puts the cards at the start.
- `cardIds`: `card id[]`, required
- `context`: `"milestone" | "deck" | "sprint"`, required. Which manual order to change: the one of the cards' deck, milestone or run. A card has one position in each.
- `label`: `string | null`, required. The zone, one of the `manualOrderLabels` of the deck, milestone or run. `null` is the zone without a name.

Response:

No body.

### cardOrders/updateLabel _(preview)_

Moves cards into another zone of a manual order and keeps their position. Cards that have no position in that order yet are skipped.

`POST /dispatch/cardOrders/updateLabel`

Requires `cardOrder:write`.

Params:

- `cardIds`: `card id[]`, required
- `context`: `"milestone" | "deck" | "sprint"`, required. Which manual order to change: the one of the cards' deck, milestone or run. A card has one position in each.
- `label`: `string | null`, required. The zone, one of the `manualOrderLabels` of the deck, milestone or run. `null` is the zone without a name.

Response:

No body.

### cards/bulkUpdate

Sets the same values on every card in `ids`. Look at `cards/update` for the property details.

`POST /dispatch/cards/bulkUpdate`

Requires `card:write`.

Params:

- `ids`: `card id[]`, required
- `priority`: `"a" | "b" | "c" | null`
- `effort`: `int32 | null`
- `status`: `"not_started" | "started" | "done"`
- `assigneeId`: `user id | null`
- `deckId`: `deck id`
- `visibility`: `"default" | "archived" | "deleted"`
- `milestoneId`: `milestone id | null`
- `sprintId`: `sprint id | null`
- `dueDate`: `string | null`. `YYYY-MM-DD`, or `null` to remove it. Fails unless the organization's plan includes due dates.
- `isDoc`: `boolean`
- `addMasterTag`: `string`. Adds this tag to every card's `masterTags`.
- `removeMasterTag`: `string`. Removes this tag from every card, ignoring case.
- `parentCardId`: `card id | null`

Response:

No body.

### cards/copy _(preview)_

Copies each card into a deck, as `not_started` and with the same `content`. A copied sub card is added to its hero card right after the original.

`POST /dispatch/cards/copy`

Requires `card:write`.

Params:

- `ids`: `card id[]`, required
- `props`: `("effort" | "priority" | "assigneeId" | "masterTags" | "attachments" | "milestoneId" | "sprintId")[]`, required. The fields to copy besides `content`.
- `target`: `string`, required. The id of the deck to copy into.

Response:

No body.

### cards/create

Creates a card as `not_started`. The first line of `content` becomes its `title`. Everyone mentioned in `content` is subscribed to the card.

`POST /dispatch/cards/create`

Requires `card:write`.

Params:

- `content`: `string`, required. Markdown. Its first line becomes the card's `title`.
- `priority`: `"a" | "b" | "c" | null`
- `effort`: `int32 | null`
- `assigneeId`: `user id | null`
- `deckId`: `deck id | null`, required. `null` creates a private card, and bookmarks it for you.
- `masterTags`: `string[]`. Project tags, on top of those written as `#tag` in `content`.
- `milestoneId`: `milestone id | null`
- `sprintId`: `sprint id | null`
- `parentCardId`: `card id | null`. Adds the card as the last sub card of this hero card.
- `dueDate`: `string | null`. `YYYY-MM-DD`. Fails unless the organization's plan includes due dates.
- `isDoc`: `boolean`
- `addAsBookmark`: `boolean`. Bookmarks the card for you.
- `putInQueue`: `boolean`. Adds the card to its assignee's Hand, or to yours when it has none. Fails when that Hand has no free slot (`account.maxHandSlotCount`).
- `subscribeCreator`: `boolean`. Subscribes you to the card, so you're notified about its changes and comments.

Response:

- `id`: `card id`
- `accountSeq`: `int32`. The card's number, see `card.accountSeq`.

### cards/createMany

Creates cards in one deck and returns their ids in the same order. Unlike `cards/create`, it doesn't subscribe you to them.

`POST /dispatch/cards/createMany`

Requires `card:write`.

Params:

- `cards`: `object[]`, required. The first line of each `content` becomes the card's `title`. `status` defaults to `not_started`, `visibility` to `default`.
  - `content`: `string`, required
  - `priority`: `"a" | "b" | "c" | null`
  - `effort`: `int32 | null`
  - `assigneeId`: `user id | null`
  - `isDoc`: `boolean`
  - `visibility`: `"archived" | "default" | "deleted"`
  - `status`: `"not_started" | "started" | "done" | null`
- `deckId`: `deck id`, required
- `parentCardId`: `card id | null`. Adds the cards as the last sub cards of this hero card.

Response:

- `cards`: `object[]`
  - `id`: `card id`
  - `accountSeq`: `int32`

### cards/update

Changes the given fields of a card. Some writes change other fields too: `content` sets `title` and `tags`, and giving a started card another assignee sets it back to `not_started`.

`POST /dispatch/cards/update`

Requires `card:write`.

Params:

- `id`: `card id`, required
- `content`: `string`. Markdown. Its first line becomes the card's `title`.
- `priority`: `"a" | "b" | "c" | null`
- `effort`: `int32 | null`
- `status`: `"not_started" | "started" | "done"`. A Hero Card can't be started.
- `assigneeId`: `user id | null`
- `deckId`: `deck id`
- `masterTags`: `string[]`. Replaces the project tags. Those written as `#tag` in `content` stay.
- `visibility`: `"default" | "archived" | "deleted"`. Archiving or deleting a started card sets it back to `not_started`. Deleting also removes its dependencies.
- `milestoneId`: `milestone id | null`
- `sprintId`: `sprint id | null`
- `parentCardId`: `card id | null`. The hero card to make this card a sub card of.
- `childCards`: `card id[] | null` _(preview)_. A hero card's sub cards, in order.
- `inDeps`: `card id[]` _(preview)_. The cards that have to be done before this one. Fails when it would make a cycle.
- `outDeps`: `card id[]` _(preview)_. The cards that are blocked by this one.
- `dueDate`: `string | null`. `YYYY-MM-DD`, or `null` to remove it. Fails unless the organization's plan includes due dates.
- `isDoc`: `boolean`

Response:

No body.

### cards/updateMany

Updates several cards, each with its own values. Cards that don't exist or that you can't see are skipped.

`POST /dispatch/cards/updateMany`

Requires `card:write`.

Params:

- `cards`: `object[]`, required. Each needs a `content` or a `title`. With both, the content becomes the title, an empty line and `content`. A `title` alone replaces the first line of the current content. Project tags in `masterTags` that the project doesn't have yet are created.
  - `id`: `card id`, required
  - `content`: `string | null`
  - `title`: `string | null`
  - `priority`: `"a" | "b" | "c" | null`
  - `effort`: `int32 | null`
  - `assigneeId`: `user id | null`
  - `isDoc`: `boolean`
  - `visibility`: `"archived" | "default" | "deleted"`
  - `status`: `"not_started" | "started" | "done" | null`
  - `masterTags`: `string[]`

Response:

No body.

### decks/addToSpaceAfter _(preview)_

Moves decks into a project's space, right after `targetId` and in the order of `deckIds`. Decks from another project move with their cards.

`POST /dispatch/decks/addToSpaceAfter`

Requires `deck:write`.

Params:

- `deckIds`: `deck id[]`, required
- `targetId`: `deck id | null`, required. `null` puts the decks at the end of the space.
- `targetProjectId`: `project id`, required
- `targetSpaceId`: `int32`, required. One of the ids in `project.spaces`.

Response:

No body.

### decks/addToSpaceBefore _(preview)_

Moves decks into a project's space, right before `targetId` and in the order of `deckIds`. Decks from another project move with their cards.

`POST /dispatch/decks/addToSpaceBefore`

Requires `deck:write`.

Params:

- `deckIds`: `deck id[]`, required
- `targetId`: `deck id | null`, required. `null` puts the decks at the start of the space.
- `targetProjectId`: `project id`, required
- `targetSpaceId`: `int32`, required. One of the ids in `project.spaces`.

Response:

No body.

### decks/create

Creates a deck at the end of the project's first space. `deckType` defaults to that space's `defaultDeckType`.

`POST /dispatch/decks/create`

Requires `deck:write`.

Params:

- `title`: `string`, required
- `projectId`: `project id`, required
- `coverColor`: `string | null` _(preview)_. A hex color like `#ff8800`.
- `deckType`: `"task" | "hero" | "doc" | "mixed"`

Response:

- `id`: `deck id`

### decks/delete

Sets `isDeleted` on the deck and archives its cards, their sub cards in other decks, and the journey steps that create cards in this deck.

`POST /dispatch/decks/delete`

Requires `deck:delete`.

Params:

- `id`: `deck id`, required

Response:

No body.

### decks/update

`POST /dispatch/decks/update`

Requires `deck:write` or `project:write`, depending on who the call acts on.

Params:

- `id`: `deck id`, required
- `milestoneId`: `milestone id | null`. Also puts the deck's cards that aren't archived or deleted into this milestone. `null` only takes it from cards that were in the deck's old milestone.
- `title`: `string`
- `description`: `string`
- `handSyncEnabled`: `boolean` _(preview)_. Turning it on adds the deck's assigned cards that aren't done to their assignee's Hand.
- `defaultProjectTagId`: `projectTag id | null`. The deck's auto tag. Changing it removes the old tag from all of the deck's cards and adds the new one.
- `manualOrderLabels`: `(string | null)[]` _(preview)_. The zone names are trimmed, and duplicates are dropped.
- `coverColor`: `string | null` _(preview)_. A hex color like `#ff8800`.
- `deckType`: `"task" | "hero" | "doc" | "mixed"`. Switching to `hero` sets the deck's started cards back to `not_started`. Switching to `hero` or `doc` fails while journey steps create their cards in this deck.

Response:

No body.

### decks/updateGuardians _(preview)_

Sets the deck's guardians to exactly `userIds` and updates `hasGuardians`. Fails unless the organization's plan includes guardians or `userIds` is empty.

`POST /dispatch/decks/updateGuardians`

Requires `deckGuardian:write`.

Params:

- `id`: `deck id`, required
- `userIds`: `user id[]`, required

Response:

No body.

### handQueue/addCardsToHand

Adds cards to the end of a user's Hand. Cards that are done, deleted, archived or already in that Hand are skipped. A card without an owner gets the user as owner. Fails when the Hand doesn't have enough free slots (`account.maxHandSlotCount`).

`POST /dispatch/handQueue/addCardsToHand`

Requires `queueEntry:write`.

Params:

- `cardIds`: `card id[]`, required
- `userId`: `user id`, required

Response:

- `queueEntries`: `object[]`. The new entries, one for each added card.
  - `id`: `queueEntry id`
  - `cardId`: `card id`

### handQueue/removeCards

Removes cards from a user's Hand, or from every Hand when `userId` is left out. This includes entries in the done pile.

`POST /dispatch/handQueue/removeCards`

Requires `queueEntry:write`.

Params:

- `cardIds`: `card id[]`, required
- `userId`: `user id`

Response:

No body.

### handQueue/setCardOrders

Sets the order of a user's Hand and can add cards to it. A card added this way that has no owner gets the user as owner. Fails when the Hand doesn't have enough free slots for the added cards (`account.maxHandSlotCount`).

`POST /dispatch/handQueue/setCardOrders`

Requires `queueEntry:write`.

Params:

- `cardIds`: `card id[]`, required. The new order from the first card on. Cards of the Hand that are missing here follow in their current order. Cards that are neither in the Hand nor in `draggedCardIds` are ignored.
- `draggedCardIds`: `card id[]`, required. The cards that moved, from within the Hand or from outside. Cards from outside are added. Nothing changes if it is empty or none of them can be in a Hand, e.g. because they are deleted.
- `userId`: `user id`, required

Response:

- `queueEntries`: `object[]`. The Hand's entries after the change.
  - `id`: `queueEntry id`
  - `cardId`: `card id`

### milestones/create

Creates a milestone for the projects in `projectIds`, or for all projects with `isGlobal`.

`POST /dispatch/milestones/create`

Requires `milestone:write`.

Params:

- `accountId`: `account id`, required
- `name`: `string`, required
- `description`: `string`
- `color`: `"gray" | "brown" | "yellow" | "red" | "pink" | "blue" | "green"`, required
- `date`: `string`, required. `YYYY-MM-DD`.
- `startDate`: `string | null`. `YYYY-MM-DD`.
- `isGlobal`: `boolean`, required
- `projectIds`: `project id[] | null`, required. Ignored when `isGlobal` is set.

Response:

- `id`: `milestone id`
- `accountSeq`: `int32`. The milestone's number, see `milestone.accountSeq`.

### milestones/delete

Marks a milestone as deleted (`isDeleted`). It's removed from all its cards and decks, and nobody has it pinned anymore.

`POST /dispatch/milestones/delete`

Requires `milestone:write`.

Params:

- `id`: `milestone id`, required

Response:

No body.

### milestones/pin _(preview)_

Pins a milestone for a user. The web app shows it in the user's Hand. A user has one pinned milestone per organization, so pinning another one replaces it.

`POST /dispatch/milestones/pin`

Requires `pinnedMilestone:writeOwn`.

Params:

- `milestoneId`: `milestone id | null`, required. `null` removes the user's pin.
- `userId`: `user id`, required. The token's own user. For a personal token, that's you.

Response:

No body.

### milestones/update

`POST /dispatch/milestones/update`

Requires `milestone:write`.

Params:

- `id`: `milestone id`, required
- `name`: `string`
- `description`: `string`
- `color`: `"gray" | "brown" | "yellow" | "red" | "pink" | "blue" | "green"`
- `date`: `string`. `YYYY-MM-DD`.
- `startDate`: `string | null`. `YYYY-MM-DD`, or `null` to remove it.
- `handSyncEnabled`: `boolean` _(preview)_. Turning it on also adds the milestone's cards that are not done and have an assignee to the assignee's Hand.
- `isGlobal`: `boolean`. `true` links the milestone to all projects. `false` needs `projectIds` if the milestone is global.
- `projectIds`: `project id[] | null`. Replaces the milestone's projects and makes it non-global. Ignored with `isGlobal: true`. Cards and decks of projects that are removed lose the milestone.
- `manualOrderLabels`: `(string | null)[]` _(preview)_. Labels are trimmed, and duplicates are removed.
- `userCapacities`: `{[key: string]: int32}` _(preview)_. Replaces all capacities. Fails with a value above 0 unless the organization's plan includes capacity tracking.

Response:

No body.

### resolvables/addReaction _(preview)_

Adds your reaction to a comment. Fails if you already reacted with the same emoji.

`POST /dispatch/resolvables/addReaction`

Requires `resolvableEntryReaction:writeOwn`.

Params:

- `entryId`: `resolvableEntry id`, required
- `value`: `object`, required. `value` is the emoji character itself, like `👍`.
  - `type`: `"emoji"`, required
  - `value`: `string`, required

Response:

- `id`: `resolvableEntryReaction id`

### resolvables/close

Closes a comment thread. Closing a `block` or `review` thread ends the card's blocked or in review state. In a deck with guardians, only a guardian, a producer or an admin may close a `review`, unless the guardians in it approved and you are the card's assignee.

`POST /dispatch/resolvables/close`

Requires `resolvable:write` or `card:write`, depending on who the call acts on.

Params:

- `id`: `resolvable id`, required
- `markCardDone`: `boolean`. Also sets the card's `status` to `done`.

Response:

- `cardId`: `card id`

### resolvables/comment

Adds a comment to a thread. You and everyone mentioned in `content` join the thread, even if you opted out before. The other participants who didn't opt out are notified.

`POST /dispatch/resolvables/comment`

Requires `resolvableEntry:writeOwn`.

Params:

- `resolvableId`: `resolvable id`, required
- `content`: `string`, required. Markdown. `@name` mentions of the organization's users are stored as `@[userId:<id>]`.

Response:

- `id`: `resolvableEntry id`

### resolvables/create

Starts a comment thread on a card with its first comment. You, the card's assignee and everyone mentioned in `content` join it, and everyone watching the card is notified.

`POST /dispatch/resolvables/create`

Requires `resolvable:write` or `card:write`, depending on who the call acts on.

Params:

- `cardId`: `card id`, required
- `context`: `"block" | "review" | "comment"`, required. `block` or `review` sets the card back to `not_started` and marks it as blocked or in review until the thread is closed. A `review` thread also adds the deck's guardians.
- `content`: `string`, required. Markdown. `@name` mentions of the organization's users are stored as `@[userId:<id>]`.

Response:

- `id`: `resolvable id`

### resolvables/removeReaction _(preview)_

`POST /dispatch/resolvables/removeReaction`

Requires `resolvableEntryReaction:writeOwn`.

Params:

- `reactionId`: `resolvableEntryReaction id`, required

Response:

No body.

### resolvables/reopen

Opens a closed comment thread again. Reopening a `block` or `review` thread sets the card back to `not_started`, and fails for doc cards, hero cards and cards that already have an open `block` or `review` thread.

`POST /dispatch/resolvables/reopen`

Requires `resolvable:write` or `card:write`, depending on who the call acts on.

Params:

- `id`: `resolvable id`, required

Response:

No body.

### resolvables/updateComment

Changes a comment's `content`. If the thread is open, users newly mentioned in it join the thread.

`POST /dispatch/resolvables/updateComment`

Requires `resolvableEntry:writeOwn`.

Params:

- `entryId`: `resolvableEntry id`, required
- `content`: `string`, required. Markdown. `@name` mentions of the organization's users are stored as `@[userId:<id>]`.

Response:

No body.

### resolvables/updateParticipantDone _(preview)_

Opts a participant out of a comment thread (`done: true`) or back in (`done: false`). Participants who opted out aren't notified about new comments. The card's assignee can't opt out.

`POST /dispatch/resolvables/updateParticipantDone`

Requires `resolvableParticipant:writeOwn`.

Params:

- `resolvableId`: `resolvable id`, required
- `userId`: `user id`, required
- `done`: `boolean`, required
- `status`: `"active" | "opt_out" | "approve"`. Defaults to `opt_out` with `done: true` and to `active` otherwise. A guardian uses `approve` with `done: true` to approve a `review` thread.

Response:

No body.

### sprints/updateConfig

Changes the given fields of a Run Config. A new `autoBeastModeDurationHours` starts Beast Mode for the current run right away if that time has already passed.

`POST /dispatch/sprints/updateConfig`

Requires `sprintConfig:write`.

Params:

- `id`: `sprintConfig id`, required
- `name`: `string`
- `color`: `"gray" | "brown" | "yellow" | "red" | "pink" | "blue" | "green"`
- `isGlobal`: `boolean`. `true` links the Run Config to all projects. `false` needs `projectIds` if the Run Config is global.
- `autoBeastModeDurationHours`: `int32 | null` _(preview)_. Fails with a value above 0 unless the organization's plan includes Beast Mode.
- `beastGracePeriodHours`: `int32` _(preview)_. Fails with a value above 0 unless the organization's plan includes Beast Mode.
- `projectIds`: `project id[] | null`. Replaces the Run Config's projects and makes it non-global. Ignored with `isGlobal: true`. Cards of projects that are removed are taken out of their run.
- `moveOnFinish`: `("undone" | "review" | "blocked")[]` _(preview)_
- `autoAssignStartedCard`: `boolean` _(preview)_
- `autoAssignNewCard`: `boolean` _(preview)_
- `runLabelTemplate`: `string | null` _(preview)_. Has to contain `%RUN_NR%` or `%CALENDAR_WEEK%`.

Response:

No body.

### sprints/updateSprint

Changes the given fields of a run, and can start its Beast Mode or complete it.

`POST /dispatch/sprints/updateSprint`

Requires `sprint:write`.

Params:

- `id`: `sprint id`, required
- `name`: `string | null`. `null` makes the web app show the label from the Run Config's `runLabelTemplate`.
- `description`: `string`
- `handSyncEnabled`: `boolean` _(preview)_
- `manualOrderLabels`: `(string | null)[]` _(preview)_. Labels are trimmed, and duplicates are removed.
- `userCapacities`: `{[key: string]: int32}` _(preview)_. Replaces all capacities. Fails with a value above 0 unless the organization's plan includes capacity tracking.
- `autoMilestoneId`: `milestone id | null` _(preview)_. Also gives this milestone to the run's cards that have none.
- `lockIn`: `boolean` _(preview)_. `true` starts Beast Mode for the run now and locks in all its cards. Fails if it's already on, or unless the organization's plan includes Beast Mode.
- `complete`: `boolean`. `true` completes the run now, as if it ended. Cards move to the next run as set in the Run Config's `moveOnFinish`. Fails if the run is already completed.

Response:

No body.

### watchings/addCard _(preview)_

Watches a card for you, so you're notified about its changes and new comment threads. Does nothing if you already watch it.

`POST /dispatch/watchings/addCard`

Requires `cardSubscription:writeOwn`.

Params:

- `cardId`: `card id`, required

Response:

- `id`: `cardSubscription id`

### watchings/addDeck _(preview)_

Makes a user watch a deck. That's the same as watching every card in it, including cards added later. Does nothing if the user already watches it.

`POST /dispatch/watchings/addDeck`

Requires `deckSubscription:writeOwn`.

Params:

- `deckId`: `deck id`, required
- `userId`: `user id`, required. The token's own user. For a personal token, that's you.

Response:

- `id`: `deckSubscription id`

### watchings/removeCard _(preview)_

Stops watching a card. You still get its notifications while you watch its deck.

`POST /dispatch/watchings/removeCard`

Requires `cardSubscription:writeOwn`.

Params:

- `cardId`: `card id`, required

Response:

No body.

### watchings/removeDeck _(preview)_

Stops a user from watching a deck. Cards the user watches on their own stay watched.

`POST /dispatch/watchings/removeDeck`

Requires `deckSubscription:writeOwn`.

Params:

- `deckId`: `deck id`, required
- `userId`: `user id`, required

Response:

No body.