Skip to content

Epics API ​

Epics group related issues on a timeline. Each epic has a color, an optional goal, assignee and date range, a set of assigned issues, and optional Done when wishes and What done looks like checks.

An epic has no status field to set. is_closed is derived: an epic with Done when wishes is closed once every wish is ticked, even with issues still open. An epic without wishes is closed when it has issues and every one is in the project's last lane.

Permissions ​

ActionRequired PermissionScope
List / viewEpics: ReadIn accessible projects
CreateEpics: CreateIn accessible projects
UpdateEpics: Update (Own/All)In accessible projects
DeleteEpics: Delete (Own/All)In accessible projects

Admins and project owners bypass all permission checks for project-scoped resources.

Endpoints ​

MethodEndpointDescription
GET/api/projects/{projectId}/epicsList epics
GET/api/projects/{projectId}/epics/{epicId}Get one epic in full
POST/api/projects/{projectId}/epicsCreate an epic
PUT/api/projects/{projectId}/epics/{epicId}Update an epic
PATCH/api/projects/{projectId}/epics/{epicId}Update an epic partially
DELETE/api/projects/{projectId}/epics/{epicId}Delete an epic
POST/api/projects/{projectId}/epics/{epicId}/wishes/{wishId}/tickTick a Done when wish
DELETE/api/projects/{projectId}/epics/{epicId}/wishes/{wishId}/tickUntick a Done when wish

An epic's comments live under /api/projects/{projectId}/epics/{epicId}/comments; see the Comments API.

The list returns no goal, description, checks or wishes. Get one epic for those.

Get Epic ​

GET /api/projects/{projectId}/epics/{epicId}

Returns one epic in full: every field of a list row, plus the fields the list leaves out.

FieldTypeDescription
goalstring | nullWhat the epic delivers
descriptionstring | nullEpic description
checksobject[]What done looks like, in order: { "id", "text" }. Plain text, not ticked
wishesobject[]Done when, in order: { "id", "text", "issues", "checked_at", "checked_by_id" }, each linked issue as { "id", "key", "title", "lane_id" }

Create Epic ​

POST /api/projects/{projectId}/epics

Request Fields ​

FieldTypeRequiredDescription
titlestringYesEpic name, max 255 characters
colorintegerYes0–9, see Color
orderintegerYesPosition in the epic list, min 0
goalstringNoWhat the epic delivers, max 500 characters
assignee_idintegerNoUser who owns the epic; must be able to be assigned in the project. The new assignee is notified, unless they set it themselves
descriptionstringNoEpic description, max 65,535 characters
startdateNoStart date (YYYY-MM-DD), required if end is set
enddateNoEnd date, must be after start, required if start is set
issue_idsinteger[]NoIssue IDs to assign (must belong to the project)
checksobject[]NoWhat done looks like, in order: { "text" } objects, at most 100, each text at most 500 characters
wishesobject[]NoDone when, in order: { "text", "issue_ids"? } objects, at most 100, each text at most 500 characters and linking at most 100 issues. A wish links only issues of this epic, including those this request assigns through issue_ids; any other answers 422. A wish is never ticked here
bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/epics \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Authentication Overhaul",
    "description": "Replace legacy auth with 2FA support and improved session management.",
    "color": 2,
    "order": 0,
    "start": "2026-03-17",
    "end": "2026-04-11",
    "issue_ids": [42, 43, 44]
  }'
json
{
  "id": 5,
  "project_id": 1,
  "assignee_id": null,
  "issue_ids": [42, 43, 44],
  "issues_count": 3,
  "completed_issues_count": 0,
  "is_closed": false,
  "wishes_count": 0,
  "checked_wishes_count": 0,
  "title": "Authentication Overhaul",
  "color": 2,
  "start": "2026-03-17",
  "end": "2026-04-11",
  "order": 0,
  "created_at": "2026-03-17T09:00:00+00:00",
  "updated_at": "2026-03-17T09:00:00+00:00"
}

Update Epic ​

