Appearance
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
| Action | Required Permission | Scope |
|---|---|---|
| List / view | Epics: Read | In accessible projects |
| Create | Epics: Create | In accessible projects |
| Update | Epics: Update (Own/All) | In accessible projects |
| Delete | Epics: Delete (Own/All) | In accessible projects |
Admins and project owners bypass all permission checks for project-scoped resources.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/projects/{projectId}/epics | List epics |
GET | /api/projects/{projectId}/epics/{epicId} | Get one epic in full |
POST | /api/projects/{projectId}/epics | Create 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}/tick | Tick a Done when wish |
DELETE | /api/projects/{projectId}/epics/{epicId}/wishes/{wishId}/tick | Untick 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.
| Field | Type | Description |
|---|---|---|
goal | string | null | What the epic delivers |
description | string | null | Epic description |
checks | object[] | What done looks like, in order: { "id", "text" }. Plain text, not ticked |
wishes | object[] | 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
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Epic name, max 255 characters |
color | integer | Yes | 0–9, see Color |
order | integer | Yes | Position in the epic list, min 0 |
goal | string | No | What the epic delivers, max 500 characters |
assignee_id | integer | No | User who owns the epic; must be able to be assigned in the project. The new assignee is notified, unless they set it themselves |
description | string | No | Epic description, max 65,535 characters |
start | date | No | Start date (YYYY-MM-DD), required if end is set |
end | date | No | End date, must be after start, required if start is set |
issue_ids | integer[] | No | Issue IDs to assign (must belong to the project) |
checks | object[] | No | What done looks like, in order: { "text" } objects, at most 100, each text at most 500 characters |
wishes | object[] | No | Done 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
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Epic name, max 255 characters |
color | integer | Yes | 0–9, see Color |
order | integer | Yes | Position in the epic list, min 0 |
goal | string | No | Max 500 characters. Omitting the key keeps the stored goal under PUT as well as PATCH; null clears it |
assignee_id | integer | No | Omitting the key keeps the stored assignee under PUT as well as PATCH; null unassigns |
description | string | No | Max 65,535 characters |
start | date | No | Required if end is set |
end | date | No | Must be after start, required if start is set |
issue_ids | integer[] | No | Issue IDs to assign. Omitting the key leaves membership untouched under PUT as well as PATCH; [] detaches every issue |
checks | object[] | No | What 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 |
wishes | object[] | No | Done 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
wishIdthat is not a Done when wish of this epic answers404 - Both answer
200with 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
| Value | Label |
|---|---|
0 | Grey |
1 | Green |
2 | Blue |
3 | Red |
4 | Purple |
5 | Yellow |
6 | Orange |
7 | Teal |
8 | Pink |
9 | Indigo |
See Also
- Issues API — Assign issues to epics via
epic_id - Projects API — Project that owns the epics
- Projects guide — Working with epics in the UI