PUT /api/projects/{projectId}/epics/{epicId}

Partial update: send PATCH instead of PUT to change a subset of fields. An omitted field keeps its stored value; goal, description or assignee_id sent as null clears it. start and end are validated as a pair against the request alone, so a PATCH that changes or clears one date must send both. A PATCH that sends none of the writable fields is rejected with 422.

Request Fields ​

FieldTypeRequiredDescription
titlestringYesEpic name, max 255 characters
colorintegerYes0–9, see Color
orderintegerYesPosition in the epic list, min 0
goalstringNoMax 500 characters. Omitting the key keeps the stored goal under PUT as well as PATCH; null clears it
assignee_idintegerNoOmitting the key keeps the stored assignee under PUT as well as PATCH; null unassigns
descriptionstringNoMax 65,535 characters
startdateNoRequired if end is set
enddateNoMust be after start, required if start is set
issue_idsinteger[]NoIssue IDs to assign. Omitting the key leaves membership untouched under PUT as well as PATCH; [] detaches every issue
checksobject[]NoWhat done looks like, the whole list in order: { "id"?, "text" } objects, at most 100, each text at most 500 characters. Send each kept check with its id; a check left out is deleted, one without an id is created, and an id of another list is refused with 422. Omitting the key (or null) keeps the checks under PUT as well as PATCH; [] clears them
wishesobject[]NoDone when, the whole list in order: { "id"?, "text", "issue_ids"? } objects, with the limits of Create. Send each kept wish with its id: it keeps its tick through a move or an edit, and its links become the issue_ids sent. A wish left out is deleted, one without an id is created, and an id of another list or a link to an issue outside the epic is refused with 422. A save never ticks or unticks a wish. Omitting the key (or null) keeps the wishes under PUT as well as PATCH; [] clears them
bash
curl -X PUT https://{tenant}.kendo.dev/api/projects/1/epics/5 \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Authentication Overhaul",
    "description": "Replace legacy auth with 2FA support and improved session management.",
    "color": 2,
    "order": 0,
    "start": "2026-03-17",
    "end": "2026-04-18",
    "issue_ids": [42, 43, 44, 50]
  }'
json
{
  "id": 5,
  "project_id": 1,
  "assignee_id": null,
  "issue_ids": [42, 43, 44, 50],
  "issues_count": 4,
  "completed_issues_count": 0,
  "is_closed": false,
  "wishes_count": 0,
  "checked_wishes_count": 0,
  "title": "Authentication Overhaul",
  "color": 2,
  "start": "2026-03-17",
  "end": "2026-04-18",
  "order": 0,
  "created_at": "2026-03-17T09:00:00+00:00",
  "updated_at": "2026-03-20T11:30:00+00:00"
}

Delete Epic ​

DELETE /api/projects/{projectId}/epics/{epicId}

Returns 204 No Content. Issues in the epic are unassigned from it (not deleted).

bash
curl -X DELETE https://{tenant}.kendo.dev/api/projects/1/epics/5 \
  -H "Authorization: Bearer your-token"

Tick a Done When Wish ​

POST /api/projects/{projectId}/epics/{epicId}/wishes/{wishId}/tick ticks the wish; DELETE on the same path unticks it. Both need Epics: Update.

  • A tick records the caller and the time; ticking a ticked wish keeps its first ticker and time
  • An untick clears both; unticking an open wish changes nothing
  • A wishId that is not a Done when wish of this epic answers 404
  • Both answer 200 with the epic in full, as Get Epic returns it
json
"wishes": [
  {
    "id": 7,
    "text": "The customer can pay by card",
    "issues": [{ "id": 42, "key": "KD-0042", "title": "Card payments", "lane_id": 2 }],
    "checked_at": "2026-10-15T09:30:00+00:00",
    "checked_by_id": 3
  }
]

Enums ​

Color ​

ValueLabel
0Grey
1Green
2Blue
3Red
4Purple
5Yellow
6Orange
7Teal
8Pink
9Indigo

See Also ​